diff --git a/apps/rpc-proxy/.env.example b/apps/rpc-proxy/.env.example new file mode 100644 index 000000000..e83634788 --- /dev/null +++ b/apps/rpc-proxy/.env.example @@ -0,0 +1 @@ +BASE_RPC_URL= diff --git a/apps/rpc-proxy/.prettierignore b/apps/rpc-proxy/.prettierignore new file mode 100644 index 000000000..869887df8 --- /dev/null +++ b/apps/rpc-proxy/.prettierignore @@ -0,0 +1,7 @@ +# Build outputs +dist/ +*.js +*.mjs + +# Node modules +node_modules/ diff --git a/apps/rpc-proxy/README.md b/apps/rpc-proxy/README.md new file mode 100644 index 000000000..1fa9ea40d --- /dev/null +++ b/apps/rpc-proxy/README.md @@ -0,0 +1,19 @@ +# @x402scan/rpc-proxy + +Standalone HTTP service exposing a single endpoint: + +- `GET /balance/:address` + - EVM addresses return **Base USDC** (ERC-20) balance + +## Configuration + +Set environment variables: + +- `PORT` (default: `6970`) +- `BASE_RPC_URL` (required for EVM/Base lookups) + +## Run locally + +```bash +pnpm -w -F @x402scan/rpc-proxy dev +``` diff --git a/apps/rpc-proxy/eslint.config.js b/apps/rpc-proxy/eslint.config.js new file mode 100644 index 000000000..de3f5cba7 --- /dev/null +++ b/apps/rpc-proxy/eslint.config.js @@ -0,0 +1,4 @@ +import { config } from '@x402scan/eslint-config/base'; + +/** @type {import("eslint").Linter.Config[]} */ +export default config; diff --git a/apps/rpc-proxy/package.json b/apps/rpc-proxy/package.json new file mode 100644 index 000000000..1f603dec7 --- /dev/null +++ b/apps/rpc-proxy/package.json @@ -0,0 +1,32 @@ +{ + "name": "@x402scan/rpc-proxy", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "tsx watch src/server.ts", + "build": "tsc", + "start": "node dist/server.js", + "types:check": "tsc --noEmit", + "lint": "eslint . --max-warnings 0", + "lint:fix": "eslint . --fix", + "knip": "pnpm -w knip --workspace ./apps/rpc-proxy", + "format": "pnpm -w format:dir ./apps/rpc-proxy", + "format:check": "pnpm -w format:check:dir ./apps/rpc-proxy" + }, + "dependencies": { + "@hono/node-server": "^1.13.8", + "dotenv": "catalog:env", + "hono": "^4.7.1", + "neverthrow": "^8.2.0", + "viem": "catalog:" + }, + "devDependencies": { + "@types/node": "catalog:", + "@x402scan/eslint-config": "workspace:*", + "@x402scan/typescript-config": "workspace:*", + "eslint": "catalog:", + "tsx": "catalog:", + "typescript": "catalog:" + } +} diff --git a/apps/rpc-proxy/src/routes/balance.ts b/apps/rpc-proxy/src/routes/balance.ts new file mode 100644 index 000000000..5a978fad6 --- /dev/null +++ b/apps/rpc-proxy/src/routes/balance.ts @@ -0,0 +1,83 @@ +import type { Context, Hono } from 'hono'; +import type { Address } from 'viem'; + +import { + createPublicClient, + erc20Abi, + formatUnits, + getAddress, + http, + isAddress, +} from 'viem'; +import { base } from 'viem/chains'; + +import { errAsync, ResultAsync } from 'neverthrow'; + +const BASE_USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; +const USDC_DECIMALS = 6; +const USDC_SYMBOL = 'USDC'; +const BASE_CHAIN = base; + +const ERROR_NO_RPC = 'no_rpc'; +const ERROR_RPC_FAILED = 'rpc_failed'; + +type GetBalanceError = + | { type: typeof ERROR_NO_RPC; message: string } + | { type: typeof ERROR_RPC_FAILED; message: string }; + +function getBalance(address: Address): ResultAsync { + const rpcUrl = process.env.BASE_RPC_URL; + if (!rpcUrl) { + return errAsync({ type: ERROR_NO_RPC, message: 'No RPC URL provided' }); + } + const client = createPublicClient({ chain: base, transport: http(rpcUrl) }); + return ResultAsync.fromPromise( + client.readContract({ + address: BASE_USDC_ADDRESS, + abi: erc20Abi, + functionName: 'balanceOf', + args: [address], + }), + (): GetBalanceError => ({ + type: ERROR_RPC_FAILED, + message: 'RPC balanceOf call failed', + }) + ); +} + +async function balanceHandler(c: Context) { + const addressParam = c.req.param('address'); + if (!isAddress(addressParam)) { + return c.json({ error: 'Invalid EVM address' }, 400); + } + const address = getAddress(addressParam); + + return await getBalance(address).match( + balance => + c.json({ + chain: BASE_CHAIN.id, + address, + balance: formatUnits(balance, USDC_DECIMALS), + rawBalance: balance.toString(), + token: { + symbol: USDC_SYMBOL, + decimals: USDC_DECIMALS, + address: BASE_USDC_ADDRESS, + }, + }), + error => { + switch (error.type) { + case ERROR_NO_RPC: + return c.json({ error: error.message }, 503); + case ERROR_RPC_FAILED: + return c.json({ error: error.message }, 502); + default: + return c.json({ error: 'Unhandled error' }, 500); + } + } + ); +} + +export function registerBalanceRouter(app: Hono) { + app.get('/balance/:address', balanceHandler); +} diff --git a/apps/rpc-proxy/src/server.ts b/apps/rpc-proxy/src/server.ts new file mode 100644 index 000000000..18df1e74a --- /dev/null +++ b/apps/rpc-proxy/src/server.ts @@ -0,0 +1,39 @@ +import 'dotenv/config'; +import { Hono } from 'hono'; +import { serve } from '@hono/node-server'; +import { cors } from 'hono/cors'; + +import { registerBalanceRouter } from './routes/balance.js'; + +const app = new Hono(); + +// Enable CORS for all origins (simple service endpoint) +app.use( + '*', + cors({ + origin: '*', + allowMethods: ['GET', 'OPTIONS'], + allowHeaders: ['*'], + exposeHeaders: ['*'], + credentials: true, + }) +); + +app.get('/', c => { + return c.json({ + service: 'rpc-proxy', + version: '1.0.0', + endpoints: { + balance: '/balance/:address', + }, + }); +}); + +registerBalanceRouter(app); + +const port = Number(process.env.PORT) || 6970; + +serve({ + fetch: app.fetch, + port, +}); diff --git a/apps/rpc-proxy/tsconfig.json b/apps/rpc-proxy/tsconfig.json new file mode 100644 index 000000000..847a1386f --- /dev/null +++ b/apps/rpc-proxy/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@x402scan/typescript-config/base.json", + "compilerOptions": { + "outDir": "./dist", + "rootDir": "./src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/apps/rpc-proxy/turbo.json b/apps/rpc-proxy/turbo.json new file mode 100644 index 000000000..42d3f0767 --- /dev/null +++ b/apps/rpc-proxy/turbo.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://turbo.build/schema.json", + "extends": ["//"], + "tasks": { + "dev": { + "env": ["PORT", "BASE_RPC_URL"] + }, + "build": { + "env": ["PORT", "BASE_RPC_URL"] + } + } +} diff --git a/apps/scan/src/app/api/rpc/balance/[address]/route.ts b/apps/scan/src/app/api/rpc/balance/[address]/route.ts new file mode 100644 index 000000000..4787abb4c --- /dev/null +++ b/apps/scan/src/app/api/rpc/balance/[address]/route.ts @@ -0,0 +1,47 @@ +import { NextResponse } from 'next/server'; +import { + ERROR_NO_RPC, + ERROR_RPC_FAILED, + getBalance, + USDC_DECIMALS, +} from '@/services/rpc/balance'; +import { formatUnits, getAddress, isAddress } from 'viem'; + +export async function GET( + _: Request, + { params }: { params: Promise<{ address: string }> } +) { + const { address } = await params; + + if (!isAddress(address)) { + return NextResponse.json({ error: 'Invalid EVM address' }, { status: 400 }); + } + + const parsedAddress = getAddress(address); + + const balance = await getBalance(parsedAddress).match( + balance => + NextResponse.json( + { + balance: formatUnits(balance, USDC_DECIMALS), + rawBalance: balance.toString(), + }, + { status: 200 } + ), + error => { + switch (error.type) { + case ERROR_NO_RPC: + return NextResponse.json({ error: error.message }, { status: 503 }); + case ERROR_RPC_FAILED: + return NextResponse.json({ error: error.message }, { status: 502 }); + default: + return NextResponse.json( + { error: 'Unhandled error' }, + { status: 500 } + ); + } + } + ); + + return balance; +} diff --git a/apps/scan/src/services/rpc/balance.ts b/apps/scan/src/services/rpc/balance.ts new file mode 100644 index 000000000..7ccbc85bc --- /dev/null +++ b/apps/scan/src/services/rpc/balance.ts @@ -0,0 +1,41 @@ +import { env } from '@/env'; +import { errAsync, ResultAsync } from 'neverthrow'; +import { base } from 'wagmi/chains'; +import { + createPublicClient, + http, + erc20Abi, + type Address, +} from 'viem'; + +export const USDC_DECIMALS = 6; +const BASE_USDC_ADDRESS = '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913'; + +export const ERROR_NO_RPC = 'no_rpc'; +export const ERROR_RPC_FAILED = 'rpc_failed'; + +type GetBalanceError = + | { type: typeof ERROR_NO_RPC; message: string } + | { type: typeof ERROR_RPC_FAILED; message: string }; + +export function getBalance( + address: Address +): ResultAsync { + const rpcUrl = env.NEXT_PUBLIC_BASE_RPC_URL; + if (!rpcUrl) { + return errAsync({ type: ERROR_NO_RPC, message: 'No RPC URL provided' }); + } + const client = createPublicClient({ chain: base, transport: http(rpcUrl) }); + return ResultAsync.fromPromise( + client.readContract({ + address: BASE_USDC_ADDRESS, + abi: erc20Abi, + functionName: 'balanceOf', + args: [address], + }), + (): GetBalanceError => ({ + type: ERROR_RPC_FAILED, + message: 'RPC balanceOf call failed', + }) + ); +} diff --git a/knip.config.ts b/knip.config.ts index a462c1aea..87bbd0027 100644 --- a/knip.config.ts +++ b/knip.config.ts @@ -13,6 +13,7 @@ const config: KnipConfig = { ignore: ['src/scripts/**', 'src/components/ui/charts/chart/**'], }, 'apps/proxy': {}, + 'apps/rpc-proxy': {}, 'apps/rpcs/solana': {}, 'packages/external/facilitators': { project: ['src/**/*.ts'], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 67fb72feb..f5b66a4a4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -263,6 +263,43 @@ importers: specifier: 'catalog:' version: 5.9.3 + apps/rpc-proxy: + dependencies: + '@hono/node-server': + specifier: ^1.13.8 + version: 1.19.9(hono@4.10.6) + dotenv: + specifier: catalog:env + version: 17.2.3 + hono: + specifier: ^4.7.1 + version: 4.10.6 + neverthrow: + specifier: ^8.2.0 + version: 8.2.0 + viem: + specifier: 2.44.1 + version: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 20.19.27 + '@x402scan/eslint-config': + specifier: workspace:* + version: link:../../packages/internal/configurations/eslint + '@x402scan/typescript-config': + specifier: workspace:* + version: link:../../packages/internal/configurations/typescript + eslint: + specifier: 'catalog:' + version: 9.39.2(jiti@2.6.1) + tsx: + specifier: 'catalog:' + version: 4.21.0 + typescript: + specifier: 'catalog:' + version: 5.9.3 + apps/rpcs/solana: devDependencies: '@cloudflare/workers-types': @@ -944,8 +981,6 @@ importers: specifier: 'catalog:' version: 5.9.3 - packages/internal/databases/scan/generated/client: {} - packages/internal/databases/transfers: dependencies: '@neondatabase/serverless': @@ -992,8 +1027,6 @@ importers: specifier: 'catalog:' version: 5.9.3 - packages/internal/databases/transfers/generated/client: {} - sync/alerts: dependencies: '@trigger.dev/sdk': @@ -12644,7 +12677,7 @@ snapshots: transitivePeerDependencies: - supports-color - '@gemini-wallet/core@0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))': + '@gemini-wallet/core@0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5))': dependencies: '@metamask/rpc-errors': 7.0.2 eventemitter3: 5.0.1 @@ -12652,6 +12685,14 @@ snapshots: transitivePeerDependencies: - supports-color + '@gemini-wallet/core@0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + '@metamask/rpc-errors': 7.0.2 + eventemitter3: 5.0.1 + viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + transitivePeerDependencies: + - supports-color + '@google-cloud/bigquery@8.1.1': dependencies: '@google-cloud/common': 6.0.0 @@ -18148,7 +18189,7 @@ snapshots: dependencies: '@base-org/account': 2.4.0(@types/react@19.2.2)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@4.3.5) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(utf-8-validate@5.0.10)(zod@4.3.5) - '@gemini-wallet/core': 0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@gemini-wallet/core': 0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) @@ -18202,7 +18243,7 @@ snapshots: dependencies: '@base-org/account': 2.4.0(@types/react@19.2.2)(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.2)(bufferutil@4.0.9)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)) + '@gemini-wallet/core': 0.3.1(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)) '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -18260,11 +18301,11 @@ snapshots: '@metamask/sdk': 0.33.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.16)(@types/react@19.2.2)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)) + '@wagmi/core': 2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)) '@walletconnect/ethereum-provider': 2.21.1(bufferutil@4.0.9)(react@19.2.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' porto: 0.2.35(@tanstack/react-query@5.90.16(react@19.2.2))(@wagmi/core@2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)))(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(wagmi@2.19.1(@tanstack/react-query@5.90.16(react@19.2.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) + viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: @@ -18375,6 +18416,20 @@ snapshots: - react - use-sync-external-store + '@wagmi/core@2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))': + dependencies: + eventemitter3: 5.0.1 + mipd: 0.0.7(typescript@5.9.3) + viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) + zustand: 5.0.0(@types/react@19.2.2)(react@19.2.2)(use-sync-external-store@1.4.0(react@19.2.2)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@types/react' + - immer + - react + - use-sync-external-store + '@wagmi/core@2.22.1(typescript@5.9.3)(use-sync-external-store@1.4.0)(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: eventemitter3: 5.0.1 @@ -23884,12 +23939,12 @@ snapshots: porto@0.2.35(@tanstack/react-query@5.90.16(react@19.2.2))(@wagmi/core@2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)))(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(wagmi@2.19.1(@tanstack/react-query@5.90.16(react@19.2.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.16)(@types/react@19.2.2)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)) + '@wagmi/core': 2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)) hono: 4.10.6 idb-keyval: 6.2.2 mipd: 0.0.7(typescript@5.9.3) ox: 0.9.14(typescript@5.9.3)(zod@4.3.5) - viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) + viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) zod: 4.3.5 zustand: 5.0.8(@types/react@19.2.2)(react@19.2.2)(use-sync-external-store@1.4.0(react@19.2.2)) optionalDependencies: @@ -25934,10 +25989,10 @@ snapshots: dependencies: '@tanstack/react-query': 5.90.16(react@19.2.2) '@wagmi/connectors': 6.1.2(@tanstack/react-query@5.90.16(react@19.2.2))(@wagmi/core@2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(utf-8-validate@5.0.10)(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(wagmi@2.19.1(@tanstack/react-query@5.90.16(react@19.2.2))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.2.2)(typescript@5.9.3)(utf-8-validate@5.0.10)(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) - '@wagmi/core': 2.22.1(@tanstack/query-core@5.90.16)(@types/react@19.2.2)(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5)) + '@wagmi/core': 2.22.1(react@19.2.2)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.2))(viem@2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)) react: 19.2.2 use-sync-external-store: 1.4.0(react@19.2.2) - viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.5) + viem: 2.44.1(bufferutil@4.0.9)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@3.22.4) optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: