From 083a68def38db3571f1a5034d386d34b61f97874 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:35:34 -0600 Subject: [PATCH 1/2] feat: restrict public api to production-validated chains Add a SUPPORTED_CHAIN_IDS allowlist matching the chains fully validated and enabled in the production web app, and filter the public surface by it: - /v1/chains now lists only supported chains instead of all KnownChainIds - /v1/assets and asset lookups exclude assets on unsupported chains, so /v1/rates and /v1/quote reject them as unknown assets - getAssetsById stays unfiltered for swapper deps (e.g. NearIntents denominates its affiliate fee in NEAR's fee asset) Second-class EVM chains (Monad, HyperEVM, Plasma, Katana) are excluded until their chain adapters are wired into the API in a follow-up PR. Co-Authored-By: Claude Fable 5 --- packages/public-api/src/assets.ts | 18 +++++++++-- packages/public-api/src/constants.ts | 26 +++++++++++++++ .../public-api/src/routes/chains/utils.ts | 32 ++++++++----------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/packages/public-api/src/assets.ts b/packages/public-api/src/assets.ts index b66ab3419e4..e49c551df83 100644 --- a/packages/public-api/src/assets.ts +++ b/packages/public-api/src/assets.ts @@ -4,6 +4,8 @@ import { getBaseAsset } from '@shapeshiftoss/utils' import fs from 'fs' import path from 'path' +import { SUPPORTED_CHAIN_IDS_SET } from './constants' + let assetsById: AssetsById = {} let assetIds: AssetId[] = [] let assets: Asset[] = [] @@ -49,8 +51,10 @@ export const initAssets = (): Promise => { } assetsById = enrichedAssetsById - assetIds = sortedAssetIds - assets = sortedAssetIds.map((id: AssetId) => enrichedAssetsById[id]).filter(Boolean) as Asset[] + assetIds = sortedAssetIds.filter(id => + SUPPORTED_CHAIN_IDS_SET.has(enrichedAssetsById[id]?.chainId), + ) + assets = assetIds.map((id: AssetId) => enrichedAssetsById[id]).filter(Boolean) as Asset[] console.log(`Loaded ${assetIds.length} assets from ${assetDataPath}`) initialized = true @@ -61,7 +65,15 @@ export const initAssets = (): Promise => { } } +// Full asset map (all chains) for swapper deps only — e.g. NearIntents denominates +// its affiliate fee in NEAR's fee asset (buildAffiliateFee silently returns undefined +// if the asset is missing), so filtering this to supported chains would drop +// affiliate fee metadata from NearIntents rates/quotes export const getAssetsById = (): AssetsById => assetsById export const getAssetIds = (): AssetId[] => assetIds export const getAllAssets = (): Asset[] => assets -export const getAsset = (assetId: AssetId): Asset | undefined => assetsById[assetId] +export const getAsset = (assetId: AssetId): Asset | undefined => { + const asset = assetsById[assetId] + if (!asset || !SUPPORTED_CHAIN_IDS_SET.has(asset.chainId)) return undefined + return asset +} diff --git a/packages/public-api/src/constants.ts b/packages/public-api/src/constants.ts index 565a62c1d82..117259186b4 100644 --- a/packages/public-api/src/constants.ts +++ b/packages/public-api/src/constants.ts @@ -1,4 +1,30 @@ import { SwapperName } from '@shapeshiftoss/swapper' +import { KnownChainIds } from '@shapeshiftoss/types' + +export const SUPPORTED_CHAIN_IDS: readonly KnownChainIds[] = [ + // EVM + KnownChainIds.ArbitrumMainnet, + KnownChainIds.AvalancheMainnet, + KnownChainIds.BaseMainnet, + KnownChainIds.BnbSmartChainMainnet, + KnownChainIds.EthereumMainnet, + KnownChainIds.GnosisMainnet, + KnownChainIds.OptimismMainnet, + KnownChainIds.PolygonMainnet, + // UTXO + KnownChainIds.BitcoinCashMainnet, + KnownChainIds.BitcoinMainnet, + KnownChainIds.DogecoinMainnet, + KnownChainIds.LitecoinMainnet, + // Cosmos SDK + KnownChainIds.CosmosMainnet, + KnownChainIds.MayachainMainnet, + KnownChainIds.ThorchainMainnet, + // Solana + KnownChainIds.SolanaMainnet, +] + +export const SUPPORTED_CHAIN_IDS_SET: ReadonlySet = new Set(SUPPORTED_CHAIN_IDS) export const ENABLED_SWAPPER_NAMES: readonly SwapperName[] = [ SwapperName.Bebop, diff --git a/packages/public-api/src/routes/chains/utils.ts b/packages/public-api/src/routes/chains/utils.ts index 65435404fdf..f6b3655b1bb 100644 --- a/packages/public-api/src/routes/chains/utils.ts +++ b/packages/public-api/src/routes/chains/utils.ts @@ -1,35 +1,31 @@ -import { fromChainId } from '@shapeshiftoss/caip' -import { KnownChainIds } from '@shapeshiftoss/types' +import type { ChainNamespace } from '@shapeshiftoss/caip' +import { CHAIN_NAMESPACE, fromChainId } from '@shapeshiftoss/caip' import { getBaseAsset } from '@shapeshiftoss/utils' +import { SUPPORTED_CHAIN_IDS } from '../../constants' import type { Chain, ChainType } from './types' -const chainNamespaceToType: Record = { - eip155: 'evm', - bip122: 'utxo', - cosmos: 'cosmos', - solana: 'solana', - tron: 'tron', - sui: 'sui', - near: 'near', - starknet: 'starknet', - ton: 'ton', +const chainNamespaceToType: Record = { + [CHAIN_NAMESPACE.Evm]: 'evm', + [CHAIN_NAMESPACE.Utxo]: 'utxo', + [CHAIN_NAMESPACE.CosmosSdk]: 'cosmos', + [CHAIN_NAMESPACE.Solana]: 'solana', + [CHAIN_NAMESPACE.Tron]: 'tron', + [CHAIN_NAMESPACE.Sui]: 'sui', + [CHAIN_NAMESPACE.Near]: 'near', + [CHAIN_NAMESPACE.Starknet]: 'starknet', + [CHAIN_NAMESPACE.Ton]: 'ton', } const buildChainList = (): Chain[] => { const chains: Chain[] = [] - for (const chainId of Object.values(KnownChainIds)) { + for (const chainId of SUPPORTED_CHAIN_IDS) { try { const baseAsset = getBaseAsset(chainId) const { chainNamespace } = fromChainId(chainId) const chainType = chainNamespaceToType[chainNamespace] - if (!chainType) { - console.warn(`Unknown chain namespace: ${chainNamespace} for chainId: ${chainId}`) - continue - } - chains.push({ chainId, name: baseAsset.networkName ?? baseAsset.name, From 82899d39b51e034b5a53facab0800ad521b070a5 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Thu, 11 Jun 2026 10:52:03 -0600 Subject: [PATCH 2/2] feat: restrict swap widget to production-validated chains Remove chains not fully validated in production from the widget's chain constants, wallet networks, and viem chain configs: - WorldChain (feature flag off in production) - Monad, HyperEVM, Plasma, Katana (second-class EVM chains pending chain adapter support in the public api) The widget derives its visible chain list from /v1/assets, so this aligns its local constants with the public api's supported chain set. Also key VIEM_CHAINS_BY_ID by chain.id instead of hardcoded numbers. Co-Authored-By: Claude Fable 5 --- packages/swap-widget/src/config/appkit.ts | 10 ----- packages/swap-widget/src/constants/chains.ts | 10 ----- .../swap-widget/src/constants/viemChains.ts | 40 +++++-------------- packages/swap-widget/src/types/index.ts | 10 ----- 4 files changed, 9 insertions(+), 61 deletions(-) diff --git a/packages/swap-widget/src/config/appkit.ts b/packages/swap-widget/src/config/appkit.ts index 50d4c16e06b..7c6715430c9 100644 --- a/packages/swap-widget/src/config/appkit.ts +++ b/packages/swap-widget/src/config/appkit.ts @@ -6,15 +6,10 @@ import { bitcoin, bsc, gnosis, - hyperEvm, - katana, mainnet, - monad, optimism, - plasma, polygon, solana, - worldchain, } from '@reown/appkit/networks' import { createAppKit } from '@reown/appkit/react' import { BitcoinAdapter } from '@reown/appkit-adapter-bitcoin' @@ -31,11 +26,6 @@ const EVM_NETWORKS: readonly AppKitNetwork[] = [ avalanche, bsc, gnosis, - monad, - hyperEvm, - plasma, - worldchain, - katana, ] const ALL_NETWORKS: readonly AppKitNetwork[] = [...EVM_NETWORKS, bitcoin, solana] diff --git a/packages/swap-widget/src/constants/chains.ts b/packages/swap-widget/src/constants/chains.ts index 9d14daf6339..5eddf02693b 100644 --- a/packages/swap-widget/src/constants/chains.ts +++ b/packages/swap-widget/src/constants/chains.ts @@ -10,17 +10,12 @@ import { dogecoin, ethereum, gnosis, - hyperevm, - katana, litecoin, mayachain, - monad, optimism, - plasma, polygon, solana, thorchain, - worldchain, } from '@shapeshiftoss/utils' import type { ChainId } from '../types' @@ -34,11 +29,6 @@ const BASE_ASSETS_BY_CHAIN_ID: Record = { [avax.chainId]: avax, [bnbsmartchain.chainId]: bnbsmartchain, [gnosis.chainId]: gnosis, - [monad.chainId]: monad, - [hyperevm.chainId]: hyperevm, - [plasma.chainId]: plasma, - [worldchain.chainId]: worldchain, - [katana.chainId]: katana, [bitcoin.chainId]: bitcoin, [bitcoincash.chainId]: bitcoincash, [dogecoin.chainId]: dogecoin, diff --git a/packages/swap-widget/src/constants/viemChains.ts b/packages/swap-widget/src/constants/viemChains.ts index 68e9c140be3..8f0b8672e6a 100644 --- a/packages/swap-widget/src/constants/viemChains.ts +++ b/packages/swap-widget/src/constants/viemChains.ts @@ -1,61 +1,39 @@ import type { Chain, WalletClient } from 'viem' -import { - arbitrum, - avalanche, - base, - bsc, - gnosis, - hyperEvm, - katana, - mainnet, - monad, - optimism, - plasma, - polygon, - worldchain, -} from 'viem/chains' +import { arbitrum, avalanche, base, bsc, gnosis, mainnet, optimism, polygon } from 'viem/chains' export const VIEM_CHAINS_BY_ID: Record = { - 1: { + [mainnet.id]: { ...mainnet, rpcUrls: { default: { http: ['https://api.ethereum.shapeshift.com/api/v1/jsonrpc'] } }, }, - 10: { + [optimism.id]: { ...optimism, rpcUrls: { default: { http: ['https://api.optimism.shapeshift.com/api/v1/jsonrpc'] } }, }, - 56: { + [bsc.id]: { ...bsc, rpcUrls: { default: { http: ['https://api.bnbsmartchain.shapeshift.com/api/v1/jsonrpc'] } }, }, - 100: { + [gnosis.id]: { ...gnosis, rpcUrls: { default: { http: ['https://api.gnosis.shapeshift.com/api/v1/jsonrpc'] } }, }, - 137: { + [polygon.id]: { ...polygon, rpcUrls: { default: { http: ['https://api.polygon.shapeshift.com/api/v1/jsonrpc'] } }, }, - 143: { ...monad, rpcUrls: { default: { http: ['https://rpc.monad.xyz'] } } }, - 480: { - ...worldchain, - rpcUrls: { default: { http: ['https://worldchain-mainnet.g.alchemy.com/public'] } }, - }, - 999: { ...hyperEvm, rpcUrls: { default: { http: ['https://rpc.hyperliquid.xyz/evm'] } } }, - 8453: { + [base.id]: { ...base, rpcUrls: { default: { http: ['https://api.base.shapeshift.com/api/v1/jsonrpc'] } }, }, - 9745: { ...plasma, rpcUrls: { default: { http: ['https://rpc.plasma.to'] } } }, - 42161: { + [arbitrum.id]: { ...arbitrum, rpcUrls: { default: { http: ['https://api.arbitrum.shapeshift.com/api/v1/jsonrpc'] } }, }, - 43114: { + [avalanche.id]: { ...avalanche, rpcUrls: { default: { http: ['https://api.avalanche.shapeshift.com/api/v1/jsonrpc'] } }, }, - 747474: { ...katana, rpcUrls: { default: { http: ['https://rpc.katana.network'] } } }, } export const addChainToWallet = async (client: WalletClient, chain: Chain): Promise => { diff --git a/packages/swap-widget/src/types/index.ts b/packages/swap-widget/src/types/index.ts index 59f36b710a7..53ed85e07b3 100644 --- a/packages/swap-widget/src/types/index.ts +++ b/packages/swap-widget/src/types/index.ts @@ -12,17 +12,12 @@ import { ethChainId, fromChainId, gnosisChainId, - hyperEvmChainId, - katanaChainId, ltcChainId, mayachainChainId, - monadChainId, optimismChainId, - plasmaChainId, polygonChainId, solanaChainId, thorchainChainId, - worldChainChainId, } from '@shapeshiftoss/caip' import type { TransactionData } from '@shapeshiftoss/types' import { BigAmount } from '@shapeshiftoss/utils' @@ -234,11 +229,6 @@ export const EVM_CHAIN_IDS = { avalanche: avalancheChainId, bsc: bscChainId, gnosis: gnosisChainId, - monad: monadChainId, - hyperEvm: hyperEvmChainId, - plasma: plasmaChainId, - worldChain: worldChainChainId, - katana: katanaChainId, } as const export const UTXO_CHAIN_IDS = {