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
new file mode 100644
index 000000000..1cc7016b7
--- /dev/null
+++ b/.changeset/restore-sei-chain-exports.md
@@ -0,0 +1,5 @@
+---
+'@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/.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 1cfd54ced..a372a1dc7 100644
--- a/packages/precompiles/src/viem/__tests__/chain.spec.ts
+++ b/packages/precompiles/src/viem/__tests__/chain.spec.ts
@@ -1,7 +1,20 @@
-import { seiLocal } from '../chain';
+import { sei as viemSei, seiTestnet as viemSeiTestnet } from 'viem/chains';
+import * as packageRoot from '../../index';
+import { sei, 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 local', () => {
expect(seiLocal).toMatchObject({
id: 713714,
name: 'Sei Local',
@@ -17,4 +30,12 @@ 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.seiLocal).toBe(seiLocal);
+ }
+ });
});
diff --git a/packages/precompiles/src/viem/chain.ts b/packages/precompiles/src/viem/chain.ts
index ae0db9f2a..f68907798 100644
--- a/packages/precompiles/src/viem/chain.ts
+++ b/packages/precompiles/src/viem/chain.ts
@@ -1,5 +1,7 @@
import { defineChain } from 'viem';
+export { sei, seiTestnet } from 'viem/chains';
+
/**
* The Viem chain definition for the Sei local chain.
* @category Chain
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/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..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,4 +50,4 @@ type GasInfo = {
* console.log(pacific1Info.denom); // 'usei'
* ```
*/
-export const GAS_INFO: GasInfo = GasInfoJSON as GasInfo;
+export const GAS_INFO: GasInfo = pickSupportedNetworks(GasInfoJSON);
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..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,4 +50,4 @@ 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 = pickSupportedNetworks(IBCInfoJSON);
diff --git a/packages/registry/src/index.ts b/packages/registry/src/index.ts
index 33cff832a..7315c6fb9 100644
--- a/packages/registry/src/index.ts
+++ b/packages/registry/src/index.ts
@@ -2,18 +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' | 'arctic-1';
-
-/**
- * An object for referencing the official Sei network chain identifiers.
- */
-export const CHAIN_IDS = {
- mainnet: 'pacific-1',
- testnet: 'atlantic-2',
- devnet: 'arctic-1'
-};
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..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.
@@ -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,4 @@ type NetworksConfig = {
* const pacific1 = NETWORKS.find((network) => network.chainId === 'pacific-1');
* ```
*/
-export const NETWORKS: NetworksConfig = NetworksJSON 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/__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..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,4 +69,4 @@ 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 = 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'
+});
diff --git a/scripts/check-precompile-exports.ts b/scripts/check-precompile-exports.ts
index 00c462525..43ae10d1d 100644
--- a/scripts/check-precompile-exports.ts
+++ b/scripts/check-precompile-exports.ts
@@ -41,8 +41,17 @@ 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],
+ ['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');