From 3e983d7d926e77af8d3960c94bf8871ea3d71da4 Mon Sep 17 00:00:00 2001 From: abtestingalpha Date: Thu, 24 Sep 2026 19:03:23 -0400 Subject: [PATCH] use absolute RPC URLs for WalletConnect connections --- packages/synapse-interface/package.json | 2 +- .../utils/createTransports.test.cjs | 69 +++++++++++++++++++ .../utils/createTransports.ts | 9 ++- 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 packages/synapse-interface/utils/createTransports.test.cjs diff --git a/packages/synapse-interface/package.json b/packages/synapse-interface/package.json index 1c445f19f6..76198e920d 100644 --- a/packages/synapse-interface/package.json +++ b/packages/synapse-interface/package.json @@ -13,7 +13,7 @@ "lint": "next lint", "cypress:run": "echo 'No cypress tests defined.'", "test:coverage": "echo 'No tests defined.'", - "test:rpc": "node --test utils/providers/TransportAwareJsonRpcProvider.test.cjs", + "test:rpc": "node --test utils/providers/TransportAwareJsonRpcProvider.test.cjs utils/createTransports.test.cjs", "lint:fix": "npm run lint -- --fix", "lint:check": "eslint . --max-warnings=0", "lint:find-string-literals": "npx eslint -c .eslintrc-i18n.js './**/*.{js,jsx,ts,tsx}'", diff --git a/packages/synapse-interface/utils/createTransports.test.cjs b/packages/synapse-interface/utils/createTransports.test.cjs new file mode 100644 index 0000000000..f3be94d823 --- /dev/null +++ b/packages/synapse-interface/utils/createTransports.test.cjs @@ -0,0 +1,69 @@ +const assert = require('node:assert/strict') +const fs = require('node:fs') +const path = require('node:path') +const test = require('node:test') +const vm = require('node:vm') +const ts = require('typescript') +const { extractRpcUrls } = require('@wagmi/core') +const { arbitrum, mainnet } = require('@wagmi/core/chains') +const { HttpConnection } = require('@walletconnect/jsonrpc-http-connection') + +const chains = [arbitrum, mainnet] +const chainConfig = Object.fromEntries( + chains.map((chain) => [ + chain.id, + { + rpcUrls: { + primary: chain.rpcUrls.default.http[0], + fallback: `https://fallback.example/${chain.id}`, + }, + }, + ]) +) +const source = fs.readFileSync( + path.join(__dirname, 'createTransports.ts'), + 'utf8' +) +const compiled = ts.transpileModule(source, { + compilerOptions: { module: ts.ModuleKind.CommonJS }, +}).outputText + +function loadTransports(origin) { + const context = { + exports: {}, + URL, + require: (id) => + id === '@/constants/chains' + ? { CHAINS_BY_ID: chainConfig } + : require(id), + } + if (origin) context.window = { location: { origin } } + vm.runInNewContext(compiled, context) + return context.exports.createTransports(chains) +} + +for (const origin of [ + 'https://bridge.synapseprotocol.com', + 'https://preview.example', + 'http://localhost:3000', +]) { + test(`WalletConnect accepts extracted RPC URLs on ${origin}`, () => { + const transports = loadTransports(origin) + for (const chain of chains) { + // Exercise the same extraction and URL validation as the connector. + const urls = extractRpcUrls({ chain, transports }) + assert.deepEqual(Array.from(urls), [ + `${origin}/api/rpc/${chain.id}`, + chainConfig[chain.id].rpcUrls.primary, + chainConfig[chain.id].rpcUrls.fallback, + ]) + assert.doesNotThrow(() => new HttpConnection(urls[0])) + } + }) +} + +test('transport configuration remains safe to load during SSR', () => { + const transports = loadTransports() + const urls = extractRpcUrls({ chain: arbitrum, transports }) + assert.equal(urls[0], '/api/rpc/42161') +}) diff --git a/packages/synapse-interface/utils/createTransports.ts b/packages/synapse-interface/utils/createTransports.ts index d6603c6e7d..c0b0095701 100644 --- a/packages/synapse-interface/utils/createTransports.ts +++ b/packages/synapse-interface/utils/createTransports.ts @@ -8,9 +8,16 @@ type Transports = Record export const createTransports = (chains: Chain[]): Transports => { return chains.reduce((acc, chain) => { const synapseChain = CHAINS_BY_ID[chain.id] + const proxyPath = `/api/rpc/${chain.id}` + // WalletConnect extracts this URL from the transport and requires HTTP(S). + // Resolve against the current origin so previews and localhost use their proxy. + const proxyUrl = + typeof window === 'undefined' + ? proxyPath + : new URL(proxyPath, window.location.origin).href const rpcUrls = [ - `/api/rpc/${chain.id}`, + proxyUrl, synapseChain.rpcUrls.primary, synapseChain.rpcUrls.fallback, ]