From fad1a11e940008190918e64cfdf272158ee50877 Mon Sep 17 00:00:00 2001 From: Alex Harley Date: Mon, 17 Jun 2024 20:22:09 +0200 Subject: [PATCH 1/5] feat: allow overriding RPCs via env var --- .env.example | 3 ++- src/context/chains.ts | 33 +++++++++++++++++++++++++++++++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/.env.example b/.env.example index eab91031..1d295fcb 100644 --- a/.env.example +++ b/.env.example @@ -1 +1,2 @@ -NEXT_PUBLIC_WALLET_CONNECT_ID=12345678901234567890123456789012 \ No newline at end of file +NEXT_PUBLIC_WALLET_CONNECT_ID=12345678901234567890123456789012 +NEXT_PUBLIC_RPC_OVERRIDES='{"chain1":{"http":"https://..."}}' \ No newline at end of file diff --git a/src/context/chains.ts b/src/context/chains.ts index 3c404a6b..cc8c1e91 100644 --- a/src/context/chains.ts +++ b/src/context/chains.ts @@ -1,7 +1,7 @@ import { z } from 'zod'; import { GithubRegistry, chainMetadata } from '@hyperlane-xyz/registry'; -import { ChainMap, ChainMetadata, ChainMetadataSchema } from '@hyperlane-xyz/sdk'; +import { ChainMap, ChainMetadata, ChainMetadataSchema, RpcUrlSchema } from '@hyperlane-xyz/sdk'; import { chains as ChainsTS } from '../consts/chains.ts'; import ChainsYaml from '../consts/chains.yaml'; @@ -9,6 +9,8 @@ import { config } from '../consts/config.ts'; import { cosmosDefaultChain } from '../features/chains/cosmosDefault'; import { logger } from '../utils/logger'; +const NEXT_PUBLIC_RPC_OVERRIDES = process.env['NEXT_PUBLIC_RPC_OVERRIDES']; + export async function assembleChainMetadata() { // Chains must include a cosmos chain or CosmosKit throws errors const result = z.record(ChainMetadataSchema).safeParse({ @@ -20,6 +22,14 @@ export async function assembleChainMetadata() { logger.warn('Invalid chain config', result.error); throw new Error(`Invalid chain config: ${result.error.toString()}`); } + + const rpcOverrides = z + .record(RpcUrlSchema) + .safeParse(NEXT_PUBLIC_RPC_OVERRIDES ? JSON.parse(NEXT_PUBLIC_RPC_OVERRIDES) : {}); + if (NEXT_PUBLIC_RPC_OVERRIDES && !rpcOverrides.success) { + logger.warn('Invalid RPC overrides config', rpcOverrides.error); + } + const customChainMetadata = result.data as ChainMap; const registry = new GithubRegistry({ uri: config.registryUrl }); @@ -34,6 +44,25 @@ export async function assembleChainMetadata() { await registry.listRegistryContent(); } - const chains = { ...defaultChainMetadata, ...customChainMetadata }; + const chains: ChainMap = Object.entries({ + ...defaultChainMetadata, + ...customChainMetadata, + }).reduce((accum, [name, chain]) => { + const privateRpc = rpcOverrides.success ? rpcOverrides.data[name] : null; + if (privateRpc) { + return { + ...accum, + [name]: { + ...chain, + rpurls: chain.rpcUrls, + }, + }; + } + return { + ...accum, + [name]: chain, + }; + }, {}); + return { chains, registry }; } From 4bd1827eec9b7a1d998148669f8cab776cbd2ce6 Mon Sep 17 00:00:00 2001 From: Alex Harley Date: Mon, 17 Jun 2024 20:27:54 +0200 Subject: [PATCH 2/5] refactor: more defensive --- src/context/chains.ts | 9 ++++----- src/utils/json.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) create mode 100644 src/utils/json.ts diff --git a/src/context/chains.ts b/src/context/chains.ts index cc8c1e91..b1e8d266 100644 --- a/src/context/chains.ts +++ b/src/context/chains.ts @@ -7,9 +7,10 @@ import { chains as ChainsTS } from '../consts/chains.ts'; import ChainsYaml from '../consts/chains.yaml'; import { config } from '../consts/config.ts'; import { cosmosDefaultChain } from '../features/chains/cosmosDefault'; +import { tryParseJson } from '../utils/json.ts'; import { logger } from '../utils/logger'; -const NEXT_PUBLIC_RPC_OVERRIDES = process.env['NEXT_PUBLIC_RPC_OVERRIDES']; +const RPC_OVERRIDES = process.env['NEXT_PUBLIC_RPC_OVERRIDES']; export async function assembleChainMetadata() { // Chains must include a cosmos chain or CosmosKit throws errors @@ -23,10 +24,8 @@ export async function assembleChainMetadata() { throw new Error(`Invalid chain config: ${result.error.toString()}`); } - const rpcOverrides = z - .record(RpcUrlSchema) - .safeParse(NEXT_PUBLIC_RPC_OVERRIDES ? JSON.parse(NEXT_PUBLIC_RPC_OVERRIDES) : {}); - if (NEXT_PUBLIC_RPC_OVERRIDES && !rpcOverrides.success) { + const rpcOverrides = z.record(RpcUrlSchema).safeParse(tryParseJson(RPC_OVERRIDES ?? '')); + if (RPC_OVERRIDES && !rpcOverrides.success) { logger.warn('Invalid RPC overrides config', rpcOverrides.error); } diff --git a/src/utils/json.ts b/src/utils/json.ts new file mode 100644 index 00000000..87c94dc1 --- /dev/null +++ b/src/utils/json.ts @@ -0,0 +1,8 @@ +export function tryParseJson(input: string): unknown | null { + try { + return JSON.parse(input); + } catch (e) { + console.warn('unable to parse JSON', e); + return null; + } +} From 7b0286078b0219661b4b28c863e355d2461fe2be Mon Sep 17 00:00:00 2001 From: Alex Harley Date: Tue, 18 Jun 2024 17:56:44 +0200 Subject: [PATCH 3/5] fix: no console --- .eslintrc | 6 +++--- src/utils/json.ts | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.eslintrc b/.eslintrc index dcc1fd5e..3f64c5fc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -15,12 +15,12 @@ "extends": [ "eslint:recommended", "plugin:@typescript-eslint/recommended", - "next", - "next/core-web-vitals", + "next", + "next/core-web-vitals", "prettier" ], "rules": { - "no-console": ["warn"], + "no-console": ["error"], "no-eval": ["error"], "no-ex-assign": ["error"], "no-constant-condition": ["off"], diff --git a/src/utils/json.ts b/src/utils/json.ts index 87c94dc1..7fbacb94 100644 --- a/src/utils/json.ts +++ b/src/utils/json.ts @@ -1,8 +1,10 @@ +import { logger } from './logger'; + export function tryParseJson(input: string): unknown | null { try { return JSON.parse(input); } catch (e) { - console.warn('unable to parse JSON', e); + logger.warn('unable to parse JSON', e); return null; } } From a0c1f9188ee5b70c3da3fff947b4e2f94d317127 Mon Sep 17 00:00:00 2001 From: Alex Harley Date: Tue, 18 Jun 2024 18:02:36 +0200 Subject: [PATCH 4/5] fix: type safe --- src/consts/config.ts | 3 +++ src/context/chains.ts | 32 +++++++++++++------------------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/consts/config.ts b/src/consts/config.ts index 2f09dfc6..3b9096e7 100644 --- a/src/consts/config.ts +++ b/src/consts/config.ts @@ -7,6 +7,7 @@ const explorerApiKeys = JSON.parse(process?.env?.EXPLORER_API_KEYS || '{}'); const walletConnectProjectId = process?.env?.NEXT_PUBLIC_WALLET_CONNECT_ID || ''; const withdrawalWhitelist = process?.env?.NEXT_PUBLIC_BLOCK_WITHDRAWAL_WHITELIST || ''; const transferBlacklist = process?.env?.NEXT_PUBLIC_TRANSFER_BLACKLIST || ''; +const rpcOverrides = process?.env?.NEXT_PUBLIC_RPC_OVERRIDES || ''; interface Config { isDevMode: boolean; // Enables some debug features in the app @@ -20,6 +21,7 @@ interface Config { transferBlacklist: string; // comma-separated list of routes between which transfers are disabled. Expects Caip2Id-Caip2Id (e.g. ethereum:1-sealevel:1399811149) enableExplorerLink: boolean; // Include a link to the hyperlane explorer in the transfer modal addressBlacklist: string[]; // A list of addresses that are blacklisted and cannot be used in the app + rpcOverrides: string; } export const config: Config = Object.freeze({ @@ -34,4 +36,5 @@ export const config: Config = Object.freeze({ transferBlacklist, enableExplorerLink: false, addressBlacklist: ADDRESS_BLACKLIST.map((address) => address.toLowerCase()), + rpcOverrides, }); diff --git a/src/context/chains.ts b/src/context/chains.ts index b1e8d266..24465ae5 100644 --- a/src/context/chains.ts +++ b/src/context/chains.ts @@ -10,8 +10,6 @@ import { cosmosDefaultChain } from '../features/chains/cosmosDefault'; import { tryParseJson } from '../utils/json.ts'; import { logger } from '../utils/logger'; -const RPC_OVERRIDES = process.env['NEXT_PUBLIC_RPC_OVERRIDES']; - export async function assembleChainMetadata() { // Chains must include a cosmos chain or CosmosKit throws errors const result = z.record(ChainMetadataSchema).safeParse({ @@ -24,8 +22,8 @@ export async function assembleChainMetadata() { throw new Error(`Invalid chain config: ${result.error.toString()}`); } - const rpcOverrides = z.record(RpcUrlSchema).safeParse(tryParseJson(RPC_OVERRIDES ?? '')); - if (RPC_OVERRIDES && !rpcOverrides.success) { + const rpcOverrides = z.record(RpcUrlSchema).safeParse(tryParseJson(config.rpcOverrides)); + if (config.rpcOverrides && !rpcOverrides.success) { logger.warn('Invalid RPC overrides config', rpcOverrides.error); } @@ -46,22 +44,18 @@ export async function assembleChainMetadata() { const chains: ChainMap = Object.entries({ ...defaultChainMetadata, ...customChainMetadata, - }).reduce((accum, [name, chain]) => { - const privateRpc = rpcOverrides.success ? rpcOverrides.data[name] : null; - if (privateRpc) { - return { - ...accum, - [name]: { - ...chain, - rpurls: chain.rpcUrls, - }, - }; - } - return { + }).reduce( + (accum, [name, chain]) => ({ ...accum, - [name]: chain, - }; - }, {}); + [name]: rpcOverrides.success + ? { + ...chain, + rpcUrls: [rpcOverrides.data[name]], + } + : chain, + }), + {} as ChainMap, + ); return { chains, registry }; } From eb772cb9d85cdec65c60d4322300f5bd755940e5 Mon Sep 17 00:00:00 2001 From: Alex Harley Date: Tue, 18 Jun 2024 18:06:14 +0200 Subject: [PATCH 5/5] fix: no undefined --- src/context/chains.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/context/chains.ts b/src/context/chains.ts index 24465ae5..66d2c882 100644 --- a/src/context/chains.ts +++ b/src/context/chains.ts @@ -47,12 +47,13 @@ export async function assembleChainMetadata() { }).reduce( (accum, [name, chain]) => ({ ...accum, - [name]: rpcOverrides.success - ? { - ...chain, - rpcUrls: [rpcOverrides.data[name]], - } - : chain, + [name]: + rpcOverrides.success && rpcOverrides.data[name] + ? { + ...chain, + rpcUrls: [rpcOverrides.data[name]], + } + : chain, }), {} as ChainMap, );