// shared upstream header policy for SootSim's CORS-bypassing fetch proxy. // requests still forward tenant auth and app headers, but the proxy transport // defaults to native-app semantics: no browser Origin or Referer. web-backed // mobile APIs that require a specific web origin opt in below. export const FETCH_PROXY_BROWSER_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ' + '(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36' const HOST_HEADER_OVERRIDES: Array<{ hostSuffix: string headers: Record suppressBrowserUserAgent?: boolean }> = [ { hostSuffix: 'uniswap.org', headers: { origin: 'https://app.uniswap.org', referer: 'https://app.uniswap.org/', }, // uniswap's native gateway accepts the app origin, but forcing a desktop // browser ua on native api posts returns a bogus 414/tcp-stack response. suppressBrowserUserAgent: true, }, ] function headerOverridesFor(hostname: string): | { headers: Record suppressBrowserUserAgent?: boolean } | undefined { const host = hostname.toLowerCase() for (const override of HOST_HEADER_OVERRIDES) { if (host === override.hostSuffix || host.endsWith(`.${override.hostSuffix}`)) { return override } } return undefined } // an app that builds its rpc provider hostname out of a per-tenant key cannot be // pointed at another node by configuration alone. demo tooling injects this // tenant name when a checkout carries no usable provider credentials, and the // proxy resolves it to a public node for the chain. a real tenant never matches. export const PUBLIC_RPC_TENANT = 'rnx-public-rpc' const QUICKNODE_HOST_SUFFIX = '.quiknode.pro' // quicknode encodes the chain in the subdomain and leaves mainnet off entirely. const PUBLIC_RPC_BY_QUICKNODE_CHAIN: Record = { '': 'https://ethereum-rpc.publicnode.com', 'arbitrum-mainnet': 'https://arbitrum-one-rpc.publicnode.com', 'avalanche-mainnet': 'https://avalanche-c-chain-rpc.publicnode.com', 'base-mainnet': 'https://base-rpc.publicnode.com', 'blast-mainnet': 'https://blast-rpc.publicnode.com', bsc: 'https://bsc-rpc.publicnode.com', 'celo-mainnet': 'https://celo-rpc.publicnode.com', matic: 'https://polygon-bor-rpc.publicnode.com', optimism: 'https://optimism-rpc.publicnode.com', 'soneium-mainnet': 'https://soneium-rpc.publicnode.com', 'unichain-mainnet': 'https://unichain-rpc.publicnode.com', } // the url the proxy should actually fetch, which differs from the requested one // only for the sentinel tenant above export function resolveFetchProxyTargetUrl(requested: URL): URL { const host = requested.hostname.toLowerCase() if (!host.endsWith(QUICKNODE_HOST_SUFFIX)) return requested const tenant = host.slice(0, -QUICKNODE_HOST_SUFFIX.length) if (tenant !== PUBLIC_RPC_TENANT && !tenant.startsWith(`${PUBLIC_RPC_TENANT}.`)) { return requested } const chain = tenant === PUBLIC_RPC_TENANT ? '' : tenant.slice(PUBLIC_RPC_TENANT.length + 1) const replacement = PUBLIC_RPC_BY_QUICKNODE_CHAIN[chain] // the tenant token rides in the path, which a public node does not want return replacement ? new URL(replacement) : requested } export function getFetchProxyTargetHeaders(targetUrl: URL): Record { const hostOverrides = headerOverridesFor(targetUrl.hostname) return { 'accept-encoding': 'identity', ...(hostOverrides?.suppressBrowserUserAgent ? {} : { 'user-agent': FETCH_PROXY_BROWSER_USER_AGENT }), ...hostOverrides?.headers, } }