diff --git a/apps/mcp/.env.example b/apps/mcp/.env.example index 172367a..a776438 100644 --- a/apps/mcp/.env.example +++ b/apps/mcp/.env.example @@ -1,17 +1,44 @@ -# Phase 1 will populate this. Phase 0 just reserves the file. -# See docs/superpowers/specs/2026-05-05-ghbounty-mcp-server-design.md - +# GitHub OAuth App for Device Flow +# Public; ships in env (production + preview). GITHUB_OAUTH_CLIENT_ID= + +# Secret; production env only. Backed up in 1Password. GITHUB_OAUTH_CLIENT_SECRET= -# Same value as the existing frontend service-role key, but kept in a -# separate Vercel project so it can be rotated independently. -SUPABASE_SERVICE_ROLE_KEY= +# Supabase service-role key (separate from the frontend's; rotate independently) SUPABASE_URL= +SUPABASE_SERVICE_ROLE_KEY= -# Solana RPC for tx-building (Helius mainnet or devnet). +# Solana RPC (Helius mainnet for production, devnet for preview/dev) SOLANA_RPC_URL= -# Stake authority keypair (JSON array). Same format as GAS_STATION_KEYPAIR_JSON. -# Generated per Task 2 of Phase 0. -STAKE_AUTHORITY_KEYPAIR_JSON= +# Stake authority keypair (JSON array, 64 bytes). Same format as +# GAS_STATION_KEYPAIR_JSON. Used by the relayer-side cron jobs (Phase 4), +# NOT by the MCP server itself. Phase 1 doesn't read this — it's listed +# here for documentation completeness. +# STAKE_AUTHORITY_KEYPAIR_JSON= + +# Upstash Redis for rate limiting — provisioned via Vercel Marketplace +# (Project Settings → Storage → Browse Marketplace → Upstash → Connect). +# Vercel auto-injects these env vars after the connection is created. +# Do NOT set them manually for production/preview deployments. +UPSTASH_REDIS_REST_URL= +UPSTASH_REDIS_REST_TOKEN= + +# Frontend's gas-station endpoint (production: https://www.ghbounty.com/api/gas-station/sponsor) +GAS_STATION_SPONSOR_URL= + +# Shared secret with the frontend's gas-station-route-core.ts. Phase 1 PR +# adds this to the frontend env too. Generate a random 64-char hex string. +GAS_STATION_SERVICE_TOKEN= + +# Public; identifies this service in logs / agent welcome messages. +NEXT_PUBLIC_MCP_BASE_URL=https://mcp.ghbounty.com + +# Public; same value as the frontend's GAS_STATION_PUBKEY env var. Used as +# the fee_payer in unsigned txs returned by prepare_* tools. +NEXT_PUBLIC_GAS_STATION_PUBKEY= + +# Random 32-byte hex (or any string >= 32 chars). Used to AES-256-GCM +# encrypt GitHub access tokens at rest in agent_accounts.github_oauth_token_encrypted. +MCP_TOKEN_ENCRYPTION_KEY= diff --git a/apps/mcp/.gitignore b/apps/mcp/.gitignore new file mode 100644 index 0000000..025e7a4 --- /dev/null +++ b/apps/mcp/.gitignore @@ -0,0 +1,5 @@ +.next/ +node_modules/ +.env.local +.env*.local +next-env.d.ts diff --git a/apps/mcp/README.md b/apps/mcp/README.md new file mode 100644 index 0000000..bdeaae9 --- /dev/null +++ b/apps/mcp/README.md @@ -0,0 +1,36 @@ +# @ghbounty/mcp + +Public MCP server hosted at `https://mcp.ghbounty.com`. Lets any AI agent (Claude Code, Cursor, Codex, custom) sign up and operate the GhBounty marketplace autonomously. + +## Architecture + +- **Next.js 16** + Turbopack (matches the frontend stack) +- **`@vercel/mcp-adapter`** for the MCP transport (Streamable HTTP) +- **Supabase service-role** for DB writes; bypasses RLS, enforces equivalent policies in code +- **Upstash Redis** for rate limiting (provisioned via Vercel Marketplace, no separate Upstash account) +- **Helius RPC** for Solana +- **GitHub Device Flow** for agentic OAuth (no browser redirect needed) + +## Local development + +```bash +# 1. Copy the env template and fill in real values from 1Password +cp apps/mcp/.env.example apps/mcp/.env.local +# Edit apps/mcp/.env.local + +# 2. Run the dev server +pnpm --filter @ghbounty/mcp dev + +# 3. Health check +curl http://localhost:3001/api/health +``` + +## Deploy + +The Vercel project is `ghbounty-mcp` in the `weareghbounty-6269` team. DNS for `mcp.ghbounty.com` is configured to point at this project. Pushes to `main` auto-deploy to production; PR branches get preview deployments. + +Upstash Redis is provisioned via Vercel Marketplace (Project Settings → Storage → Browse Marketplace → Upstash → Connect). No upstash.com signup needed. + +## Tools + +See `lib/tools/` for the implementations. Surface and contracts documented in `docs/superpowers/specs/2026-05-05-ghbounty-mcp-server-design.md` section 6. diff --git a/apps/mcp/app/api/health/route.ts b/apps/mcp/app/api/health/route.ts new file mode 100644 index 0000000..ee1c513 --- /dev/null +++ b/apps/mcp/app/api/health/route.ts @@ -0,0 +1,12 @@ +export const dynamic = "force-dynamic"; + +export async function GET() { + return new Response( + JSON.stringify({ + ok: true, + service: "ghbounty-mcp", + timestamp: new Date().toISOString(), + }), + { status: 200, headers: { "Content-Type": "application/json" } } + ); +} diff --git a/apps/mcp/app/api/mcp/[transport]/route.ts b/apps/mcp/app/api/mcp/[transport]/route.ts new file mode 100644 index 0000000..b5bb131 --- /dev/null +++ b/apps/mcp/app/api/mcp/[transport]/route.ts @@ -0,0 +1,25 @@ +// Public MCP endpoint. The dynamic route segment `[transport]` is +// `sse` for Streamable HTTP transport. Tools are registered by +// `lib/tools/register.ts`; this file is just the framework shell. + +import { createMcpHandler } from "mcp-handler"; +import { registerAllTools } from "@/lib/tools/register"; + +const handler = createMcpHandler( + async (server) => { + await registerAllTools(server); + }, + { + capabilities: { + tools: {}, + }, + }, + { + basePath: "/api/mcp", + } +); + +export { handler as GET, handler as POST, handler as DELETE }; + +export const dynamic = "force-dynamic"; +export const maxDuration = 60; diff --git a/apps/mcp/app/globals.css b/apps/mcp/app/globals.css new file mode 100644 index 0000000..80ca823 --- /dev/null +++ b/apps/mcp/app/globals.css @@ -0,0 +1,28 @@ +:root { + color-scheme: dark; +} +body { + margin: 0; + font-family: -apple-system, system-ui, sans-serif; + background: #0a0a0a; + color: #e5e5e5; + min-height: 100vh; +} +.container { + max-width: 720px; + margin: 80px auto; + padding: 0 24px; +} +code { + background: #1a1a1a; + padding: 2px 6px; + border-radius: 4px; + font-family: ui-monospace, monospace; +} +pre { + background: #1a1a1a; + padding: 16px; + border-radius: 8px; + overflow-x: auto; +} +a { color: #00e5d1; } diff --git a/apps/mcp/app/layout.tsx b/apps/mcp/app/layout.tsx new file mode 100644 index 0000000..1330a0e --- /dev/null +++ b/apps/mcp/app/layout.tsx @@ -0,0 +1,17 @@ +import type { Metadata } from "next"; +import "./globals.css"; + +export const metadata: Metadata = { + title: "GhBounty MCP", + description: "Public MCP server for AI agents to operate the GhBounty marketplace.", +}; + +export default function RootLayout({ + children, +}: Readonly<{ children: React.ReactNode }>) { + return ( + + {children} + + ); +} diff --git a/apps/mcp/app/page.tsx b/apps/mcp/app/page.tsx new file mode 100644 index 0000000..3b0ce88 --- /dev/null +++ b/apps/mcp/app/page.tsx @@ -0,0 +1,18 @@ +export default function Page() { + return ( +
+

GhBounty MCP Server

+

+ This is the MCP endpoint for AI agents. Connect with the URL: +

+
https://mcp.ghbounty.com/api/mcp/sse
+

+ Full docs:{" "} + ghbounty.com/agents +

+

+ Health: /api/health +

+
+ ); +} diff --git a/apps/mcp/lib/auth/api-key.ts b/apps/mcp/lib/auth/api-key.ts new file mode 100644 index 0000000..46f4609 --- /dev/null +++ b/apps/mcp/lib/auth/api-key.ts @@ -0,0 +1,42 @@ +// API key generation + verification. Format: `ghbk_live_<32 hex chars>`. +// +// Storage: +// - Plaintext is shown to the agent ONCE (response of create_account.complete). +// - bcrypt hash + first 12 chars (prefix) are stored in api_keys table. +// - Lookup is by prefix (indexed); bcrypt verifies on match. + +import { randomBytes } from "node:crypto"; +import bcrypt from "bcryptjs"; + +const PREFIX = "ghbk_live_"; +const SECRET_HEX_LEN = 32; // 16 bytes → 32 hex chars +const PREFIX_HEX_LEN = 12; // first 12 chars of the hex part used as table lookup index +const BCRYPT_ROUNDS = 12; + +export interface MintedKey { + /** Full plaintext key. Show to the agent ONCE; never store. */ + plaintext: string; + /** First 12 hex chars (prefixed). Indexed in DB for O(1) lookup. */ + prefix: string; + /** bcrypt hash. Store this in `api_keys.key_hash`. */ + hash: string; +} + +export function mintApiKey(): MintedKey { + const secret = randomBytes(SECRET_HEX_LEN / 2).toString("hex"); + const plaintext = `${PREFIX}${secret}`; + const prefix = `${PREFIX}${secret.slice(0, PREFIX_HEX_LEN)}`; + const hash = bcrypt.hashSync(plaintext, BCRYPT_ROUNDS); + return { plaintext, prefix, hash }; +} + +export function extractPrefix(plaintext: string): string { + if (!plaintext.startsWith(PREFIX)) { + throw new Error("Invalid API key format"); + } + return plaintext.slice(0, PREFIX.length + PREFIX_HEX_LEN); +} + +export function verifyApiKey(plaintext: string, hash: string): boolean { + return bcrypt.compareSync(plaintext, hash); +} diff --git a/apps/mcp/lib/auth/middleware.ts b/apps/mcp/lib/auth/middleware.ts new file mode 100644 index 0000000..6a62039 --- /dev/null +++ b/apps/mcp/lib/auth/middleware.ts @@ -0,0 +1,84 @@ +// Bearer token authentication for MCP tool calls. +// +// Flow: +// 1. Parse `Authorization: Bearer ` header. +// 2. Extract first 22 chars (prefix) for indexed DB lookup. +// 3. Fetch api_keys row + joined agent_accounts row. +// 4. bcrypt-verify the plaintext against key_hash. +// 5. Reject if revoked OR agent_account.status is not 'active'. +// 6. Return the agent for the tool to use. + +import { extractPrefix, verifyApiKey } from "./api-key"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import type { AuthResult, AgentAccount } from "@/lib/tools/types"; + +export async function authenticate( + authorizationHeader: string | undefined +): Promise<AuthResult> { + if (!authorizationHeader || !authorizationHeader.startsWith("Bearer ")) { + return { ok: false, error: { code: "Unauthorized", message: "Missing or malformed Authorization header" } }; + } + + const plaintext = authorizationHeader.slice("Bearer ".length).trim(); + + let prefix: string; + try { + prefix = extractPrefix(plaintext); + } catch { + return { ok: false, error: { code: "Unauthorized", message: "Invalid API key format" } }; + } + + const supabase = supabaseAdmin(); + const { data, error } = await supabase + .from("api_keys") + .select("id, key_hash, agent_account_id, agent_accounts(id, role, status, wallet_pubkey, github_handle)") + .eq("key_prefix", prefix) + .is("revoked_at", null) + .maybeSingle(); + + if (error) { + return { ok: false, error: { code: "Unauthorized", message: "Authentication lookup failed" } }; + } + if (!data) { + return { ok: false, error: { code: "Unauthorized", message: "API key not found" } }; + } + + if (!verifyApiKey(plaintext, (data as any).key_hash)) { + return { ok: false, error: { code: "Unauthorized", message: "API key mismatch" } }; + } + + // The Supabase typed-join syntax returns agent_accounts as either an object + // or a single-element array depending on the relationship. Normalize. + const rawAgent = (data as any).agent_accounts; + const agentRow = Array.isArray(rawAgent) ? rawAgent[0] : rawAgent; + if (!agentRow) { + return { ok: false, error: { code: "Unauthorized", message: "Agent record missing" } }; + } + + if (agentRow.status !== "active") { + return { + ok: false, + error: { + code: "Forbidden", + message: `Agent account is ${agentRow.status}, not active`, + }, + }; + } + + const agent: AgentAccount = { + id: agentRow.id, + role: agentRow.role, + status: agentRow.status, + wallet_pubkey: agentRow.wallet_pubkey, + github_handle: agentRow.github_handle, + }; + + // Async: update last_used_at without blocking the response. + supabase + .from("api_keys") + .update({ last_used_at: new Date().toISOString() }) + .eq("id", (data as any).id) + .then(() => {}); + + return { ok: true, agent, apiKeyId: (data as any).id }; +} diff --git a/apps/mcp/lib/errors.ts b/apps/mcp/lib/errors.ts new file mode 100644 index 0000000..52b4bff --- /dev/null +++ b/apps/mcp/lib/errors.ts @@ -0,0 +1,28 @@ +// Typed errors matching the spec's error model. Each tool returns these +// to the MCP transport, which formats them as JSON-RPC errors. + +export type McpErrorCode = + | "BlockhashExpired" + | "WalletInsufficientFunds" + | "InvalidSignature" + | "WrongSigner" + | "TxTampered" + | "ProgramError" + | "RateLimited" + | "Unauthorized" + | "Forbidden" + | "NotFound" + | "Conflict" + | "RpcError" + | "InternalError" + | "InvalidInput"; + +export interface McpError { + code: McpErrorCode; + message: string; + details?: unknown; +} + +export function mcpError(code: McpErrorCode, message: string, details?: unknown): McpError { + return { code, message, details }; +} diff --git a/apps/mcp/lib/github/device-flow.ts b/apps/mcp/lib/github/device-flow.ts new file mode 100644 index 0000000..0db00e7 --- /dev/null +++ b/apps/mcp/lib/github/device-flow.ts @@ -0,0 +1,134 @@ +// GitHub Device Flow proxy + at-rest token encryption. +// +// Calls 3 GitHub endpoints: +// 1. POST /login/device/code → start +// 2. POST /login/oauth/access_token → poll +// 3. GET /user → fetch handle (after auth success) +// +// Plus AES-256-GCM helpers for encrypting the access_token before storing +// it in agent_accounts.github_oauth_token_encrypted. +// +// Docs: https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/ + +import { createCipheriv, createDecipheriv, randomBytes, createHash } from "node:crypto"; + +const GH_DEVICE_CODE_URL = "https://github.com/login/device/code"; +const GH_TOKEN_URL = "https://github.com/login/oauth/access_token"; +const GH_USER_URL = "https://api.github.com/user"; +const SCOPE = "read:user user:email"; + +// --- Device flow --------------------------------------------------------- + +export interface DeviceFlowStart { + device_code: string; + user_code: string; + verification_uri: string; + expires_in: number; + interval: number; +} + +export type PollResult = + | { kind: "ok"; access_token: string } + | { kind: "pending" } + | { kind: "error"; error: string }; + +function clientId(): string { + const id = process.env.GITHUB_OAUTH_CLIENT_ID; + if (!id) throw new Error("GITHUB_OAUTH_CLIENT_ID must be set"); + return id; +} + +export async function startDeviceFlow(): Promise<DeviceFlowStart> { + const res = await fetch(GH_DEVICE_CODE_URL, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + client_id: clientId(), + scope: SCOPE, + }), + }); + if (!res.ok) { + throw new Error(`GitHub /login/device/code returned ${res.status}`); + } + const json = await res.json(); + return json as DeviceFlowStart; +} + +export async function pollAccessToken(device_code: string): Promise<PollResult> { + const res = await fetch(GH_TOKEN_URL, { + method: "POST", + headers: { + Accept: "application/json", + "Content-Type": "application/json", + }, + body: JSON.stringify({ + client_id: clientId(), + device_code, + grant_type: "urn:ietf:params:oauth:grant-type:device_code", + }), + }); + if (!res.ok) { + return { kind: "error", error: `http_${res.status}` }; + } + const json = await res.json(); + if (typeof json.access_token === "string") { + return { kind: "ok", access_token: json.access_token }; + } + if (json.error === "authorization_pending" || json.error === "slow_down") { + return { kind: "pending" }; + } + return { kind: "error", error: typeof json.error === "string" ? json.error : "unknown" }; +} + +export async function fetchUserHandle(access_token: string): Promise<string> { + const res = await fetch(GH_USER_URL, { + headers: { + Accept: "application/vnd.github+json", + Authorization: `Bearer ${access_token}`, + "X-GitHub-Api-Version": "2022-11-28", + }, + }); + if (!res.ok) { + throw new Error(`GitHub /user returned ${res.status}`); + } + const json = await res.json(); + if (typeof json.login !== "string") { + throw new Error("GitHub /user response missing login"); + } + return json.login; +} + +// --- Token encryption (at-rest) ---------------------------------------- + +const ALGO = "aes-256-gcm"; +const IV_LEN = 12; +const TAG_LEN = 16; + +function encryptionKey(): Buffer { + const raw = process.env.MCP_TOKEN_ENCRYPTION_KEY; + if (!raw) throw new Error("MCP_TOKEN_ENCRYPTION_KEY must be set (32+ chars)"); + return createHash("sha256").update(raw).digest(); +} + +export function encryptAccessToken(plaintext: string): string { + const iv = randomBytes(IV_LEN); + const cipher = createCipheriv(ALGO, encryptionKey(), iv); + const ct = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]); + const tag = cipher.getAuthTag(); + // Layout: iv | tag | ciphertext, base64 encoded. + return Buffer.concat([iv, tag, ct]).toString("base64"); +} + +export function decryptAccessToken(encoded: string): string { + const buf = Buffer.from(encoded, "base64"); + if (buf.length < IV_LEN + TAG_LEN) throw new Error("ciphertext too short"); + const iv = buf.subarray(0, IV_LEN); + const tag = buf.subarray(IV_LEN, IV_LEN + TAG_LEN); + const ct = buf.subarray(IV_LEN + TAG_LEN); + const decipher = createDecipheriv(ALGO, encryptionKey(), iv); + decipher.setAuthTag(tag); + return Buffer.concat([decipher.update(ct), decipher.final()]).toString("utf8"); +} diff --git a/apps/mcp/lib/rate-limit/ip.ts b/apps/mcp/lib/rate-limit/ip.ts new file mode 100644 index 0000000..a81957b --- /dev/null +++ b/apps/mcp/lib/rate-limit/ip.ts @@ -0,0 +1,16 @@ +// Extract the client IP from a request, taking Vercel's headers into +// account. The MCP adapter passes a standard fetch Request, so we read +// from headers. + +export function getClientIp(request: Request): string { + // Vercel sets these in order of preference: + const forwarded = request.headers.get("x-forwarded-for"); + if (forwarded) { + // First entry in the comma-separated list is the original client. + const first = forwarded.split(",")[0]; + return first ? first.trim() : "unknown"; + } + const real = request.headers.get("x-real-ip"); + if (real) return real; + return "unknown"; +} diff --git a/apps/mcp/lib/rate-limit/upstash.ts b/apps/mcp/lib/rate-limit/upstash.ts new file mode 100644 index 0000000..c7b6470 --- /dev/null +++ b/apps/mcp/lib/rate-limit/upstash.ts @@ -0,0 +1,72 @@ +// Sliding-window rate limits for the MCP server. +// +// Three tiers (spec section 9 layer 3): +// - createAccount: 5 req / hour / IP (anonymous) +// - read: 100 req / minute / agent (authenticated) +// - prepare: 30 req / minute / agent (authenticated, for prepare_* tools) +// +// Each tier is a separate Ratelimit instance so we can monitor / tune +// independently. Upstash's REST client makes them safe to invoke from +// any serverless environment. +// +// Provisioned via Vercel Marketplace (Project → Storage → Browse Marketplace +// → Upstash → Connect). Vercel auto-injects KV_REST_API_URL + +// KV_REST_API_TOKEN (its legacy KV naming) when connected this way; direct +// Upstash setups use UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN. +// We accept both so the same code runs in either configuration. + +import { Ratelimit } from "@upstash/ratelimit"; +import { Redis } from "@upstash/redis"; + +let _redis: Redis | null = null; +function redis(): Redis { + if (_redis) return _redis; + const url = + process.env.UPSTASH_REDIS_REST_URL ?? process.env.KV_REST_API_URL; + const token = + process.env.UPSTASH_REDIS_REST_TOKEN ?? process.env.KV_REST_API_TOKEN; + if (!url || !token) { + throw new Error( + "Upstash Redis credentials must be set: UPSTASH_REDIS_REST_URL/_TOKEN (direct) or KV_REST_API_URL/_TOKEN (Vercel Marketplace)" + ); + } + _redis = new Redis({ url, token }); + return _redis; +} + +let _createAccount: Ratelimit | null = null; +let _read: Ratelimit | null = null; +let _prepare: Ratelimit | null = null; + +export function createAccountLimiter(): Ratelimit { + if (_createAccount) return _createAccount; + _createAccount = new Ratelimit({ + redis: redis(), + limiter: Ratelimit.slidingWindow(5, "1 h"), + prefix: "mcp:create_account", + analytics: true, + }); + return _createAccount; +} + +export function readLimiter(): Ratelimit { + if (_read) return _read; + _read = new Ratelimit({ + redis: redis(), + limiter: Ratelimit.slidingWindow(100, "1 m"), + prefix: "mcp:read", + analytics: true, + }); + return _read; +} + +export function prepareLimiter(): Ratelimit { + if (_prepare) return _prepare; + _prepare = new Ratelimit({ + redis: redis(), + limiter: Ratelimit.slidingWindow(30, "1 m"), + prefix: "mcp:prepare", + analytics: true, + }); + return _prepare; +} diff --git a/apps/mcp/lib/solana/gas-station-client.ts b/apps/mcp/lib/solana/gas-station-client.ts new file mode 100644 index 0000000..27c8fe3 --- /dev/null +++ b/apps/mcp/lib/solana/gas-station-client.ts @@ -0,0 +1,56 @@ +// Calls the frontend's /api/gas-station/sponsor endpoint to submit a +// gas-station-sponsored transaction. The endpoint is shared with the +// frontend and has its own auth (Privy bearer for human users; we use +// a service-to-service shared secret for the MCP). +// +// FRONTEND FOLLOW-UP NEEDED: frontend/lib/gas-station-route-core.ts must +// be extended to accept a new auth path: requests with the +// `x-mcp-service-token` header where the value matches the +// GAS_STATION_SERVICE_TOKEN env var. Open a separate PR after this one +// merges. +// +// Returns either { tx_hash } or a structured error. + +export interface SponsorResult { + ok: boolean; + tx_hash?: string; + error?: { code: string; message: string }; +} + +function endpointUrl(): string { + const url = process.env.GAS_STATION_SPONSOR_URL; + if (!url) throw new Error("GAS_STATION_SPONSOR_URL must be set"); + return url; +} + +function serviceToken(): string { + const tok = process.env.GAS_STATION_SERVICE_TOKEN; + if (!tok) throw new Error("GAS_STATION_SERVICE_TOKEN must be set"); + return tok; +} + +export async function sponsorAndSubmit(signed_tx_b64: string): Promise<SponsorResult> { + const res = await fetch(endpointUrl(), { + method: "POST", + headers: { + "Content-Type": "application/json", + "x-mcp-service-token": serviceToken(), + }, + body: JSON.stringify({ signed_tx_b64, source: "mcp" }), + }); + + let json: { tx_hash?: string; error?: { code: string; message: string } }; + try { + json = (await res.json()) as { tx_hash?: string; error?: { code: string; message: string } }; + } catch { + return { ok: false, error: { code: "RpcError", message: `Gas station returned ${res.status} (non-JSON body)` } }; + } + + if (res.status === 200 && json.tx_hash) { + return { ok: true, tx_hash: json.tx_hash }; + } + if (json.error) { + return { ok: false, error: json.error }; + } + return { ok: false, error: { code: "RpcError", message: `Gas station returned ${res.status}` } }; +} diff --git a/apps/mcp/lib/solana/rpc.ts b/apps/mcp/lib/solana/rpc.ts new file mode 100644 index 0000000..e93212f --- /dev/null +++ b/apps/mcp/lib/solana/rpc.ts @@ -0,0 +1,36 @@ +// @solana/kit RPC client. Reads SOLANA_RPC_URL from env (Helius mainnet +// for production, devnet for dev). Singleton for connection reuse. + +import { + createSolanaRpc, + createSolanaRpcSubscriptions, + type Rpc, + type SolanaRpcApi, + type RpcSubscriptions, + type SolanaRpcSubscriptionsApi, +} from "@solana/kit"; + +let _rpc: Rpc<SolanaRpcApi> | null = null; +let _subs: RpcSubscriptions<SolanaRpcSubscriptionsApi> | null = null; + +export function solanaRpc(): Rpc<SolanaRpcApi> { + if (_rpc) return _rpc; + const url = process.env.SOLANA_RPC_URL; + if (!url) { + throw new Error("SOLANA_RPC_URL must be set in apps/mcp env"); + } + _rpc = createSolanaRpc(url); + return _rpc; +} + +export function solanaRpcSubscriptions(): RpcSubscriptions<SolanaRpcSubscriptionsApi> { + if (_subs) return _subs; + const url = process.env.SOLANA_RPC_URL; + if (!url) { + throw new Error("SOLANA_RPC_URL must be set in apps/mcp env"); + } + // ws:// URL is the http URL with the protocol swapped + const wsUrl = url.replace(/^http/, "ws"); + _subs = createSolanaRpcSubscriptions(wsUrl); + return _subs; +} diff --git a/apps/mcp/lib/supabase/admin.ts b/apps/mcp/lib/supabase/admin.ts new file mode 100644 index 0000000..848ea25 --- /dev/null +++ b/apps/mcp/lib/supabase/admin.ts @@ -0,0 +1,34 @@ +// Service-role Supabase client. Bypasses RLS. Used ONLY by MCP tool +// handlers, which must enforce equivalent policies in code (e.g., agent X +// can only see their own api_keys, not other agents'). +// +// Singleton because Next.js can re-import per-request in dev; we want +// connection reuse where possible. +// +// TODO Phase 4: import the typed `Database` shape from @ghbounty/db once +// frontend/lib/db.types.ts is shared via the workspace package. For now +// the client is untyped — tool handlers cast at call sites. + +import { createClient, type SupabaseClient } from "@supabase/supabase-js"; + +let _client: SupabaseClient | null = null; + +export function supabaseAdmin(): SupabaseClient { + if (_client) return _client; + + const url = process.env.SUPABASE_URL; + const key = process.env.SUPABASE_SERVICE_ROLE_KEY; + if (!url || !key) { + throw new Error( + "SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set in apps/mcp env" + ); + } + + _client = createClient(url, key, { + auth: { + persistSession: false, + autoRefreshToken: false, + }, + }); + return _client; +} diff --git a/apps/mcp/lib/tools/bounties/get.ts b/apps/mcp/lib/tools/bounties/get.ts new file mode 100644 index 0000000..9398810 --- /dev/null +++ b/apps/mcp/lib/tools/bounties/get.ts @@ -0,0 +1,76 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { authenticate } from "@/lib/auth/middleware"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { mcpError } from "@/lib/errors"; + +const GetInput = z.object({ + authorization: z.string().optional(), + id: z.string().uuid(), +}); + +export async function handleBountiesGet(raw: unknown) { + const parsed = GetInput.safeParse(raw); + if (!parsed.success) return { error: mcpError("InvalidInput", parsed.error.message) }; + + const auth = await authenticate(parsed.data.authorization); + if (!auth.ok) return { error: auth.error }; + + const supabase = supabaseAdmin(); + + const { data, error } = await supabase + .from("issues") + .select( + "id, amount, state, pda, github_issue_url, submission_count, bounty_meta(title, description, release_mode, evaluation_criteria, reject_threshold), created_at, creator" + ) + .eq("id", parsed.data.id) + .maybeSingle(); + + if (error) return { error: mcpError("InternalError", error.message) }; + if (!data) return { error: mcpError("NotFound", "Bounty not found") }; + + const row = data as any; + const meta = Array.isArray(row.bounty_meta) ? row.bounty_meta[0] : row.bounty_meta; + + // If caller is a dev, surface their submission for this bounty (if any). + let my_submission: { id: string; status: string } | null = null; + if (auth.agent.role === "dev" && row.pda) { + const { data: sub } = await supabase + .from("submissions") + .select("id, state") + .eq("issue_pda", row.pda) + .eq("solver", auth.agent.wallet_pubkey) + .maybeSingle(); + if (sub) my_submission = { id: (sub as any).id, status: (sub as any).state }; + } + + return { + bounty: { + id: row.id, + amount_sol: (Number(row.amount) / 1e9).toString(), + state: row.state, + pda: row.pda, + github_issue_url: row.github_issue_url, + title: meta?.title ?? null, + description: meta?.description ?? null, + release_mode: meta?.release_mode ?? null, + evaluation_criteria: meta?.evaluation_criteria ?? null, + reject_threshold: meta?.reject_threshold ?? null, + submission_count: row.submission_count, + created_at: row.created_at, + }, + my_submission, + }; +} + +export function registerBountiesGet(server: McpServer): void { + server.tool( + "bounties.get", + { id: z.string().uuid() }, + async (input, extra) => { + const authorization = (extra as any)?.requestInfo?.headers?.authorization; + const result = await handleBountiesGet({ ...input, authorization }); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + ); +} diff --git a/apps/mcp/lib/tools/bounties/list.ts b/apps/mcp/lib/tools/bounties/list.ts new file mode 100644 index 0000000..545b800 --- /dev/null +++ b/apps/mcp/lib/tools/bounties/list.ts @@ -0,0 +1,75 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { authenticate } from "@/lib/auth/middleware"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { mcpError } from "@/lib/errors"; + +const ListInput = z.object({ + authorization: z.string().optional(), + filter: z + .object({ + status: z.enum(["open", "resolved", "cancelled"]).optional(), + min_sol: z.string().optional(), + max_sol: z.string().optional(), + }) + .optional(), + cursor: z.string().optional(), +}); + +export async function handleBountiesList(raw: unknown) { + const parsed = ListInput.safeParse(raw); + if (!parsed.success) return { error: mcpError("InvalidInput", parsed.error.message) }; + + const auth = await authenticate(parsed.data.authorization); + if (!auth.ok) return { error: auth.error }; + + const filter = parsed.data.filter ?? {}; + const supabase = supabaseAdmin(); + + let q: any = supabase + .from("issues") + .select( + "id, amount, state, github_issue_url, submission_count, bounty_meta(title, description, release_mode), created_at" + ); + + if (filter.status) q = q.eq("state", filter.status); + + q = q.order("created_at", { ascending: false }).limit(50); + + const { data, error } = await q; + if (error) return { error: mcpError("InternalError", error.message) }; + + return { + items: (data ?? []).map((row: any) => ({ + id: row.id, + title: Array.isArray(row.bounty_meta) ? row.bounty_meta[0]?.title ?? null : row.bounty_meta?.title ?? null, + amount_sol: (Number(row.amount) / 1e9).toString(), + github_url: row.github_issue_url, + submission_count: row.submission_count, + state: row.state, + created_at: row.created_at, + })), + next_cursor: null, // simple paging in v1 + }; +} + +export function registerBountiesList(server: McpServer): void { + server.tool( + "bounties.list", + { + filter: z + .object({ + status: z.enum(["open", "resolved", "cancelled"]).optional(), + min_sol: z.string().optional(), + max_sol: z.string().optional(), + }) + .optional(), + cursor: z.string().optional(), + }, + async (input, extra) => { + const authorization = (extra as any)?.requestInfo?.headers?.authorization; + const result = await handleBountiesList({ ...input, authorization }); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + ); +} diff --git a/apps/mcp/lib/tools/create-account/complete.ts b/apps/mcp/lib/tools/create-account/complete.ts new file mode 100644 index 0000000..98a640a --- /dev/null +++ b/apps/mcp/lib/tools/create-account/complete.ts @@ -0,0 +1,211 @@ +// apps/mcp/lib/tools/create-account/complete.ts +// +// Tool: create_account.complete +// Public (no auth). Submits the signed init_stake_deposit tx. +// +// Steps (full algorithm in spec section 7): +// 1. SELECT pending_txs by (agent_account_id, tool_name='create_account.complete'). +// 404 if missing/expired/consumed. +// 2. Decode signed_tx_b64. Verify the agent's signature is present +// and the wire-bytes hash matches pending_txs.message_hash (anti-tamper). +// 3. POST to gas-station endpoint to submit. Wait for confirm. +// 4. On confirm: +// - INSERT stake_deposits row. +// - INSERT profiles, developers OR companies, wallets. +// - Mint API key, INSERT api_keys. +// - UPDATE agent_accounts.status = active. +// - UPDATE pending_txs.consumed_at. +// 5. Return { api_key, agent_id, profile, github_handle }. + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { createHash } from "node:crypto"; +import { getTransactionDecoder } from "@solana/kit"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { sponsorAndSubmit } from "@/lib/solana/gas-station-client"; +import { mintApiKey } from "@/lib/auth/api-key"; +import { mcpError, type McpError } from "@/lib/errors"; + +const CompleteInput = z.object({ + account_id: z.string().uuid(), + signed_tx_b64: z.string().min(1), +}); + +interface CompleteOk { + api_key: string; + agent_id: string; + github_handle: string; + profile: { + id: string; + role: "dev" | "company"; + wallet_pubkey: string; + }; +} +type CompleteResult = CompleteOk | { error: McpError }; + +export async function handleCreateAccountComplete(raw: unknown): Promise<CompleteResult> { + const parsed = CompleteInput.safeParse(raw); + if (!parsed.success) { + return { error: mcpError("InvalidInput", parsed.error.message) }; + } + const { account_id, signed_tx_b64 } = parsed.data; + + const supabase = supabaseAdmin(); + + // 1. Find the pending_tx row. + const { data: pending } = await supabase + .from("pending_txs") + .select("id, message_hash, expected_signer, expires_at, consumed_at") + .eq("agent_account_id", account_id) + .eq("tool_name", "create_account.complete") + .order("created_at", { ascending: false }) + .limit(1) + .single(); + + if (!pending) { + return { error: mcpError("BlockhashExpired", "No pending transaction found for this account") }; + } + const p = pending as any; + if (p.consumed_at) { + return { error: mcpError("BlockhashExpired", "Pending transaction already consumed") }; + } + if (new Date(p.expires_at) < new Date()) { + return { error: mcpError("BlockhashExpired", "Pending transaction expired") }; + } + + // 2. Decode signed tx, verify signer + wire-bytes hash. + let decoded: any; + let wireBytes: Buffer; + try { + wireBytes = Buffer.from(signed_tx_b64, "base64"); + decoded = getTransactionDecoder().decode(wireBytes); + } catch { + return { error: mcpError("InvalidSignature", "Could not decode signed transaction") }; + } + + // Verify the expected signer's signature is present and not null. + const sig = decoded.signatures?.[p.expected_signer]; + if (!sig) { + return { error: mcpError("WrongSigner", "Expected signer signature missing") }; + } + + // Verify hash matches what poll() recorded. + const actualHash = createHash("sha256").update(wireBytes).digest("hex"); + if (actualHash !== p.message_hash) { + return { error: mcpError("TxTampered", "Transaction wire bytes do not match prepared hash") }; + } + + // 3. Submit via gas station (handles fee payer signing + RPC submit + confirm). + const sponsorRes = await sponsorAndSubmit(signed_tx_b64); + if (!sponsorRes.ok || !sponsorRes.tx_hash) { + return { + error: mcpError( + sponsorRes.error?.code === "WalletInsufficientFunds" ? "WalletInsufficientFunds" : "RpcError", + sponsorRes.error?.message ?? "Sponsor failed" + ), + }; + } + + // 4. Persist post-confirmation rows. + const { data: agent } = await supabase + .from("agent_accounts") + .select("id, role, wallet_pubkey, github_handle") + .eq("id", account_id) + .single(); + + if (!agent || !(agent as any).github_handle) { + return { error: mcpError("InternalError", "Agent missing or has no github_handle") }; + } + const ag = agent as any; + + // INSERT stake_deposits. PDA derivation TODO Phase 2 — for v1, store the + // wallet pubkey + tx_hash, derive the actual PDA later from the chain. + // The DB row schema requires `pda`; use a placeholder format that includes + // the tx_hash so the row is unique and traceable. + await supabase.from("stake_deposits").insert({ + agent_account_id: account_id, + pda: `stake-deposit-pda:${ag.wallet_pubkey}`, // TODO: real PDA derivation + tx_signature: sponsorRes.tx_hash, + amount_lamports: "35000000", + locked_until: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(), + }); + + // INSERT profiles, developers/companies, wallets. + const userId = `did:agent:${ag.id}`; + await supabase.from("profiles").insert({ + user_id: userId, + role: ag.role, + github_handle: ag.github_handle, + }); + + if (ag.role === "dev") { + await supabase.from("developers").insert({ + user_id: userId, + github_handle: ag.github_handle, + }); + } else { + await supabase.from("companies").insert({ + user_id: userId, + name: ag.github_handle, + slug: ag.github_handle.toLowerCase(), + }); + } + + await supabase.from("wallets").insert({ + user_id: userId, + chain_id: "solana-mainnet", + address: ag.wallet_pubkey, + }); + + // Mint API key. + const { plaintext, prefix, hash } = mintApiKey(); + const { data: keyRow } = await supabase + .from("api_keys") + .insert({ + agent_account_id: account_id, + key_hash: hash, + key_prefix: prefix, + }) + .select("id") + .single(); + + if (!keyRow) { + return { error: mcpError("InternalError", "API key insert failed") }; + } + + // Mark agent active + consume pending_tx. + await supabase + .from("agent_accounts") + .update({ status: "active" }) + .eq("id", account_id); + + await supabase + .from("pending_txs") + .update({ consumed_at: new Date().toISOString() }) + .eq("id", p.id); + + return { + api_key: plaintext, + agent_id: ag.id, + github_handle: ag.github_handle, + profile: { + id: userId, + role: ag.role, + wallet_pubkey: ag.wallet_pubkey, + }, + }; +} + +export function registerCreateAccountComplete(server: McpServer): void { + server.tool( + "create_account.complete", + { + account_id: z.string().uuid(), + signed_tx_b64: z.string(), + }, + async (input) => { + const result = await handleCreateAccountComplete(input); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + ); +} diff --git a/apps/mcp/lib/tools/create-account/init.ts b/apps/mcp/lib/tools/create-account/init.ts new file mode 100644 index 0000000..5109316 --- /dev/null +++ b/apps/mcp/lib/tools/create-account/init.ts @@ -0,0 +1,119 @@ +// apps/mcp/lib/tools/create-account/init.ts +// +// Tool: create_account.init +// Public (no auth). Rate-limited per IP. +// +// Steps: +// 1. Validate input (role + wallet_pubkey shape). +// 2. Rate-limit by IP via createAccountLimiter. +// 3. POST GitHub /login/device/code to get user_code. +// 4. INSERT agent_accounts row with status=pending_oauth, store device_code in +// github_oauth_token_encrypted (gets overwritten with the access_token in poll). +// 5. Return account_id, user_code, verification_uri, expires_at. + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { startDeviceFlow, encryptAccessToken } from "@/lib/github/device-flow"; +import { createAccountLimiter } from "@/lib/rate-limit/upstash"; +import { mcpError, type McpError } from "@/lib/errors"; + +const InitInput = z.object({ + role: z.enum(["dev", "company"]), + wallet_pubkey: z.string().regex(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/, "Invalid Solana pubkey"), + ip: z.string().optional(), + company_info: z + .object({ + name: z.string().min(1).max(80), + slug: z.string().regex(/^[a-z0-9-]{2,40}$/), + website: z.string().url().optional(), + github_org: z.string().optional(), + }) + .optional(), +}); + +interface InitOk { + account_id: string; + user_code: string; + verification_uri: string; + expires_at: string; +} +type InitResult = InitOk | { error: McpError }; + +export async function handleCreateAccountInit(raw: unknown): Promise<InitResult> { + const parsed = InitInput.safeParse(raw); + if (!parsed.success) { + return { error: mcpError("InvalidInput", parsed.error.message) }; + } + const { role, wallet_pubkey, ip = "unknown" } = parsed.data; + + // Rate limit by IP. + const rl = await createAccountLimiter().limit(ip); + if (!rl.success) { + return { error: mcpError("RateLimited", "Too many account creation attempts from this IP") }; + } + + // Start GitHub Device Flow. + let dev: Awaited<ReturnType<typeof startDeviceFlow>>; + try { + dev = await startDeviceFlow(); + } catch (err) { + return { error: mcpError("RpcError", `GitHub Device Flow failed: ${(err as Error).message}`) }; + } + + // INSERT agent_accounts. + const supabase = supabaseAdmin(); + const { data, error } = await supabase + .from("agent_accounts") + .insert({ + role, + wallet_pubkey, + status: "pending_oauth", + github_oauth_token_encrypted: encryptAccessToken(dev.device_code), + }) + .select("id") + .single(); + + if (error) { + if (error.code === "23505") { + return { error: mcpError("Conflict", "An agent with this wallet_pubkey already exists") }; + } + return { error: mcpError("InternalError", `agent_accounts insert: ${error.message}`) }; + } + + return { + account_id: (data as any).id, + user_code: dev.user_code, + verification_uri: dev.verification_uri, + expires_at: new Date(Date.now() + dev.expires_in * 1000).toISOString(), + }; +} + +// Tool registration glue. +export function registerCreateAccountInit(server: McpServer): void { + server.tool( + "create_account.init", + { + role: z.enum(["dev", "company"]), + wallet_pubkey: z.string(), + company_info: z + .object({ + name: z.string(), + slug: z.string(), + website: z.string().optional(), + github_org: z.string().optional(), + }) + .optional(), + }, + async (input, extra) => { + const ip = + (extra as any)?.requestInfo?.headers?.["x-forwarded-for"]?.toString().split(",")[0]?.trim() || + (extra as any)?.requestInfo?.headers?.["x-real-ip"] || + "unknown"; + const result = await handleCreateAccountInit({ ...input, ip }); + return { + content: [{ type: "text", text: JSON.stringify(result, null, 2) }], + }; + } + ); +} diff --git a/apps/mcp/lib/tools/create-account/poll.ts b/apps/mcp/lib/tools/create-account/poll.ts new file mode 100644 index 0000000..c1e96b5 --- /dev/null +++ b/apps/mcp/lib/tools/create-account/poll.ts @@ -0,0 +1,215 @@ +// apps/mcp/lib/tools/create-account/poll.ts +// +// Tool: create_account.poll +// Public (no auth). +// +// Steps (full algorithm in spec section 7): +// 1. SELECT agent_accounts row by id, must be status=pending_oauth. +// 2. Decrypt the stored device_code. +// 3. POST GitHub /login/oauth/access_token with device_code. +// 4. If pending → return { status: "pending" }. +// 5. If ok: +// a. GET /user with the access_token to extract login (handle). +// b. UPDATE agent_accounts: github_handle, status=pending_stake, +// github_oauth_token_encrypted=encrypted access_token. +// c. Build unsigned tx: init_stake_deposit(35M lamports), with +// GAS_STATION_PUBKEY as fee_payer, agent's pubkey as signer. +// d. Compute message hash; INSERT pending_txs row. +// e. Return { status: "ready_to_stake", github_handle, tx_to_sign_b64, +// expected_signers, expected_program_id, stake_amount_sol }. + +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { + pollAccessToken, + fetchUserHandle, + decryptAccessToken, + encryptAccessToken, +} from "@/lib/github/device-flow"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { solanaRpc } from "@/lib/solana/rpc"; +import { mcpError, type McpError } from "@/lib/errors"; +import { + address, + appendTransactionMessageInstructions, + createTransactionMessage, + pipe, + setTransactionMessageFeePayer, + setTransactionMessageLifetimeUsingBlockhash, + compileTransaction, + getBase64EncodedWireTransaction, +} from "@solana/kit"; +import { + getInitStakeDepositInstruction, + findStakePda, +} from "@ghbounty/sdk"; +import { createHash } from "node:crypto"; + +const PollInput = z.object({ + account_id: z.string().uuid(), +}); + +const STAKE_AMOUNT = 35_000_000n; // 0.035 SOL +const PENDING_TX_TTL_SECONDS = 50; + +interface PollPending { status: "pending" } +interface PollReady { + status: "ready_to_stake"; + github_handle: string; + tx_to_sign_b64: string; + expected_signers: string[]; + expected_program_id: string; + stake_amount_sol: string; +} +type PollResult = PollPending | PollReady | { error: McpError }; + +function getProgramAddress(): string { + // The IDL-generated GHBOUNTY_ESCROW_PROGRAM_ADDRESS is "" (empty). + // Read the real address from env; fall back to the devnet address from Anchor.toml. + return ( + process.env.GHBOUNTY_PROGRAM_ADDRESS ?? + "CPZx26QXs3HjwGobr8cVAZEtF1qGzqnNbBdt7h1EwbBg" + ); +} + +export async function handleCreateAccountPoll(raw: unknown): Promise<PollResult> { + const parsed = PollInput.safeParse(raw); + if (!parsed.success) { + return { error: mcpError("InvalidInput", parsed.error.message) }; + } + const { account_id } = parsed.data; + + const supabase = supabaseAdmin(); + + // Fetch the agent row. + const { data: agent, error: agentErr } = await supabase + .from("agent_accounts") + .select("id, status, role, wallet_pubkey, github_oauth_token_encrypted") + .eq("id", account_id) + .single(); + + if (agentErr || !agent) { + return { error: mcpError("NotFound", "Agent account not found") }; + } + const a = agent as any; + if (a.status === "active") { + return { error: mcpError("Conflict", "Account already active") }; + } + if (a.status !== "pending_oauth") { + return { error: mcpError("Forbidden", `Cannot poll account with status ${a.status}`) }; + } + if (!a.github_oauth_token_encrypted) { + return { error: mcpError("InternalError", "Device code missing on account") }; + } + + // Decrypt + poll. + let device_code: string; + try { + device_code = decryptAccessToken(a.github_oauth_token_encrypted); + } catch { + return { error: mcpError("InternalError", "Failed to decrypt device code") }; + } + + const pollResult = await pollAccessToken(device_code); + if (pollResult.kind === "pending") { + return { status: "pending" }; + } + if (pollResult.kind === "error") { + return { error: mcpError("Forbidden", `GitHub Device Flow error: ${pollResult.error}`) }; + } + + // Got access_token — fetch user, update agent, build tx. + const handle = await fetchUserHandle(pollResult.access_token); + + const { error: updErr } = await supabase + .from("agent_accounts") + .update({ + github_handle: handle, + status: "pending_stake", + github_oauth_token_encrypted: encryptAccessToken(pollResult.access_token), + }) + .eq("id", account_id); + + if (updErr) { + if ((updErr as any).code === "23505") { + return { error: mcpError("Conflict", "GitHub handle already used by another agent") }; + } + return { error: mcpError("InternalError", `agent update: ${(updErr as any).message}`) }; + } + + // Build the init_stake_deposit transaction. + // Use the sync builder + explicitly derive the stake PDA ourselves, + // passing the real program address (IDL placeholder is ""). + const programAddr = address(getProgramAddress()); + const ownerAddr = address(a.wallet_pubkey); + + // Derive stake PDA using the real program address. + const [stakePdaAddr] = await findStakePda( + { owner: ownerAddr }, + { programAddress: programAddr }, + ); + + // Build a partial signer shape (address-only); signing happens later in `complete`. + const ownerSigner = { address: ownerAddr } as any; + + const ix = getInitStakeDepositInstruction( + { + owner: ownerSigner, + stake: stakePdaAddr, + amount: STAKE_AMOUNT, + }, + { programAddress: programAddr }, + ); + + const rpc = solanaRpc(); + const { value: blockhash } = await rpc.getLatestBlockhash().send(); + + const gasStationPubkey = process.env.NEXT_PUBLIC_GAS_STATION_PUBKEY; + if (!gasStationPubkey) { + return { error: mcpError("InternalError", "NEXT_PUBLIC_GAS_STATION_PUBKEY must be set") }; + } + + const message = pipe( + createTransactionMessage({ version: 0 }), + (m) => setTransactionMessageFeePayer(address(gasStationPubkey), m), + (m) => setTransactionMessageLifetimeUsingBlockhash(blockhash, m), + (m) => appendTransactionMessageInstructions([ix], m), + ); + + const compiled = compileTransaction(message); + const tx_to_sign_b64 = getBase64EncodedWireTransaction(compiled); + + // Compute message hash for anti-tamper validation in `complete`. + const wireBytes = Buffer.from(tx_to_sign_b64, "base64"); + const message_hash = createHash("sha256").update(wireBytes).digest("hex"); + + // INSERT pending_txs row. + await supabase.from("pending_txs").insert({ + agent_account_id: account_id, + tool_name: "create_account.complete", + resource_id: null, + message_hash, + expected_signer: a.wallet_pubkey, + expires_at: new Date(Date.now() + PENDING_TX_TTL_SECONDS * 1000).toISOString(), + }); + + return { + status: "ready_to_stake", + github_handle: handle, + tx_to_sign_b64, + expected_signers: [a.wallet_pubkey], + expected_program_id: getProgramAddress(), + stake_amount_sol: "0.035", + }; +} + +export function registerCreateAccountPoll(server: McpServer): void { + server.tool( + "create_account.poll", + { account_id: z.string().uuid() }, + async (input) => { + const result = await handleCreateAccountPoll(input); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + ); +} diff --git a/apps/mcp/lib/tools/register.ts b/apps/mcp/lib/tools/register.ts new file mode 100644 index 0000000..cd38ea7 --- /dev/null +++ b/apps/mcp/lib/tools/register.ts @@ -0,0 +1,18 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { registerCreateAccountInit } from "./create-account/init"; +import { registerCreateAccountPoll } from "./create-account/poll"; +import { registerCreateAccountComplete } from "./create-account/complete"; +import { registerWhoami } from "./whoami"; +import { registerBountiesList } from "./bounties/list"; +import { registerBountiesGet } from "./bounties/get"; +import { registerSubmissionsGet } from "./submissions/get"; + +export async function registerAllTools(server: McpServer): Promise<void> { + registerCreateAccountInit(server); + registerCreateAccountPoll(server); + registerCreateAccountComplete(server); + registerWhoami(server); + registerBountiesList(server); + registerBountiesGet(server); + registerSubmissionsGet(server); +} diff --git a/apps/mcp/lib/tools/submissions/get.ts b/apps/mcp/lib/tools/submissions/get.ts new file mode 100644 index 0000000..f381fde --- /dev/null +++ b/apps/mcp/lib/tools/submissions/get.ts @@ -0,0 +1,61 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { z } from "zod"; +import { authenticate } from "@/lib/auth/middleware"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { mcpError } from "@/lib/errors"; + +const GetInput = z.object({ + authorization: z.string().optional(), + submission_id: z.string().uuid(), +}); + +export async function handleSubmissionsGet(raw: unknown) { + const parsed = GetInput.safeParse(raw); + if (!parsed.success) return { error: mcpError("InvalidInput", parsed.error.message) }; + + const auth = await authenticate(parsed.data.authorization); + if (!auth.ok) return { error: auth.error }; + + const supabase = supabaseAdmin(); + const { data, error } = await supabase + .from("submissions") + .select("id, solver, pr_url, score, state, opus_report_hash, bounty:issue_pda(creator)") + .eq("id", parsed.data.submission_id) + .maybeSingle(); + + if (error) return { error: mcpError("InternalError", error.message) }; + if (!data) return { error: mcpError("NotFound", "Submission not found") }; + + const row = data as any; + const callerWallet = auth.agent.wallet_pubkey; + const isSolver = row.solver === callerWallet; + const bountyRel = Array.isArray(row.bounty) ? row.bounty[0] : row.bounty; + const isBountyOwner = bountyRel?.creator === callerWallet; + + if (!isSolver && !isBountyOwner) { + return { error: mcpError("Forbidden", "Not authorized to view this submission") }; + } + + return { + submission: { + id: row.id, + solver: row.solver, + pr_url: row.pr_url, + score: row.score, + state: row.state, + opus_report_hash: row.opus_report_hash, + }, + }; +} + +export function registerSubmissionsGet(server: McpServer): void { + server.tool( + "submissions.get", + { submission_id: z.string().uuid() }, + async (input, extra) => { + const authorization = (extra as any)?.requestInfo?.headers?.authorization; + const result = await handleSubmissionsGet({ ...input, authorization }); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + } + ); +} diff --git a/apps/mcp/lib/tools/types.ts b/apps/mcp/lib/tools/types.ts new file mode 100644 index 0000000..7313a35 --- /dev/null +++ b/apps/mcp/lib/tools/types.ts @@ -0,0 +1,13 @@ +// Shared types for tool handlers. + +export interface AgentAccount { + id: string; + role: "dev" | "company"; + status: "pending_oauth" | "pending_stake" | "active" | "suspended" | "revoked"; + wallet_pubkey: string; + github_handle: string | null; +} + +export type AuthResult = + | { ok: true; agent: AgentAccount; apiKeyId: string } + | { ok: false; error: { code: "Unauthorized" | "Forbidden"; message: string } }; diff --git a/apps/mcp/lib/tools/whoami.ts b/apps/mcp/lib/tools/whoami.ts new file mode 100644 index 0000000..4c4da4b --- /dev/null +++ b/apps/mcp/lib/tools/whoami.ts @@ -0,0 +1,44 @@ +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { authenticate } from "@/lib/auth/middleware"; +import { solanaRpc } from "@/lib/solana/rpc"; +import { address } from "@solana/kit"; + +interface WhoamiInput { + authorization?: string; +} + +export async function handleWhoami(input: WhoamiInput) { + const auth = await authenticate(input.authorization); + if (!auth.ok) { + return { error: auth.error }; + } + const { agent } = auth; + + const rpc = solanaRpc(); + let balanceLamports = 0n; + try { + const { value } = await rpc.getBalance(address(agent.wallet_pubkey)).send(); + balanceLamports = value; + } catch { + // Soft fail — RPC hiccup; return 0 balance instead of erroring. + } + + return { + agent_id: agent.id, + role: agent.role, + status: agent.status, + github_handle: agent.github_handle, + wallet_pubkey: agent.wallet_pubkey, + balances: { + sol_lamports: balanceLamports.toString(), + }, + }; +} + +export function registerWhoami(server: McpServer): void { + server.tool("whoami", {}, async (_input, extra) => { + const authorization = (extra as any)?.requestInfo?.headers?.authorization; + const result = await handleWhoami({ authorization }); + return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] }; + }); +} diff --git a/apps/mcp/next.config.ts b/apps/mcp/next.config.ts new file mode 100644 index 0000000..183b22c --- /dev/null +++ b/apps/mcp/next.config.ts @@ -0,0 +1,11 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + // Same reason as frontend: workspace packages with NodeNext-style imports + // need transpilation through Next/SWC. + transpilePackages: ["@ghbounty/sdk", "@ghbounty/shared", "@ghbounty/db"], + + reactStrictMode: true, +}; + +export default nextConfig; diff --git a/apps/mcp/package.json b/apps/mcp/package.json new file mode 100644 index 0000000..68036e2 --- /dev/null +++ b/apps/mcp/package.json @@ -0,0 +1,37 @@ +{ + "name": "@ghbounty/mcp", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "next dev --port 3001", + "build": "next build", + "start": "next start --port 3001", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@ghbounty/db": "workspace:^", + "@ghbounty/sdk": "workspace:^", + "@ghbounty/shared": "workspace:^", + "@modelcontextprotocol/sdk": "^1.0.0", + "@solana/kit": "^6.9.0", + "@supabase/supabase-js": "^2.104.1", + "@upstash/ratelimit": "^2.0.8", + "@upstash/redis": "^1.38.0", + "mcp-handler": "^1.1.0", + "bcryptjs": "^3.0.3", + "next": "16.2.4", + "react": "19.2.4", + "react-dom": "19.2.4", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/bcryptjs": "^2.4.6", + "@types/node": "^22.0.0", + "@types/react": "^19.0.0", + "@types/react-dom": "^19.0.0", + "typescript": "^5.6.0", + "vitest": "^2.1.9" + } +} diff --git a/apps/mcp/tests/auth/api-key.test.ts b/apps/mcp/tests/auth/api-key.test.ts new file mode 100644 index 0000000..6aee35b --- /dev/null +++ b/apps/mcp/tests/auth/api-key.test.ts @@ -0,0 +1,42 @@ +import { describe, it, expect } from "vitest"; +import { mintApiKey, verifyApiKey, extractPrefix } from "@/lib/auth/api-key"; + +describe("mintApiKey", () => { + it("produces a key with the correct prefix and length", () => { + const { plaintext, prefix, hash } = mintApiKey(); + expect(plaintext).toMatch(/^ghbk_live_[0-9a-f]{32}$/); + expect(prefix).toMatch(/^ghbk_live_[0-9a-f]{12}$/); + expect(plaintext.startsWith(prefix)).toBe(true); + expect(hash).not.toBe(plaintext); + expect(hash.length).toBeGreaterThan(50); // bcrypt hashes are ~60 chars + }); + + it("produces unique keys on every call", () => { + const a = mintApiKey(); + const b = mintApiKey(); + expect(a.plaintext).not.toBe(b.plaintext); + }); +}); + +describe("verifyApiKey", () => { + it("returns true for the matching plaintext", () => { + const { plaintext, hash } = mintApiKey(); + expect(verifyApiKey(plaintext, hash)).toBe(true); + }); + + it("returns false for a different plaintext", () => { + const { hash } = mintApiKey(); + expect(verifyApiKey("ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", hash)).toBe(false); + }); +}); + +describe("extractPrefix", () => { + it("returns the first 22 chars (prefix + 12 hex)", () => { + const { plaintext, prefix } = mintApiKey(); + expect(extractPrefix(plaintext)).toBe(prefix); + }); + + it("throws on invalid format", () => { + expect(() => extractPrefix("invalid_key")).toThrow(); + }); +}); diff --git a/apps/mcp/tests/auth/middleware.test.ts b/apps/mcp/tests/auth/middleware.test.ts new file mode 100644 index 0000000..73d390a --- /dev/null +++ b/apps/mcp/tests/auth/middleware.test.ts @@ -0,0 +1,130 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; +import { authenticate } from "@/lib/auth/middleware"; + +// Mock the supabase admin client +vi.mock("@/lib/supabase/admin", () => ({ + supabaseAdmin: vi.fn(), +})); + +import { supabaseAdmin } from "@/lib/supabase/admin"; + +describe("authenticate", () => { + beforeEach(() => { + vi.resetAllMocks(); + }); + + it("returns Unauthorized when header is missing", async () => { + const result = await authenticate(undefined); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("Unauthorized"); + } + }); + + it("returns Unauthorized for malformed Bearer header", async () => { + const result = await authenticate("Token abc"); + expect(result.ok).toBe(false); + }); + + it("returns Unauthorized when prefix not found in DB", async () => { + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + is: () => ({ + maybeSingle: () => Promise.resolve({ data: null, error: null }), + }), + }), + }), + }), + }); + + const result = await authenticate("Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("Unauthorized"); + } + }); + + it("returns the agent_account when prefix matches and bcrypt verifies", async () => { + const { mintApiKey } = await import("@/lib/auth/api-key"); + const { plaintext, hash } = mintApiKey(); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + is: () => ({ + maybeSingle: () => + Promise.resolve({ + data: { + id: "key-uuid", + key_hash: hash, + agent_account_id: "agent-uuid", + agent_accounts: { + id: "agent-uuid", + role: "dev", + status: "active", + wallet_pubkey: "7xK...", + github_handle: "claudebot42", + }, + }, + error: null, + }), + }), + }), + }), + update: () => ({ + eq: () => ({ + then: (cb: any) => cb(), + }), + }), + }), + }); + + const result = await authenticate(`Bearer ${plaintext}`); + expect(result.ok).toBe(true); + if (result.ok) { + expect(result.agent.role).toBe("dev"); + expect(result.agent.status).toBe("active"); + } + }); + + it("returns Forbidden when agent status is not active", async () => { + const { mintApiKey } = await import("@/lib/auth/api-key"); + const { plaintext, hash } = mintApiKey(); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + is: () => ({ + maybeSingle: () => + Promise.resolve({ + data: { + id: "key-uuid", + key_hash: hash, + agent_account_id: "agent-uuid", + agent_accounts: { + id: "agent-uuid", + role: "dev", + status: "suspended", + wallet_pubkey: "7xK...", + github_handle: "claudebot42", + }, + }, + error: null, + }), + }), + }), + }), + }), + }); + + const result = await authenticate(`Bearer ${plaintext}`); + expect(result.ok).toBe(false); + if (!result.ok) { + expect(result.error.code).toBe("Forbidden"); + } + }); +}); diff --git a/apps/mcp/tests/e2e/onboarding.test.ts b/apps/mcp/tests/e2e/onboarding.test.ts new file mode 100644 index 0000000..b1a47c8 --- /dev/null +++ b/apps/mcp/tests/e2e/onboarding.test.ts @@ -0,0 +1,66 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +// Wire mocks for Supabase, GitHub, gas-station, RPC, and rate-limit. +vi.mock("@/lib/supabase/admin", () => ({ supabaseAdmin: vi.fn() })); +vi.mock("@/lib/github/device-flow"); +vi.mock("@/lib/solana/gas-station-client", () => ({ + sponsorAndSubmit: vi.fn().mockResolvedValue({ ok: true, tx_hash: "MOCK_TX_HASH" }), +})); +vi.mock("@/lib/rate-limit/upstash", () => ({ + createAccountLimiter: () => ({ limit: () => Promise.resolve({ success: true }) }), +})); +vi.mock("@/lib/solana/rpc", () => ({ + solanaRpc: () => ({ + getLatestBlockhash: () => ({ + send: () => + Promise.resolve({ + value: { blockhash: "1".repeat(32), lastValidBlockHeight: 1n }, + }), + }), + getBalance: () => ({ send: () => Promise.resolve({ value: 100_000_000n }) }), + }), +})); + +import { handleCreateAccountInit } from "@/lib/tools/create-account/init"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { startDeviceFlow } from "@/lib/github/device-flow"; + +describe("E2E: onboarding init smoke", () => { + beforeEach(() => { + vi.resetAllMocks(); + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + }); + + it("init returns user_code + persists agent row", async () => { + (startDeviceFlow as any).mockResolvedValue({ + device_code: "DEV", + user_code: "ABCD-1234", + verification_uri: "https://github.com/login/device", + expires_in: 900, + interval: 5, + }); + + const insertCall = vi.fn().mockReturnValue({ + select: () => ({ + single: () => Promise.resolve({ data: { id: "agent-1" }, error: null }), + }), + }); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ insert: insertCall }), + }); + + const result = await handleCreateAccountInit({ + role: "dev", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + ip: "192.0.2.1", + }); + + if ("error" in result) { + throw new Error(`Expected success, got: ${result.error.code}`); + } + expect(result.user_code).toBe("ABCD-1234"); + expect(result.account_id).toBe("agent-1"); + expect(insertCall).toHaveBeenCalledOnce(); + }); +}); diff --git a/apps/mcp/tests/github/device-flow.test.ts b/apps/mcp/tests/github/device-flow.test.ts new file mode 100644 index 0000000..9127fb4 --- /dev/null +++ b/apps/mcp/tests/github/device-flow.test.ts @@ -0,0 +1,122 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; +import { + startDeviceFlow, + pollAccessToken, + fetchUserHandle, +} from "@/lib/github/device-flow"; + +const realFetch = global.fetch; + +describe("GitHub Device Flow client", () => { + beforeEach(() => { + process.env.GITHUB_OAUTH_CLIENT_ID = "test_client_id"; + }); + + afterEach(() => { + global.fetch = realFetch; + }); + + describe("startDeviceFlow", () => { + it("posts client_id + scope and returns the device_code", async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => + Promise.resolve({ + device_code: "DEV_CODE", + user_code: "ABCD-1234", + verification_uri: "https://github.com/login/device", + expires_in: 900, + interval: 5, + }), + }); + + const result = await startDeviceFlow(); + expect(result.device_code).toBe("DEV_CODE"); + expect(result.user_code).toBe("ABCD-1234"); + + const calls = (global.fetch as any).mock.calls; + expect(calls[0][0]).toBe("https://github.com/login/device/code"); + expect(calls[0][1].method).toBe("POST"); + }); + }); + + describe("pollAccessToken", () => { + it("returns the access_token when GitHub returns success", async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ access_token: "TOKEN_123", token_type: "bearer" }), + }); + + const result = await pollAccessToken("DEV_CODE"); + expect(result.kind).toBe("ok"); + if (result.kind === "ok") { + expect(result.access_token).toBe("TOKEN_123"); + } + }); + + it("returns 'pending' when authorization_pending", async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ error: "authorization_pending" }), + }); + + const result = await pollAccessToken("DEV_CODE"); + expect(result.kind).toBe("pending"); + }); + + it("returns 'error' for any other GitHub error", async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => Promise.resolve({ error: "expired_token" }), + }); + + const result = await pollAccessToken("DEV_CODE"); + expect(result.kind).toBe("error"); + if (result.kind === "error") { + expect(result.error).toBe("expired_token"); + } + }); + }); + + describe("fetchUserHandle", () => { + it("returns login for the user authenticated by the access token", async () => { + global.fetch = vi.fn().mockResolvedValue({ + ok: true, + json: () => + Promise.resolve({ + login: "claudebot42", + id: 12345, + email: "claudebot@example.com", + }), + }); + + const handle = await fetchUserHandle("TOKEN_123"); + expect(handle).toBe("claudebot42"); + }); + }); +}); + +describe("token encryption", () => { + beforeEach(() => { + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + }); + + it("encryptAccessToken / decryptAccessToken round-trip", async () => { + const { encryptAccessToken, decryptAccessToken } = await import( + "@/lib/github/device-flow" + ); + const plaintext = "ghu_1234567890abcdef"; + const encrypted = encryptAccessToken(plaintext); + expect(encrypted).not.toBe(plaintext); + expect(decryptAccessToken(encrypted)).toBe(plaintext); + }); + + it("decrypt fails on tampered ciphertext", async () => { + const { encryptAccessToken, decryptAccessToken } = await import( + "@/lib/github/device-flow" + ); + const enc = encryptAccessToken("plaintext"); + const tampered = enc.slice(0, -2) + "AA"; + expect(() => decryptAccessToken(tampered)).toThrow(); + }); +}); diff --git a/apps/mcp/tests/rate-limit/upstash.test.ts b/apps/mcp/tests/rate-limit/upstash.test.ts new file mode 100644 index 0000000..5b8dc6e --- /dev/null +++ b/apps/mcp/tests/rate-limit/upstash.test.ts @@ -0,0 +1,19 @@ +import { describe, it, expect } from "vitest"; + +const HAS_UPSTASH = + !!process.env.UPSTASH_REDIS_REST_URL && !!process.env.UPSTASH_REDIS_REST_TOKEN; + +describe.skipIf(!HAS_UPSTASH)("Upstash rate limiter (live)", () => { + it("createAccountLimiter rejects on the 6th request from same IP within an hour", async () => { + const { createAccountLimiter } = await import("@/lib/rate-limit/upstash"); + const limiter = createAccountLimiter(); + const ip = `test:${Date.now()}`; + + for (let i = 0; i < 5; i++) { + const r = await limiter.limit(ip); + expect(r.success).toBe(true); + } + const sixth = await limiter.limit(ip); + expect(sixth.success).toBe(false); + }, 30_000); +}); diff --git a/apps/mcp/tests/tools/bounties.test.ts b/apps/mcp/tests/tools/bounties.test.ts new file mode 100644 index 0000000..e465e87 --- /dev/null +++ b/apps/mcp/tests/tools/bounties.test.ts @@ -0,0 +1,89 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("@/lib/supabase/admin", () => ({ supabaseAdmin: vi.fn() })); +vi.mock("@/lib/auth/middleware"); + +import { handleBountiesList } from "@/lib/tools/bounties/list"; +import { authenticate } from "@/lib/auth/middleware"; +import { supabaseAdmin } from "@/lib/supabase/admin"; + +describe("bounties.list", () => { + beforeEach(() => vi.resetAllMocks()); + + it("returns Unauthorized when not authed", async () => { + (authenticate as any).mockResolvedValue({ + ok: false, + error: { code: "Unauthorized", message: "no key" }, + }); + const result = await handleBountiesList({ authorization: undefined }); + expect((result as any).error.code).toBe("Unauthorized"); + }); + + it("returns paginated open bounties", async () => { + (authenticate as any).mockResolvedValue({ + ok: true, + agent: { id: "a", role: "dev", status: "active", wallet_pubkey: "7", github_handle: "h" }, + }); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + order: () => ({ + limit: () => + Promise.resolve({ + data: [ + { + id: "b1", + amount: "1000000000", + state: "open", + github_issue_url: "x", + submission_count: 0, + bounty_meta: [{ title: "t" }], + created_at: "2026-05-06", + }, + ], + error: null, + }), + }), + }), + }), + }), + }); + + const result = await handleBountiesList({ + authorization: "Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + filter: { status: "open" }, + }); + expect((result as any).items).toHaveLength(1); + expect((result as any).items[0].id).toBe("b1"); + expect((result as any).items[0].amount_sol).toBe("1"); + }); +}); + +describe("bounties.get", () => { + beforeEach(() => vi.resetAllMocks()); + + it("returns 404 for unknown id", async () => { + const { handleBountiesGet } = await import("@/lib/tools/bounties/get"); + (authenticate as any).mockResolvedValue({ + ok: true, + agent: { id: "a", role: "dev", status: "active", wallet_pubkey: "7", github_handle: "h" }, + }); + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + maybeSingle: () => Promise.resolve({ data: null, error: null }), + }), + }), + }), + }); + + const result = await handleBountiesGet({ + authorization: "Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + id: "00000000-0000-0000-0000-000000000099", + }); + expect((result as any).error.code).toBe("NotFound"); + }); +}); diff --git a/apps/mcp/tests/tools/create-account.test.ts b/apps/mcp/tests/tools/create-account.test.ts new file mode 100644 index 0000000..093938f --- /dev/null +++ b/apps/mcp/tests/tools/create-account.test.ts @@ -0,0 +1,362 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("@/lib/supabase/admin", () => ({ + supabaseAdmin: vi.fn(), +})); +vi.mock("@/lib/github/device-flow"); +vi.mock("@/lib/rate-limit/upstash", () => ({ + createAccountLimiter: () => ({ limit: () => Promise.resolve({ success: true }) }), +})); + +import { handleCreateAccountInit } from "@/lib/tools/create-account/init"; +import { supabaseAdmin } from "@/lib/supabase/admin"; +import { startDeviceFlow } from "@/lib/github/device-flow"; + +describe("create_account.init handler", () => { + beforeEach(() => { + vi.resetAllMocks(); + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + }); + + it("inserts agent_accounts row + returns user_code", async () => { + (startDeviceFlow as any).mockResolvedValue({ + device_code: "DEV_CODE_AAA", + user_code: "ABCD-1234", + verification_uri: "https://github.com/login/device", + expires_in: 900, + interval: 5, + }); + + const insertChain = { + insert: vi.fn().mockReturnValue({ + select: vi.fn().mockReturnValue({ + single: vi.fn().mockResolvedValue({ + data: { id: "agent-uuid-1" }, + error: null, + }), + }), + }), + }; + (supabaseAdmin as any).mockReturnValue({ + from: () => insertChain, + }); + + const result = await handleCreateAccountInit({ + role: "dev", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + ip: "192.0.2.1", + }); + + if ("error" in result) { + throw new Error(`Expected ok, got error: ${result.error.code}`); + } + expect(result.user_code).toBe("ABCD-1234"); + expect(result.account_id).toBe("agent-uuid-1"); + expect(insertChain.insert).toHaveBeenCalledOnce(); + }); + + it("returns Conflict 409 if wallet_pubkey already exists", async () => { + (startDeviceFlow as any).mockResolvedValue({ + device_code: "DEV_CODE_AAA", + user_code: "ABCD-1234", + verification_uri: "https://github.com/login/device", + expires_in: 900, + interval: 5, + }); + const insertChain = { + insert: () => ({ + select: () => ({ + single: () => + Promise.resolve({ + data: null, + error: { code: "23505", message: "duplicate key value violates unique constraint" }, + }), + }), + }), + }; + (supabaseAdmin as any).mockReturnValue({ + from: () => insertChain, + }); + + const result = await handleCreateAccountInit({ + role: "dev", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + ip: "192.0.2.1", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(result.error.code).toBe("Conflict"); + }); + + it("returns RateLimited when limiter rejects", async () => { + vi.doMock("@/lib/rate-limit/upstash", () => ({ + createAccountLimiter: () => ({ limit: () => Promise.resolve({ success: false }) }), + })); + vi.resetModules(); + const { handleCreateAccountInit } = await import("@/lib/tools/create-account/init"); + + const result = await handleCreateAccountInit({ + role: "dev", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + ip: "192.0.2.1", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(result.error.code).toBe("RateLimited"); + + vi.doUnmock("@/lib/rate-limit/upstash"); + }); +}); + +describe("create_account.poll handler", () => { + beforeEach(() => { + vi.resetAllMocks(); + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + process.env.NEXT_PUBLIC_GAS_STATION_PUBKEY = "11111111111111111111111111111112"; + process.env.SOLANA_RPC_URL = "https://api.devnet.solana.com"; + }); + + it("returns 'pending' when GitHub still polling", async () => { + const { handleCreateAccountPoll } = await import( + "@/lib/tools/create-account/poll" + ); + const { pollAccessToken, decryptAccessToken } = await import("@/lib/github/device-flow"); + (pollAccessToken as any).mockResolvedValue({ kind: "pending" }); + (decryptAccessToken as any).mockReturnValue("DEV_CODE_DECRYPTED"); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + single: () => + Promise.resolve({ + data: { + id: "agent-uuid-1", + status: "pending_oauth", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + role: "dev", + github_oauth_token_encrypted: "encrypted_device_code_b64", + }, + error: null, + }), + }), + }), + }), + }); + + const result = await handleCreateAccountPoll({ account_id: "00000000-0000-0000-0000-000000000001" }); + expect((result as any).status).toBe("pending"); + }); + + it("returns NotFound when account_id doesn't exist", async () => { + const { handleCreateAccountPoll } = await import( + "@/lib/tools/create-account/poll" + ); + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + single: () => Promise.resolve({ data: null, error: { message: "not found" } }), + }), + }), + }), + }); + + const result = await handleCreateAccountPoll({ account_id: "00000000-0000-0000-0000-000000000099" }); + if (!("error" in result)) throw new Error("expected error"); + expect((result as any).error.code).toBe("NotFound"); + }); + + it("returns Forbidden when account already active", async () => { + const { handleCreateAccountPoll } = await import( + "@/lib/tools/create-account/poll" + ); + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + single: () => + Promise.resolve({ + data: { + id: "agent-1", + status: "active", + wallet_pubkey: "7xK...", + role: "dev", + github_oauth_token_encrypted: "abc", + }, + error: null, + }), + }), + }), + }), + }); + + const result = await handleCreateAccountPoll({ account_id: "00000000-0000-0000-0000-000000000001" }); + if (!("error" in result)) throw new Error("expected error"); + // Conflict because account is already active + expect((result as any).error.code).toBe("Conflict"); + }); +}); + +describe("create_account.complete handler", () => { + beforeEach(() => { + vi.resetAllMocks(); + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + process.env.GAS_STATION_SPONSOR_URL = "https://example.com/api/gas-station/sponsor"; + process.env.GAS_STATION_SERVICE_TOKEN = "test-token"; + }); + + it("returns BlockhashExpired when pending_txs row is gone or expired", async () => { + const { handleCreateAccountComplete } = await import( + "@/lib/tools/create-account/complete" + ); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + eq: () => ({ + order: () => ({ + limit: () => ({ + single: () => + Promise.resolve({ data: null, error: null }), + }), + }), + }), + }), + }), + }), + }); + + const result = await handleCreateAccountComplete({ + account_id: "00000000-0000-0000-0000-000000000001", + signed_tx_b64: "AQAB", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(result.error.code).toBe("BlockhashExpired"); + }); + + it("returns BlockhashExpired when pending_tx already consumed", async () => { + const { handleCreateAccountComplete } = await import( + "@/lib/tools/create-account/complete" + ); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + eq: () => ({ + order: () => ({ + limit: () => ({ + single: () => + Promise.resolve({ + data: { + id: "tx-1", + message_hash: "abc", + expected_signer: "7xK...", + expires_at: new Date(Date.now() + 30000).toISOString(), + consumed_at: new Date().toISOString(), + }, + error: null, + }), + }), + }), + }), + }), + }), + }), + }); + + const result = await handleCreateAccountComplete({ + account_id: "00000000-0000-0000-0000-000000000001", + signed_tx_b64: "AQAB", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(result.error.code).toBe("BlockhashExpired"); + }); + + it("returns BlockhashExpired when pending_tx is past expires_at", async () => { + const { handleCreateAccountComplete } = await import( + "@/lib/tools/create-account/complete" + ); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + eq: () => ({ + order: () => ({ + limit: () => ({ + single: () => + Promise.resolve({ + data: { + id: "tx-1", + message_hash: "abc", + expected_signer: "7xK...", + expires_at: new Date(Date.now() - 30000).toISOString(), + consumed_at: null, + }, + error: null, + }), + }), + }), + }), + }), + }), + }), + }); + + const result = await handleCreateAccountComplete({ + account_id: "00000000-0000-0000-0000-000000000001", + signed_tx_b64: "AQAB", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(result.error.code).toBe("BlockhashExpired"); + }); +}); + +describe("create_account.complete signature validation", () => { + beforeEach(() => { + vi.resetAllMocks(); + process.env.MCP_TOKEN_ENCRYPTION_KEY = "x".repeat(32); + process.env.GAS_STATION_SPONSOR_URL = "https://example.com/api/gas-station/sponsor"; + process.env.GAS_STATION_SERVICE_TOKEN = "test-token"; + }); + + it("returns InvalidSignature when signed_tx_b64 is malformed", async () => { + const { handleCreateAccountComplete } = await import( + "@/lib/tools/create-account/complete" + ); + + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + eq: () => ({ + order: () => ({ + limit: () => ({ + single: () => + Promise.resolve({ + data: { + id: "tx-1", + message_hash: "abc", + expected_signer: "7xK...", + expires_at: new Date(Date.now() + 30000).toISOString(), + consumed_at: null, + }, + error: null, + }), + }), + }), + }), + }), + }), + }), + }); + + const result = await handleCreateAccountComplete({ + account_id: "00000000-0000-0000-0000-000000000001", + signed_tx_b64: "not-valid-base64-or-tx", + }); + if (!("error" in result)) throw new Error("expected error"); + expect(["InvalidSignature", "WrongSigner", "TxTampered"]).toContain(result.error.code); + }); +}); diff --git a/apps/mcp/tests/tools/submissions.test.ts b/apps/mcp/tests/tools/submissions.test.ts new file mode 100644 index 0000000..7aaa7a4 --- /dev/null +++ b/apps/mcp/tests/tools/submissions.test.ts @@ -0,0 +1,79 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("@/lib/supabase/admin", () => ({ supabaseAdmin: vi.fn() })); +vi.mock("@/lib/auth/middleware"); + +import { handleSubmissionsGet } from "@/lib/tools/submissions/get"; +import { authenticate } from "@/lib/auth/middleware"; +import { supabaseAdmin } from "@/lib/supabase/admin"; + +describe("submissions.get", () => { + beforeEach(() => vi.resetAllMocks()); + + it("403 when caller is neither solver nor bounty company", async () => { + (authenticate as any).mockResolvedValue({ + ok: true, + agent: { id: "a", role: "dev", status: "active", wallet_pubkey: "OTHER_WALLET", github_handle: "h" }, + }); + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + maybeSingle: () => + Promise.resolve({ + data: { + id: "00000000-0000-0000-0000-000000000001", + solver: "DIFFERENT_WALLET", + pr_url: "https://github.com/o/r/pull/1", + score: null, + state: "Pending", + bounty: { creator: "COMPANY_WALLET" }, + }, + error: null, + }), + }), + }), + }), + }); + + const result = await handleSubmissionsGet({ + authorization: "Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + submission_id: "00000000-0000-0000-0000-000000000001", + }); + expect((result as any).error.code).toBe("Forbidden"); + }); + + it("returns submission when caller is the solver", async () => { + (authenticate as any).mockResolvedValue({ + ok: true, + agent: { id: "a", role: "dev", status: "active", wallet_pubkey: "SOLVER_WALLET", github_handle: "h" }, + }); + (supabaseAdmin as any).mockReturnValue({ + from: () => ({ + select: () => ({ + eq: () => ({ + maybeSingle: () => + Promise.resolve({ + data: { + id: "00000000-0000-0000-0000-000000000001", + solver: "SOLVER_WALLET", + pr_url: "https://github.com/o/r/pull/1", + score: 7, + state: "Scored", + bounty: { creator: "COMPANY_WALLET" }, + }, + error: null, + }), + }), + }), + }), + }); + + const result = await handleSubmissionsGet({ + authorization: "Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + submission_id: "00000000-0000-0000-0000-000000000001", + }); + expect((result as any).submission.id).toBe("00000000-0000-0000-0000-000000000001"); + expect((result as any).submission.score).toBe(7); + }); +}); diff --git a/apps/mcp/tests/tools/whoami.test.ts b/apps/mcp/tests/tools/whoami.test.ts new file mode 100644 index 0000000..0d9b4ec --- /dev/null +++ b/apps/mcp/tests/tools/whoami.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect, vi, beforeEach } from "vitest"; + +vi.mock("@/lib/supabase/admin", () => ({ supabaseAdmin: vi.fn() })); +vi.mock("@/lib/auth/middleware"); +vi.mock("@/lib/solana/rpc", () => ({ + solanaRpc: () => ({ + getBalance: () => ({ + send: () => Promise.resolve({ value: 100_000_000n }), + }), + }), +})); + +import { handleWhoami } from "@/lib/tools/whoami"; +import { authenticate } from "@/lib/auth/middleware"; + +describe("whoami handler", () => { + beforeEach(() => vi.resetAllMocks()); + + it("returns Unauthorized when middleware rejects", async () => { + (authenticate as any).mockResolvedValue({ + ok: false, + error: { code: "Unauthorized", message: "no key" }, + }); + const result = await handleWhoami({ authorization: undefined }); + expect((result as any).error.code).toBe("Unauthorized"); + }); + + it("returns agent info + balance when authorized", async () => { + (authenticate as any).mockResolvedValue({ + ok: true, + apiKeyId: "key-uuid", + agent: { + id: "agent-uuid", + role: "dev", + status: "active", + wallet_pubkey: "7xK7gE8FpQrSjVz9mYwGtCkBtNvDtTvPzGjGpZqMxKqp", + github_handle: "claudebot", + }, + }); + const result = await handleWhoami({ authorization: "Bearer ghbk_live_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" }); + expect((result as any).agent_id).toBe("agent-uuid"); + expect((result as any).balances.sol_lamports).toBe("100000000"); + }); +}); diff --git a/apps/mcp/tsconfig.json b/apps/mcp/tsconfig.json new file mode 100644 index 0000000..a4b11b3 --- /dev/null +++ b/apps/mcp/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "jsx": "preserve", + "incremental": true, + "plugins": [{ "name": "next" }], + "noUncheckedIndexedAccess": false, + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], + "exclude": ["node_modules", ".next"] +} diff --git a/apps/mcp/vercel.json b/apps/mcp/vercel.json new file mode 100644 index 0000000..94b6efa --- /dev/null +++ b/apps/mcp/vercel.json @@ -0,0 +1,12 @@ +{ + "framework": "nextjs", + "buildCommand": "cd ../.. && pnpm --filter @ghbounty/mcp build", + "installCommand": "cd ../.. && pnpm install --frozen-lockfile", + "outputDirectory": ".next", + "regions": ["iad1"], + "functions": { + "app/api/mcp/[transport]/route.ts": { + "maxDuration": 60 + } + } +} diff --git a/apps/mcp/vitest.config.ts b/apps/mcp/vitest.config.ts new file mode 100644 index 0000000..dad143e --- /dev/null +++ b/apps/mcp/vitest.config.ts @@ -0,0 +1,13 @@ +import { defineConfig } from "vitest/config"; +import { resolve } from "node:path"; + +export default defineConfig({ + resolve: { + alias: { + "@": resolve(__dirname, "."), + }, + }, + test: { + passWithNoTests: true, + }, +}); diff --git a/docs/superpowers/decisions/2026-05-06-mcp-rate-limit-backend.md b/docs/superpowers/decisions/2026-05-06-mcp-rate-limit-backend.md new file mode 100644 index 0000000..450c8ed --- /dev/null +++ b/docs/superpowers/decisions/2026-05-06-mcp-rate-limit-backend.md @@ -0,0 +1,24 @@ +# Decision — Rate-limit backend (Upstash via Vercel Marketplace) + +**Date:** 2026-05-06 +**Status:** Accepted +**Resolves:** OQ #5 in `docs/superpowers/specs/2026-05-05-ghbounty-mcp-server-design.md` + +## Decision +Use Upstash Redis (`@upstash/redis` + `@upstash/ratelimit`), **provisioned via Vercel Marketplace** (Project → Storage → Browse Marketplace → Upstash → Connect). NO separate Upstash account needed; Vercel manages the integration end-to-end. + +## Why Upstash (provisioned via Vercel) +- `@upstash/ratelimit` is purpose-built for serverless: sliding window, fixed window, token bucket — all atomic, all REST-based (no connection pooling issues). +- Mature: years in production at thousands of Vercel deployments. +- Free tier covers our v1 traffic (10K requests/day). +- REST API works from any JS runtime (Edge, Node, browser); no socket setup. +- **Vercel Marketplace provisioning means zero new accounts, single billing through Vercel.** + +## Why NOT pure Postgres (Supabase) rate limiting +Considered. Pros: zero new services. Cons: needs an atomic Postgres function (sliding window with row-level locking) — non-trivial SQL, slower (30-50ms p95 vs ~5ms for Redis), risks DB connection saturation under load. Not worth the complexity for v1. + +## Setup +- Production Vercel env: connect Upstash Redis instance via Marketplace (sized "Free" or "Pay-as-you-go"). +- Preview Vercel env: connect a separate Upstash instance (or share with prod for v1 — only adds noise to metrics, doesn't break anything). +- Vercel auto-injects `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` into the deployment env. No copy-paste of secrets. +- Configured in Task 13 (code) + Task 32 (provisioning during deploy). diff --git a/docs/superpowers/decisions/2026-05-06-mcp-vercel-deploy.md b/docs/superpowers/decisions/2026-05-06-mcp-vercel-deploy.md new file mode 100644 index 0000000..5cf2017 --- /dev/null +++ b/docs/superpowers/decisions/2026-05-06-mcp-vercel-deploy.md @@ -0,0 +1,78 @@ +# Decision — Vercel deploy config for ghbounty-mcp + +**Date:** 2026-05-06 +**Status:** Accepted (manual provisioning pending user action) + +## Project +- Slug: `ghbounty-mcp` +- Team: `weareghbounty-6269s-projects` (same as frontend, per OQ #4 decision in `2026-05-06-mcp-vercel-team.md`) +- Framework: Next.js 16 +- Root directory: `apps/mcp` +- Region: `iad1` +- `app/api/mcp/[transport]/route.ts` `maxDuration`: 60s +- `vercel.json` config inside `apps/mcp/` already wired + +## Provisioning steps (USER, one-time) + +### 1. Create the Vercel project +- Open https://vercel.com/new +- Import `Ghbounty/GhBounty` repo (already connected) +- Project name: `ghbounty-mcp` +- Team: `weareghbounty-6269s-projects` +- Root Directory: `apps/mcp` +- Framework Preset: Next.js (auto-detected) +- Build & Output: leave defaults (the repo's `apps/mcp/vercel.json` overrides them) +- DO NOT deploy yet — set env vars first. + +### 2. Set env vars (production + preview, in dashboard) + +Required in BOTH `production` and `preview`: + +| Var | Value | +|---|---| +| `GITHUB_OAUTH_CLIENT_ID` | `Iv23liabu10KaQEjpH9w` (public, from Phase 0 GitHub App) | +| `GITHUB_OAUTH_CLIENT_SECRET` | from `~/.ghbounty/github-app-credentials.json` (or 1Password) | +| `SUPABASE_URL` | same value as the frontend's | +| `SUPABASE_SERVICE_ROLE_KEY` | NEW key — generate in Supabase dashboard, distinct from frontend's. Rotate independently. | +| `SOLANA_RPC_URL` | Helius mainnet for `production`, devnet for `preview` | +| `GAS_STATION_SPONSOR_URL` | `https://www.ghbounty.com/api/gas-station/sponsor` (prod) or preview URL | +| `GAS_STATION_SERVICE_TOKEN` | Generate a random 64-char hex (`openssl rand -hex 32`). Add SAME value to the frontend's env too. | +| `NEXT_PUBLIC_GAS_STATION_PUBKEY` | same as frontend's `GAS_STATION_PUBKEY` | +| `MCP_TOKEN_ENCRYPTION_KEY` | random 32-byte hex (`openssl rand -hex 32`). Used for AES-256-GCM at-rest encryption of GitHub tokens. | +| `GHBOUNTY_PROGRAM_ADDRESS` | `CPZx26QXs3HjwGobr8cVAZEtF1qGzqnNbBdt7h1EwbBg` (Anchor.toml default — replace if program is redeployed) | +| `NEXT_PUBLIC_MCP_BASE_URL` | `https://mcp.ghbounty.com` | + +### 3. Provision Upstash Redis via Vercel Marketplace +- Vercel dashboard → `ghbounty-mcp` project → **Storage** tab +- Click **Browse Marketplace** → search **Upstash** → **Redis** → **Connect** +- Plan: Free +- Attach to: Production environment +- Vercel auto-injects `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` (no manual entry) +- Repeat for Preview environment (separate instance recommended; ok to share with prod for v1) + +### 4. Configure DNS for `mcp.ghbounty.com` +- Vercel dashboard → `ghbounty-mcp` project → **Settings → Domains** +- Add `mcp.ghbounty.com` +- Vercel issues an SSL cert automatically once DNS resolves +- DNS provider (Vercel-managed if the root `ghbounty.com` is also on Vercel): add `CNAME mcp → cname.vercel-dns.com` + +### 5. First deploy +- Push the `feat/mcp-phase-1-onboarding` branch (already done by Phase 1 Task 34 below) +- Vercel auto-deploys preview from the PR +- Once PR merges to main, production deploy goes out +- Smoke test: + ```bash + curl https://mcp.ghbounty.com/api/health + ``` + Expected: `{ "ok": true, "service": "ghbounty-mcp", ... }` + +## Frontend follow-up needed (separate PR) + +The MCP server's `gas-station-client.ts` POSTs to the frontend's `/api/gas-station/sponsor` with a new `x-mcp-service-token` header. The frontend's `gas-station-route-core.ts` needs to accept this auth path (in addition to existing Privy bearer auth). + +Tasks for that PR: +- Add `GAS_STATION_SERVICE_TOKEN` to the frontend's prod + preview Vercel env (same value as MCP project) +- Update `frontend/lib/gas-station-route-core.ts` to accept requests with a matching `x-mcp-service-token` header +- Test that requests from the MCP project are accepted; requests with wrong/missing token are rejected (401) + +Until that lands, the MCP's `create_account.complete` will fail at the gas-station call with 401. The MCP server itself works in dev (mocked) but cannot complete on-chain submissions in production until the frontend update merges. diff --git a/docs/superpowers/decisions/2026-05-06-mcp-vercel-team.md b/docs/superpowers/decisions/2026-05-06-mcp-vercel-team.md new file mode 100644 index 0000000..e467dcf --- /dev/null +++ b/docs/superpowers/decisions/2026-05-06-mcp-vercel-team.md @@ -0,0 +1,19 @@ +# Decision — Vercel team for ghbounty-mcp project + +**Date:** 2026-05-06 +**Status:** Accepted +**Resolves:** OQ #4 in `docs/superpowers/specs/2026-05-05-ghbounty-mcp-server-design.md` + +## Decision +Deploy the `ghbounty-mcp` Vercel project under the same team as the frontend: `weareghbounty-6269`. + +## Why same team +- Single billing surface +- Same DNS root (`ghbounty.com`) — DNS records live with the team +- Less context-switching for ops + +## Why NOT a separate team +The "isolated team for security" argument is theoretical for v1. The threat model: if the frontend's deploy access is compromised, an attacker would already have access to the customer-facing domain. Putting the MCP server on a separate team doesn't materially reduce blast radius if the same humans have access to both. + +## Mitigation +The MCP server uses a separate Supabase service-role key from the frontend's. Rotate independently. Vercel env vars are scoped per-project, so leaking the frontend's vars does NOT leak the MCP's. diff --git a/docs/superpowers/plans/2026-05-06-mcp-phase-1-onboarding.md b/docs/superpowers/plans/2026-05-06-mcp-phase-1-onboarding.md index 01da344..77577d6 100644 --- a/docs/superpowers/plans/2026-05-06-mcp-phase-1-onboarding.md +++ b/docs/superpowers/plans/2026-05-06-mcp-phase-1-onboarding.md @@ -22,7 +22,7 @@ 2. Solana CLI 3.1.14 + Anchor 0.30.1 + Rust 1.89 (installed during Phase 0). 3. `~/.ghbounty/github-app-credentials.json` exists locally with the GitHub App credentials registered in Phase 0. 4. Device Flow toggle activated in https://github.com/organizations/Ghbounty/settings/apps/ghbounty-mcp (manual one-time step). -5. Upstash account exists (free tier is fine). Two Redis databases: one for production, one for preview/dev. Both have `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` from the dashboard. +5. **No external Upstash account needed.** Provisioned via Vercel Marketplace from inside the `ghbounty-mcp` Vercel project (Storage tab → Browse Marketplace → Upstash → Connect). Vercel manages billing through your existing Vercel account; env vars `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` are auto-injected. Configure two instances: one attached to Production env, another attached to Preview env. _The npm package `@upstash/redis` is the same — only the provisioning path is via Vercel UI._ 6. Helius Solana RPC key (free tier OK for dev, paid for production). --- @@ -158,21 +158,23 @@ The MCP server uses a separate Supabase service-role key from the frontend's. Ro **Resolves:** OQ #5 in `docs/superpowers/specs/2026-05-05-ghbounty-mcp-server-design.md` ## Decision -Use Upstash Redis (`@upstash/redis` + `@upstash/ratelimit`) for rate limiting. +Use Upstash Redis (`@upstash/redis` + `@upstash/ratelimit`), **provisioned via Vercel Marketplace** (Project → Storage → Browse Marketplace → Upstash → Connect). NO separate Upstash account needed; Vercel manages the integration end-to-end. -## Why Upstash +## Why Upstash (provisioned via Vercel) - `@upstash/ratelimit` is purpose-built for serverless: sliding window, fixed window, token bucket — all atomic, all REST-based (no connection pooling issues). - Mature: years in production at thousands of Vercel deployments. - Free tier covers our v1 traffic (10K requests/day). - REST API works from any JS runtime (Edge, Node, browser); no socket setup. +- **Vercel Marketplace provisioning means zero new accounts, single billing through Vercel.** -## Why NOT Vercel KV -- Newer (released 2023). Less battle-tested for rate limiting specifically. -- Would couple our rate-limit infra to Vercel; harder to migrate if we ever leave Vercel. -- The `@vercel/kv` package wraps Upstash Redis under the hood anyway — using Upstash directly skips a layer. +## Why NOT pure Postgres (Supabase) rate limiting +Considered. Pros: zero new services. Cons: needs an atomic Postgres function (sliding window with row-level locking) — non-trivial SQL, slower (30-50ms p95 vs ~5ms for Redis), risks DB connection saturation under load. Not worth the complexity for v1. ## Setup -Each environment (preview, production) gets its own Upstash Redis instance. Env vars: `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN`. Configured in Task 13. +- Production Vercel env: connect Upstash Redis instance via Marketplace (sized "Free" or "Pay-as-you-go"). +- Preview Vercel env: connect a separate Upstash instance (or share with prod for v1 — only adds noise to metrics, doesn't break anything). +- Vercel auto-injects `UPSTASH_REDIS_REST_URL` + `UPSTASH_REDIS_REST_TOKEN` into the deployment env. No copy-paste of secrets. +- Configured in Task 13 (code) + Task 32 (provisioning during deploy). ``` - [ ] **Step 4: Commit** @@ -3453,9 +3455,13 @@ vercel env add SUPABASE_SERVICE_ROLE_KEY production # NEW key — generate in Supabase dashboard, distinct from frontend's vercel env add SOLANA_RPC_URL production # Helius mainnet URL -vercel env add UPSTASH_REDIS_REST_URL production -# from Upstash dashboard (production DB) -vercel env add UPSTASH_REDIS_REST_TOKEN production +# UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN are auto-injected by +# Vercel when you connect Upstash via the Marketplace. Skip these CLI +# commands — instead, in the Vercel dashboard: +# 1. Go to Project → Storage → Browse Marketplace → Upstash → Connect +# 2. Pick "Free" plan; attach to Production env +# 3. Repeat for Preview env (or share with prod for v1) +# Vercel populates the env vars automatically. vercel env add GAS_STATION_SPONSOR_URL production # https://www.ghbounty.com/api/gas-station/sponsor vercel env add GAS_STATION_SERVICE_TOKEN production @@ -3488,7 +3494,7 @@ Repeat the entire list for `preview` (use a separate Upstash DB for preview, but - `GITHUB_OAUTH_CLIENT_ID`, `GITHUB_OAUTH_CLIENT_SECRET` - `SUPABASE_URL`, `SUPABASE_SERVICE_ROLE_KEY` (NEW key, distinct from frontend's) - `SOLANA_RPC_URL` (Helius mainnet for prod, devnet for preview) -- `UPSTASH_REDIS_REST_URL`, `UPSTASH_REDIS_REST_TOKEN` (separate DBs per env) +- `UPSTASH_REDIS_REST_URL`, `UPSTASH_REDIS_REST_TOKEN` — auto-injected by Vercel after connecting Upstash via Storage Marketplace (no manual entry) - `GAS_STATION_SPONSOR_URL` (= frontend prod URL) - `GAS_STATION_SERVICE_TOKEN` (NEW shared secret with frontend; add to frontend env too) - `NEXT_PUBLIC_GAS_STATION_PUBKEY` (= frontend's value) @@ -3638,6 +3644,6 @@ After implementation, run through before opening the PR: ## Estimated effort -5-7 days for one engineer who already has the env set up (Phase 0 done, GitHub App registered, Upstash account ready). Tasks 19-24 (the 3 onboarding tools with their tx-building) are the long stretch. +5-7 days for one engineer who already has the env set up (Phase 0 done, GitHub App registered). Upstash KV provisioning happens during Task 32 via the Vercel UI — no extra account creation needed. Tasks 19-24 (the 3 onboarding tools with their tx-building) are the long stretch. If unfamiliar with `@vercel/mcp-adapter` and `@solana/kit` 6.x: pad to 8-10 days. The Codama-generated builders may need adapter calls that the spec didn't anticipate. diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8267b48..5519217 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,6 +12,70 @@ importers: specifier: ^9.1.7 version: 9.1.7 + apps/mcp: + dependencies: + '@ghbounty/db': + specifier: workspace:^ + version: link:../../packages/db + '@ghbounty/sdk': + specifier: workspace:^ + version: link:../../packages/sdk + '@ghbounty/shared': + specifier: workspace:^ + version: link:../../packages/shared + '@modelcontextprotocol/sdk': + specifier: ^1.0.0 + version: 1.29.0(zod@3.25.76) + '@solana/kit': + specifier: ^6.9.0 + version: 6.9.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) + '@supabase/supabase-js': + specifier: ^2.104.1 + version: 2.104.1(bufferutil@4.1.0)(utf-8-validate@6.0.6) + '@upstash/ratelimit': + specifier: ^2.0.8 + version: 2.0.8(@upstash/redis@1.38.0) + '@upstash/redis': + specifier: ^1.38.0 + version: 1.38.0 + bcryptjs: + specifier: ^3.0.3 + version: 3.0.3 + mcp-handler: + specifier: ^1.1.0 + version: 1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@3.25.76))(next@16.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)) + next: + specifier: 16.2.4 + version: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: 19.2.4 + version: 19.2.4 + react-dom: + specifier: 19.2.4 + version: 19.2.4(react@19.2.4) + zod: + specifier: ^3.23.8 + version: 3.25.76 + devDependencies: + '@types/bcryptjs': + specifier: ^2.4.6 + version: 2.4.6 + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + '@types/react': + specifier: ^19.0.0 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.0.0 + version: 19.2.3(@types/react@19.2.14) + typescript: + specifier: ^5.6.0 + version: 5.9.3 + vitest: + specifier: ^2.1.9 + version: 2.1.9(@types/node@22.19.17)(happy-dom@20.9.0(bufferutil@4.1.0)(utf-8-validate@6.0.6)) + frontend: dependencies: '@coral-xyz/anchor': @@ -22,7 +86,7 @@ importers: version: link:../packages/shared '@privy-io/react-auth': specifier: ^3.22.2 - version: 3.22.2(@solana-program/memo@0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/system@0.10.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/token@0.9.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@4.3.6) + version: 3.22.2(@solana-program/memo@0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/system@0.10.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/token@0.9.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@4.3.6) '@solana-program/memo': specifier: ^0.11.0 version: 0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)) @@ -86,7 +150,7 @@ importers: dependencies: drizzle-orm: specifier: ^0.45.2 - version: 0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(postgres@3.4.9)(prisma@5.22.0) + version: 0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(@upstash/redis@1.38.0)(postgres@3.4.9)(prisma@5.22.0) postgres: specifier: ^3.4.9 version: 3.4.9 @@ -191,7 +255,7 @@ importers: version: 17.4.2 drizzle-orm: specifier: ^0.45.2 - version: 0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(postgres@3.4.9)(prisma@5.22.0) + version: 0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(@upstash/redis@1.38.0)(postgres@3.4.9)(prisma@5.22.0) genlayer-js: specifier: ^1.1.7 version: 1.1.7(bufferutil@4.1.0)(eslint@9.39.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6) @@ -1077,6 +1141,12 @@ packages: peerDependencies: react: '>= 16 || ^19.0.0-rc' + '@hono/node-server@1.19.14': + resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + engines: {node: '>=18.14.1'} + peerDependencies: + hono: ^4 + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -1360,6 +1430,16 @@ packages: resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} engines: {node: '>=16.0.0'} + '@modelcontextprotocol/sdk@1.29.0': + resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} + engines: {node: '>=18'} + peerDependencies: + '@cfworker/json-schema': ^4.1.1 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + '@cfworker/json-schema': + optional: true + '@msgpack/msgpack@3.1.2': resolution: {integrity: sha512-JEW4DEtBzfe8HvUYecLU9e6+XJnKDlUAIve8FvPzF3Kzs6Xo/KuZkZJsDH0wJXl/qEZbeeE7edxDNY3kMs39hQ==} engines: {node: '>= 18'} @@ -1604,6 +1684,35 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + '@redis/bloom@1.2.0': + resolution: {integrity: sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/client@1.6.1': + resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==} + engines: {node: '>=14'} + + '@redis/graph@1.1.1': + resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/json@1.0.7': + resolution: {integrity: sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/search@1.2.0': + resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} + peerDependencies: + '@redis/client': ^1.0.0 + + '@redis/time-series@1.1.0': + resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} + peerDependencies: + '@redis/client': ^1.0.0 + '@reown/appkit-common@1.7.8': resolution: {integrity: sha512-ridIhc/x6JOp7KbDdwGKY4zwf8/iK8EYBl+HtWrruutSLwZyVi5P8WaZa+8iajL6LcDcDF7LoyLwMTym7SRuwQ==} @@ -3054,6 +3163,9 @@ packages: '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@types/bcryptjs@2.4.6': + resolution: {integrity: sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==} + '@types/bn.js@5.2.0': resolution: {integrity: sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==} @@ -3264,6 +3376,18 @@ packages: cpu: [x64] os: [win32] + '@upstash/core-analytics@0.0.10': + resolution: {integrity: sha512-7qJHGxpQgQr9/vmeS1PktEwvNAF7TI4iJDi8Pu2CFZ9YUGHZH4fOP5TfYlZ4aVxfopnELiE4BS4FBjyK7V1/xQ==} + engines: {node: '>=16.0.0'} + + '@upstash/ratelimit@2.0.8': + resolution: {integrity: sha512-YSTMBJ1YIxsoPkUMX/P4DDks/xV5YYCswWMamU8ZIfK9ly6ppjRnVOyBhMDXBmzjODm4UQKcxsJPvaeFAijp5w==} + peerDependencies: + '@upstash/redis': ^1.34.3 + + '@upstash/redis@1.38.0': + resolution: {integrity: sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg==} + '@vitest/expect@2.1.9': resolution: {integrity: sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==} @@ -3510,6 +3634,10 @@ packages: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} + accepts@2.0.0: + resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==} + engines: {node: '>= 0.6'} + acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -3524,9 +3652,20 @@ packages: resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} engines: {node: '>= 8.0.0'} + ajv-formats@3.0.1: + resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + ajv@8.20.0: + resolution: {integrity: sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==} + ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -3644,6 +3783,10 @@ packages: engines: {node: '>=6.0.0'} hasBin: true + bcryptjs@3.0.3: + resolution: {integrity: sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g==} + hasBin: true + big.js@6.2.2: resolution: {integrity: sha512-y/ie+Faknx7sZA5MfGA2xKlu0GDv8RWrXGsmlteyJQ2lvoKv9GBK/fpRMc2qlSoBAgNxrixICFCBefIq8WCQpQ==} @@ -3653,6 +3796,10 @@ packages: bn.js@5.2.3: resolution: {integrity: sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==} + body-parser@2.2.2: + resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} + engines: {node: '>=18'} + borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} @@ -3695,6 +3842,10 @@ packages: resolution: {integrity: sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==} engines: {node: '>=6.14.2'} + bytes@3.1.2: + resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} + engines: {node: '>= 0.8'} + cac@6.7.14: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} @@ -3767,6 +3918,10 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} + cluster-key-slot@1.1.2: + resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} + engines: {node: '>=0.10.0'} + codama@1.6.0: resolution: {integrity: sha512-JKydzwNYJkGjkZ98ipehd3hJksLQU6nYS7x0GPjOwD0wih+xP8q7WCKgleN8LM2sRuC75rfpr3uXLXSpQpBYKA==} hasBin: true @@ -3785,6 +3940,10 @@ packages: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} + commander@11.1.0: + resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} + engines: {node: '>=16'} + commander@14.0.2: resolution: {integrity: sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==} engines: {node: '>=20'} @@ -3799,12 +3958,28 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + content-disposition@1.1.0: + resolution: {integrity: sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==} + engines: {node: '>=18'} + + content-type@1.0.5: + resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} + engines: {node: '>= 0.6'} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} cookie-es@1.2.3: resolution: {integrity: sha512-lXVyvUvrNXblMqzIRrxHb57UUVmqsSWlxqt3XIjCkUP0wDAf6uicO6KMbEgYrMNtEvWgWHwe42CKxPu9MYAnWw==} + cookie-signature@1.2.2: + resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==} + engines: {node: '>=6.6.0'} + + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + cookie@1.1.1: resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==} engines: {node: '>=18'} @@ -3812,6 +3987,10 @@ packages: core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + crc-32@1.2.2: resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} engines: {node: '>=0.8'} @@ -3925,6 +4104,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + derive-valtio@0.1.0: resolution: {integrity: sha512-OCg2UsLbXK7GmmpzMXhYkdO64vhJ1ROUUGaTFyHjVwEdMEcTTRj7W1TxLbSBxdY8QLBPCcp66MTyaSy0RpO17A==} peerDependencies: @@ -4061,6 +4244,9 @@ packages: resolution: {integrity: sha512-wG99Zcfcys9fZux7Cft8BAX/YrOJLJSZ3jyYPfhZHqN2E+Ffx+QXBDsv3gubEgPtV6dTzJMSQUwk1H98/t/0wQ==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + electron-to-chromium@1.5.344: resolution: {integrity: sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==} @@ -4073,6 +4259,10 @@ packages: encode-utf8@1.0.3: resolution: {integrity: sha512-ucAnuBEhUK4boH2HjVYG5Q2mQyPorvv0u/ocS+zhdw0S8AlHYY+GOFhP1Gio5z4icpP2ivFSvhtFjQi8+T9ppw==} + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} @@ -4162,6 +4352,9 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} @@ -4289,6 +4482,10 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + eth-block-tracker@7.1.0: resolution: {integrity: sha512-8YdplnuE1IK4xfqpf4iU7oBxnOYAc35934o083G8ao+8WM8QQtt/mVlAY6yIAdY1eMeLqg4Z//PZjJGmWGPMRg==} engines: {node: '>=14.0.0'} @@ -4326,10 +4523,28 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + eventsource-parser@3.0.8: + resolution: {integrity: sha512-70QWGkr4snxr0OXLRWsFLeRBIRPuQOvt4s8QYjmUlmlkyTZkRqS7EDVRZtzU3TiyDbXSzaOeF0XUKy8PchzukQ==} + engines: {node: '>=18.0.0'} + + eventsource@3.0.7: + resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} + engines: {node: '>=18.0.0'} + expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} + express-rate-limit@8.5.1: + resolution: {integrity: sha512-5O6KYmyJEpuPJV5hNTXKbAHWRqrzyu+OI3vUnSd2kXFubIVpG7ezpgxQy76Zo5GQZtrQBg86hF+CM/NX+cioiQ==} + engines: {node: '>= 16'} + peerDependencies: + express: '>= 4.11' + + express@5.2.1: + resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} + engines: {node: '>= 18'} + extension-port-stream@3.0.0: resolution: {integrity: sha512-an2S5quJMiy5bnZKEf6AkfH/7r8CzHvhchU40gxN+OM6HPhe7Z9T1FUychcf2M9PpPOO0Hf7BAEfJkw2TDIBDw==} engines: {node: '>=12.0.0'} @@ -4367,6 +4582,9 @@ packages: fast-stable-stringify@1.0.0: resolution: {integrity: sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag==} + fast-uri@3.1.2: + resolution: {integrity: sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -4394,6 +4612,10 @@ packages: resolution: {integrity: sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==} engines: {node: '>=0.10.0'} + finalhandler@2.1.1: + resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==} + engines: {node: '>= 18.0.0'} + find-up@4.1.0: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} @@ -4426,6 +4648,14 @@ packages: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + + fresh@2.0.0: + resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==} + engines: {node: '>= 0.8'} + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -4445,6 +4675,10 @@ packages: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} + generic-pool@3.9.0: + resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==} + engines: {node: '>= 4'} + genlayer-js@1.1.7: resolution: {integrity: sha512-4X5AAW0k6eqP1x11F39wbfyhn3IBNlqj+gS89PuY9mBwxjaf3c9JrszVzBZ7OoYW6M5nuZ2kvCP9Dm0/rdPRkA==} @@ -4546,6 +4780,10 @@ packages: resolution: {integrity: sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==} engines: {node: '>=16.9.0'} + http-errors@2.0.1: + resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} + engines: {node: '>= 0.8'} + humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} @@ -4558,6 +4796,10 @@ packages: resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==} engines: {node: '>=20.0.0'} + iconv-lite@0.7.2: + resolution: {integrity: sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==} + engines: {node: '>=0.10.0'} + idb-keyval@6.2.1: resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} @@ -4590,6 +4832,14 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + ip-address@10.2.0: + resolution: {integrity: sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==} + engines: {node: '>= 12'} + + ipaddr.js@1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -4671,6 +4921,9 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} + is-promise@4.0.0: + resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -4791,6 +5044,12 @@ packages: json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-schema-typed@8.0.2: + resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} + json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} @@ -4897,9 +5156,27 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mcp-handler@1.1.0: + resolution: {integrity: sha512-MVCES7g18gcoZy+R/3v5nadkUMzMAWdos8jRl6DyljOKvd2/ZKDmwlCjL6zp4vo+7FeCXOYL1uWinHWlkKAAUg==} + hasBin: true + peerDependencies: + '@modelcontextprotocol/sdk': 1.26.0 + next: '>=13.0.0' + peerDependenciesMeta: + next: + optional: true + md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} + media-typer@1.1.0: + resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==} + engines: {node: '>= 0.8'} + + merge-descriptors@2.0.0: + resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==} + engines: {node: '>=18'} + merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} @@ -4915,10 +5192,18 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} + mime-types@3.0.2: + resolution: {integrity: sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==} + engines: {node: '>=18'} + minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} @@ -4959,6 +5244,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + next@16.2.4: resolution: {integrity: sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q==} engines: {node: '>=20.9.0'} @@ -5061,6 +5350,10 @@ packages: resolution: {integrity: sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==} engines: {node: '>=14.0.0'} + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -5153,6 +5446,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -5164,6 +5461,9 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-to-regexp@8.4.2: + resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -5217,6 +5517,10 @@ packages: resolution: {integrity: sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==} hasBin: true + pkce-challenge@5.0.1: + resolution: {integrity: sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==} + engines: {node: '>=16.20.0'} + pngjs@5.0.0: resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} engines: {node: '>=10.13.0'} @@ -5313,6 +5617,10 @@ packages: prop-types@15.8.1: resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-addr@2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} + proxy-compare@2.6.0: resolution: {integrity: sha512-8xuCeM3l8yqdmbPoYeLbrAXCBWu19XEYc5/F28f5qOaoAIMyfmBUkl5axiK+x9olUvRlcekvnm98AP9RDngOIw==} @@ -5339,6 +5647,10 @@ packages: engines: {node: '>=10.13.0'} hasBin: true + qs@6.15.1: + resolution: {integrity: sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==} + engines: {node: '>=0.6'} + query-string@7.1.3: resolution: {integrity: sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==} engines: {node: '>=6'} @@ -5352,6 +5664,14 @@ packages: radix3@1.1.2: resolution: {integrity: sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==} + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + raw-body@3.0.2: + resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} + engines: {node: '>= 0.10'} + react-aria@3.48.0: resolution: {integrity: sha512-jQjd4rBEIMqecBaAKYJbVGK6EqIHLa5znVQ7jwFyK5vCyljoj6KhgtiahmcIPsG5vG5vEDLw+ba+bEWn6A2P4w==} peerDependencies: @@ -5404,6 +5724,9 @@ packages: resolution: {integrity: sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==} engines: {node: '>= 12.13.0'} + redis@4.7.1: + resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==} + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -5416,6 +5739,10 @@ packages: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} @@ -5440,6 +5767,10 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true + router@2.2.0: + resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==} + engines: {node: '>= 18'} + rpc-websockets@9.3.8: resolution: {integrity: sha512-7r+fm4tSJmLf9GvZfL1DJ1SJwpagpp6AazqM0FUaeV7CA+7+NYINSk1syWa4tU/6OF2CyBicLtzENGmXRJH6wQ==} @@ -5468,6 +5799,9 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + scheduler@0.27.0: resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} @@ -5491,6 +5825,14 @@ packages: engines: {node: '>=10'} hasBin: true + send@1.2.1: + resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} + engines: {node: '>= 18'} + + serve-static@2.2.1: + resolution: {integrity: sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==} + engines: {node: '>= 18'} + set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} @@ -5509,6 +5851,9 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + sha.js@2.4.12: resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} engines: {node: '>= 0.10'} @@ -5596,6 +5941,10 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + statuses@2.0.2: + resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==} + engines: {node: '>= 0.8'} + std-env@3.10.0: resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} @@ -5760,6 +6109,10 @@ packages: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + toml@3.0.0: resolution: {integrity: sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==} @@ -5793,6 +6146,10 @@ packages: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} + type-is@2.0.1: + resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5856,6 +6213,10 @@ packages: undici-types@8.2.0: resolution: {integrity: sha512-uciYZ5yCmf+QJb18kJw10HjquzM7K0z992vWcI+84KeBpTfXT4hfgfGJ5DQbf/mCBPACofkrjvqgcjZfuujjFA==} + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + unrs-resolver@1.11.1: resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} @@ -5995,6 +6356,10 @@ packages: react: optional: true + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + viem@2.23.2: resolution: {integrity: sha512-NVmW/E0c5crMOtbEAqMF0e3NmvQykFXhLOc/CkLIXOlzHSA6KXVz3CYVmaKqBF8/xtjsjHAGjdJN3Ru1kFJLaA==} peerDependencies: @@ -6213,6 +6578,9 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + yargs-parser@18.1.3: resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} engines: {node: '>=6'} @@ -6225,6 +6593,11 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} + peerDependencies: + zod: ^3.25.28 || ^4 + zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} @@ -7091,6 +7464,10 @@ snapshots: dependencies: react: 19.2.4 + '@hono/node-server@1.19.14(hono@4.12.15)': + dependencies: + hono: 4.12.15 + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -7436,6 +7813,28 @@ snapshots: transitivePeerDependencies: - supports-color + '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': + dependencies: + '@hono/node-server': 1.19.14(hono@4.12.15) + ajv: 8.20.0 + ajv-formats: 3.0.1(ajv@8.20.0) + content-type: 1.0.5 + cors: 2.8.6 + cross-spawn: 7.0.6 + eventsource: 3.0.7 + eventsource-parser: 3.0.8 + express: 5.2.1 + express-rate-limit: 8.5.1(express@5.2.1) + hono: 4.12.15 + jose: 6.2.3 + json-schema-typed: 8.0.2 + pkce-challenge: 5.0.1 + raw-body: 3.0.2 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - supports-color + '@msgpack/msgpack@3.1.2': {} '@napi-rs/wasm-runtime@0.2.12': @@ -7610,7 +8009,7 @@ snapshots: '@privy-io/popup@0.0.4': {} - '@privy-io/react-auth@3.22.2(@solana-program/memo@0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/system@0.10.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/token@0.9.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@4.3.6)': + '@privy-io/react-auth@3.22.2(@solana-program/memo@0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/system@0.10.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana-program/token@0.9.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)))(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6))(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@base-org/account': 1.1.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@4.3.6) '@coinbase/wallet-sdk': 4.3.2 @@ -7633,8 +8032,8 @@ snapshots: '@simplewebauthn/browser': 13.3.0 '@tanstack/react-virtual': 3.13.24(react-dom@19.2.4(react@19.2.4))(react@19.2.4) '@wallet-standard/app': 1.1.0 - '@walletconnect/ethereum-provider': 2.22.4(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@walletconnect/universal-provider': 2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/ethereum-provider': 2.22.4(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/universal-provider': 2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) eventemitter3: 5.0.4 fast-password-entropy: 1.1.1 jose: 4.15.9 @@ -7653,7 +8052,7 @@ snapshots: tinycolor2: 1.6.0 uuid: 8.3.2 viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - x402: 0.7.3(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6) + x402: 0.7.3(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6) zustand: 5.0.12(@types/react@19.2.14)(react@19.2.4)(use-sync-external-store@1.4.0(react@19.2.4)) optionalDependencies: '@solana-program/memo': 0.11.0(@solana/kit@6.8.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)) @@ -7725,6 +8124,32 @@ snapshots: dependencies: react: 19.2.4 + '@redis/bloom@1.2.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + + '@redis/client@1.6.1': + dependencies: + cluster-key-slot: 1.1.2 + generic-pool: 3.9.0 + yallist: 4.0.0 + + '@redis/graph@1.1.1(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + + '@redis/json@1.0.7(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + + '@redis/search@1.2.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + + '@redis/time-series@1.1.0(@redis/client@1.6.1)': + dependencies: + '@redis/client': 1.6.1 + '@reown/appkit-common@1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.22.4)': dependencies: big.js: 6.2.2 @@ -7769,11 +8194,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: @@ -7804,11 +8229,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@reown/appkit-controllers@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@reown/appkit-wallet': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) valtio: 2.1.7(@types/react@19.2.14)(react@19.2.4) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) transitivePeerDependencies: @@ -7839,12 +8264,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) lit: 3.3.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: @@ -7875,12 +8300,12 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@reown/appkit-pay@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) + '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) lit: 3.3.0 valtio: 2.1.7(@types/react@19.2.14)(react@19.2.4) transitivePeerDependencies: @@ -7919,12 +8344,12 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: @@ -7956,12 +8381,12 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6)': + '@reown/appkit-scaffold-ui@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6)': dependencies: '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) + '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) '@reown/appkit-wallet': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) lit: 3.3.0 transitivePeerDependencies: @@ -7993,10 +8418,10 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 @@ -8028,11 +8453,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@reown/appkit-ui@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@phosphor-icons/webcomponents': 2.1.5 '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@reown/appkit-wallet': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) lit: 3.3.0 qrcode: 1.5.3 @@ -8064,14 +8489,14 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) transitivePeerDependencies: @@ -8102,15 +8527,15 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6)': + '@reown/appkit-utils@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6)': dependencies: '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@reown/appkit-polyfills': 1.8.9 '@reown/appkit-wallet': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) '@wallet-standard/wallet': 1.1.0 '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) valtio: 2.1.7(@types/react@19.2.14)(react@19.2.4) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) transitivePeerDependencies: @@ -8163,18 +8588,18 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-controllers': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-pay': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) + '@reown/appkit-ui': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit-utils': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) - '@walletconnect/types': 2.21.0 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.0(@upstash/redis@1.38.0) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) @@ -8206,17 +8631,17 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@reown/appkit@1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@reown/appkit-common': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-pay': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-controllers': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-pay': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@reown/appkit-polyfills': 1.8.9 - '@reown/appkit-scaffold-ui': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) - '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) + '@reown/appkit-scaffold-ui': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) + '@reown/appkit-ui': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit-utils': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(valtio@2.1.7(@types/react@19.2.14)(react@19.2.4))(zod@4.3.6) '@reown/appkit-wallet': 1.8.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6) - '@walletconnect/universal-provider': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/universal-provider': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) bs58: 6.0.0 semver: 7.7.2 valtio: 2.1.7(@types/react@19.2.14)(react@19.2.4) @@ -9920,6 +10345,8 @@ snapshots: tslib: 2.8.1 optional: true + '@types/bcryptjs@2.4.6': {} + '@types/bn.js@5.2.0': dependencies: '@types/node': 20.19.39 @@ -10125,6 +10552,19 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.11.1': optional: true + '@upstash/core-analytics@0.0.10': + dependencies: + '@upstash/redis': 1.38.0 + + '@upstash/ratelimit@2.0.8(@upstash/redis@1.38.0)': + dependencies: + '@upstash/core-analytics': 0.0.10 + '@upstash/redis': 1.38.0 + + '@upstash/redis@1.38.0': + dependencies: + uncrypto: 0.1.3 + '@vitest/expect@2.1.9': dependencies: '@vitest/spy': 2.1.9 @@ -10173,7 +10613,7 @@ snapshots: loupe: 3.2.1 tinyrainbow: 1.2.0 - '@wagmi/connectors@6.2.0(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@6.2.0(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76))(zod@3.25.76)': dependencies: '@base-org/account': 2.4.0(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) '@coinbase/wallet-sdk': 4.3.6(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(zod@3.25.76) @@ -10182,9 +10622,9 @@ snapshots: '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - porto: 0.2.35(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76)) + porto: 0.2.35(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76)) viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) optionalDependencies: typescript: 5.9.3 @@ -10255,21 +10695,21 @@ snapshots: dependencies: '@wallet-standard/base': 1.1.0 - '@walletconnect/core@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.0(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -10299,21 +10739,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/core@2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -10343,21 +10783,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/core@2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.9 - '@walletconnect/utils': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/types': 2.21.9(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.39.3 events: 3.3.0 @@ -10387,21 +10827,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/core@2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/jsonrpc-ws-connection': 1.0.16(bufferutil@4.1.0)(utf-8-validate@6.0.6) - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 3.0.0 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(typescript@5.9.3)(zod@4.3.6) + '@walletconnect/types': 2.22.4(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.22.4(@upstash/redis@1.38.0)(typescript@5.9.3)(zod@4.3.6) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.39.3 events: 3.3.0 @@ -10435,18 +10875,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) + '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1(@upstash/redis@1.38.0) + '@walletconnect/universal-provider': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/utils': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10476,19 +10916,19 @@ snapshots: - utf-8-validate - zod - '@walletconnect/ethereum-provider@2.22.4(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/ethereum-provider@2.22.4(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: - '@reown/appkit': 1.8.9(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@reown/appkit': 1.8.9(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 3.0.0 - '@walletconnect/sign-client': 2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@walletconnect/types': 2.22.4 - '@walletconnect/universal-provider': 2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@walletconnect/utils': 2.22.4(typescript@5.9.3)(zod@4.3.6) + '@walletconnect/sign-client': 2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/types': 2.22.4(@upstash/redis@1.38.0) + '@walletconnect/universal-provider': 2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/utils': 2.22.4(@upstash/redis@1.38.0)(typescript@5.9.3)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10565,11 +11005,11 @@ snapshots: - bufferutil - utf-8-validate - '@walletconnect/keyvaluestorage@1.1.1': + '@walletconnect/keyvaluestorage@1.1.1(@upstash/redis@1.38.0)': dependencies: '@walletconnect/safe-json': 1.0.2 idb-keyval: 6.2.2 - unstorage: 1.17.5(idb-keyval@6.2.2) + unstorage: 1.17.5(@upstash/redis@1.38.0)(idb-keyval@6.2.2) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -10616,16 +11056,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.0(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10652,16 +11092,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: - '@walletconnect/core': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/core': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10688,16 +11128,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/sign-client@2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/core': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 2.1.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.9 - '@walletconnect/utils': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/types': 2.21.9(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10724,16 +11164,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/sign-client@2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: - '@walletconnect/core': 2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/core': 2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-utils': 1.0.8 '@walletconnect/logger': 3.0.0 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(typescript@5.9.3)(zod@4.3.6) + '@walletconnect/types': 2.22.4(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.22.4(@upstash/redis@1.38.0)(typescript@5.9.3)(zod@4.3.6) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -10764,12 +11204,12 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/types@2.21.0': + '@walletconnect/types@2.21.0(@upstash/redis@1.38.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -10793,12 +11233,12 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.21.1': + '@walletconnect/types@2.21.1(@upstash/redis@1.38.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -10822,12 +11262,12 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.21.9': + '@walletconnect/types@2.21.9(@upstash/redis@1.38.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -10851,12 +11291,12 @@ snapshots: - ioredis - uploadthing - '@walletconnect/types@2.22.4': + '@walletconnect/types@2.22.4(@upstash/redis@1.38.0)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 3.0.0 events: 3.3.0 transitivePeerDependencies: @@ -10880,18 +11320,18 @@ snapshots: - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.21.0 - '@walletconnect/utils': 2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.0(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -10920,18 +11360,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - '@walletconnect/types': 2.21.1 - '@walletconnect/utils': 2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) + '@walletconnect/types': 2.21.1(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -10960,18 +11400,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/universal-provider@2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@walletconnect/types': 2.21.9 - '@walletconnect/utils': 2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/sign-client': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/types': 2.21.9(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -11000,18 +11440,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/universal-provider@2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/jsonrpc-http-connection': 1.0.8 '@walletconnect/jsonrpc-provider': 1.0.14 '@walletconnect/jsonrpc-types': 1.0.4 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 3.0.0 - '@walletconnect/sign-client': 2.22.4(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) - '@walletconnect/types': 2.22.4 - '@walletconnect/utils': 2.22.4(typescript@5.9.3)(zod@4.3.6) + '@walletconnect/sign-client': 2.22.4(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6) + '@walletconnect/types': 2.22.4(@upstash/redis@1.38.0) + '@walletconnect/utils': 2.22.4(@upstash/redis@1.38.0)(typescript@5.9.3)(zod@4.3.6) es-toolkit: 1.39.3 events: 3.3.0 transitivePeerDependencies: @@ -11040,18 +11480,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.0 + '@walletconnect/types': 2.21.0(@upstash/redis@1.38.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 @@ -11084,18 +11524,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.1 + '@walletconnect/types': 2.21.1(@upstash/redis@1.38.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 @@ -11128,7 +11568,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.9(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': + '@walletconnect/utils@2.21.9(@upstash/redis@1.38.0)(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)': dependencies: '@msgpack/msgpack': 3.1.2 '@noble/ciphers': 1.3.0 @@ -11136,12 +11576,12 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.21.9 + '@walletconnect/types': 2.21.9(@upstash/redis@1.38.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 blakejs: 1.2.1 @@ -11174,7 +11614,7 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.22.4(typescript@5.9.3)(zod@4.3.6)': + '@walletconnect/utils@2.22.4(@upstash/redis@1.38.0)(typescript@5.9.3)(zod@4.3.6)': dependencies: '@msgpack/msgpack': 3.1.2 '@noble/ciphers': 1.3.0 @@ -11182,13 +11622,13 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.38.0) '@walletconnect/logger': 3.0.0 '@walletconnect/relay-api': 1.0.11 '@walletconnect/relay-auth': 1.1.0 '@walletconnect/safe-json': 1.0.2 '@walletconnect/time': 1.0.2 - '@walletconnect/types': 2.22.4 + '@walletconnect/types': 2.22.4(@upstash/redis@1.38.0) '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 blakejs: 1.2.1 @@ -11272,6 +11712,11 @@ snapshots: dependencies: event-target-shim: 5.0.1 + accepts@2.0.0: + dependencies: + mime-types: 3.0.2 + negotiator: 1.0.0 + acorn-jsx@5.3.2(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -11282,6 +11727,10 @@ snapshots: dependencies: humanize-ms: 1.2.1 + ajv-formats@3.0.1(ajv@8.20.0): + optionalDependencies: + ajv: 8.20.0 + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 @@ -11289,6 +11738,13 @@ snapshots: json-schema-traverse: 0.4.1 uri-js: 4.4.1 + ajv@8.20.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.2 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + ansi-regex@5.0.1: {} ansi-styles@4.3.0: @@ -11424,12 +11880,28 @@ snapshots: baseline-browser-mapping@2.10.21: {} + bcryptjs@3.0.3: {} + big.js@6.2.2: {} blakejs@1.2.1: {} bn.js@5.2.3: {} + body-parser@2.2.2: + dependencies: + bytes: 3.1.2 + content-type: 1.0.5 + debug: 4.4.3 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + on-finished: 2.4.1 + qs: 6.15.1 + raw-body: 3.0.2 + type-is: 2.0.1 + transitivePeerDependencies: + - supports-color + borsh@0.7.0: dependencies: bn.js: 5.2.3 @@ -11480,6 +11952,8 @@ snapshots: dependencies: node-gyp-build: 4.8.4 + bytes@3.1.2: {} + cac@6.7.14: {} call-bind-apply-helpers@1.0.2: @@ -11544,6 +12018,8 @@ snapshots: clsx@2.1.1: {} + cluster-key-slot@1.1.2: {} + codama@1.6.0: dependencies: '@codama/cli': 1.5.1 @@ -11564,6 +12040,8 @@ snapshots: dependencies: delayed-stream: 1.0.0 + commander@11.1.0: {} + commander@14.0.2: {} commander@14.0.3: {} @@ -11572,14 +12050,27 @@ snapshots: concat-map@0.0.1: {} + content-disposition@1.1.0: {} + + content-type@1.0.5: {} + convert-source-map@2.0.0: {} cookie-es@1.2.3: {} + cookie-signature@1.2.2: {} + + cookie@0.7.2: {} + cookie@1.1.1: {} core-util-is@1.0.3: {} + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + crc-32@1.2.2: {} cross-fetch@3.2.0: @@ -11676,6 +12167,8 @@ snapshots: delayed-stream@1.0.0: {} + depd@2.0.0: {} + derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.2.14)(react@19.2.4)): dependencies: valtio: 1.13.2(@types/react@19.2.14)(react@19.2.4) @@ -11707,9 +12200,10 @@ snapshots: esbuild: 0.25.12 tsx: 4.21.0 - drizzle-orm@0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(postgres@3.4.9)(prisma@5.22.0): + drizzle-orm@0.45.2(@prisma/client@5.22.0(prisma@5.22.0))(@upstash/redis@1.38.0)(postgres@3.4.9)(prisma@5.22.0): optionalDependencies: '@prisma/client': 5.22.0(prisma@5.22.0) + '@upstash/redis': 1.38.0 postgres: 3.4.9 prisma: 5.22.0 @@ -11733,6 +12227,8 @@ snapshots: '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 + ee-first@1.1.1: {} + electron-to-chromium@1.5.344: {} emoji-regex@8.0.0: {} @@ -11741,6 +12237,8 @@ snapshots: encode-utf8@1.0.3: {} + encodeurl@2.0.0: {} + end-of-stream@1.4.5: dependencies: once: 1.4.0 @@ -11988,6 +12486,8 @@ snapshots: escalade@3.2.0: {} + escape-html@1.0.3: {} + escape-string-regexp@4.0.0: {} eslint-config-next@16.2.4(@typescript-eslint/parser@8.59.0(eslint@9.39.4)(typescript@5.9.3))(eslint@9.39.4)(typescript@5.9.3): @@ -12125,8 +12625,8 @@ snapshots: '@babel/parser': 7.29.2 eslint: 9.39.4 hermes-parser: 0.25.1 - zod: 4.3.6 - zod-validation-error: 4.0.2(zod@4.3.6) + zod: 3.25.76 + zod-validation-error: 4.0.2(zod@3.25.76) transitivePeerDependencies: - supports-color @@ -12224,6 +12724,8 @@ snapshots: esutils@2.0.3: {} + etag@1.8.1: {} + eth-block-tracker@7.1.0: dependencies: '@metamask/eth-json-rpc-provider': 1.0.1 @@ -12270,8 +12772,52 @@ snapshots: events@3.3.0: {} + eventsource-parser@3.0.8: {} + + eventsource@3.0.7: + dependencies: + eventsource-parser: 3.0.8 + expect-type@1.3.0: {} + express-rate-limit@8.5.1(express@5.2.1): + dependencies: + express: 5.2.1 + ip-address: 10.2.0 + + express@5.2.1: + dependencies: + accepts: 2.0.0 + body-parser: 2.2.2 + content-disposition: 1.1.0 + content-type: 1.0.5 + cookie: 0.7.2 + cookie-signature: 1.2.2 + debug: 4.4.3 + depd: 2.0.0 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + finalhandler: 2.1.1 + fresh: 2.0.0 + http-errors: 2.0.1 + merge-descriptors: 2.0.0 + mime-types: 3.0.2 + on-finished: 2.4.1 + once: 1.4.0 + parseurl: 1.3.3 + proxy-addr: 2.0.7 + qs: 6.15.1 + range-parser: 1.2.1 + router: 2.2.0 + send: 1.2.1 + serve-static: 2.2.1 + statuses: 2.0.2 + type-is: 2.0.1 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + extension-port-stream@3.0.0: dependencies: readable-stream: 4.7.0 @@ -12303,6 +12849,8 @@ snapshots: fast-stable-stringify@1.0.0: {} + fast-uri@3.1.2: {} + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -12323,6 +12871,17 @@ snapshots: filter-obj@1.1.0: {} + finalhandler@2.1.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + on-finished: 2.4.1 + parseurl: 1.3.3 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + find-up@4.1.0: dependencies: locate-path: 5.0.0 @@ -12354,6 +12913,10 @@ snapshots: hasown: 2.0.3 mime-types: 2.1.35 + forwarded@0.2.0: {} + + fresh@2.0.0: {} + fsevents@2.3.3: optional: true @@ -12372,6 +12935,8 @@ snapshots: generator-function@2.0.1: {} + generic-pool@3.9.0: {} + genlayer-js@1.1.7(bufferutil@4.1.0)(eslint@9.39.4)(typescript@5.9.3)(utf-8-validate@5.0.10)(zod@4.3.6): dependencies: eslint-plugin-import: 2.32.0(eslint@9.39.4) @@ -12515,6 +13080,14 @@ snapshots: hono@4.12.15: {} + http-errors@2.0.1: + dependencies: + depd: 2.0.0 + inherits: 2.0.4 + setprototypeof: 1.2.0 + statuses: 2.0.2 + toidentifier: 1.0.1 + humanize-ms@1.2.1: dependencies: ms: 2.1.3 @@ -12523,6 +13096,10 @@ snapshots: iceberg-js@0.8.1: {} + iconv-lite@0.7.2: + dependencies: + safer-buffer: 2.1.2 + idb-keyval@6.2.1: {} idb-keyval@6.2.2: {} @@ -12548,6 +13125,10 @@ snapshots: hasown: 2.0.3 side-channel: 1.1.0 + ip-address@10.2.0: {} + + ipaddr.js@1.9.1: {} + iron-webcrypto@1.2.1: {} is-arguments@1.2.0: @@ -12632,6 +13213,8 @@ snapshots: is-number@7.0.0: {} + is-promise@4.0.0: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -12778,6 +13361,10 @@ snapshots: json-schema-traverse@0.4.1: {} + json-schema-traverse@1.0.0: {} + + json-schema-typed@8.0.2: {} + json-stable-stringify-without-jsonify@1.0.1: {} json-stable-stringify@1.3.0: @@ -12886,12 +13473,25 @@ snapshots: math-intrinsics@1.1.0: {} + mcp-handler@1.1.0(@modelcontextprotocol/sdk@1.29.0(zod@3.25.76))(next@16.2.4(react-dom@19.2.4(react@19.2.4))(react@19.2.4)): + dependencies: + '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) + chalk: 5.6.2 + commander: 11.1.0 + redis: 4.7.1 + optionalDependencies: + next: 16.2.4(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + md5@2.3.0: dependencies: charenc: 0.0.2 crypt: 0.0.2 is-buffer: 1.1.6 + media-typer@1.1.0: {} + + merge-descriptors@2.0.0: {} + merge2@1.4.1: {} micro-ftch@0.3.1: {} @@ -12903,10 +13503,16 @@ snapshots: mime-db@1.52.0: {} + mime-db@1.54.0: {} + mime-types@2.1.35: dependencies: mime-db: 1.52.0 + mime-types@3.0.2: + dependencies: + mime-db: 1.54.0 + minimatch@10.2.5: dependencies: brace-expansion: 5.0.5 @@ -12933,6 +13539,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@1.0.0: {} + next@16.2.4(@babel/core@7.29.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@next/env': 16.2.4 @@ -13043,6 +13651,10 @@ snapshots: on-exit-leak-free@2.1.2: {} + on-finished@2.4.1: + dependencies: + ee-first: 1.1.1 + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -13210,12 +13822,16 @@ snapshots: dependencies: callsites: 3.1.0 + parseurl@1.3.3: {} + path-exists@4.0.0: {} path-key@3.1.1: {} path-parse@1.0.7: {} + path-to-regexp@8.4.2: {} + pathe@1.1.2: {} pathval@2.0.1: {} @@ -13293,11 +13909,13 @@ snapshots: sonic-boom: 2.8.0 thread-stream: 0.15.2 + pkce-challenge@5.0.1: {} + pngjs@5.0.0: {} pony-cause@2.1.11: {} - porto@0.2.35(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76)): + porto@0.2.35(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76)): dependencies: '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)) hono: 4.12.15 @@ -13311,7 +13929,7 @@ snapshots: '@tanstack/react-query': 5.100.6(react@19.2.4) react: 19.2.4 typescript: 5.9.3 - wagmi: 2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) transitivePeerDependencies: - '@types/react' - immer @@ -13367,6 +13985,11 @@ snapshots: object-assign: 4.1.1 react-is: 16.13.1 + proxy-addr@2.0.7: + dependencies: + forwarded: 0.2.0 + ipaddr.js: 1.9.1 + proxy-compare@2.6.0: {} proxy-compare@3.0.1: {} @@ -13393,6 +14016,10 @@ snapshots: pngjs: 5.0.0 yargs: 15.4.1 + qs@6.15.1: + dependencies: + side-channel: 1.1.0 + query-string@7.1.3: dependencies: decode-uri-component: 0.2.2 @@ -13406,6 +14033,15 @@ snapshots: radix3@1.1.2: {} + range-parser@1.2.1: {} + + raw-body@3.0.2: + dependencies: + bytes: 3.1.2 + http-errors: 2.0.1 + iconv-lite: 0.7.2 + unpipe: 1.0.0 + react-aria@3.48.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4): dependencies: '@internationalized/date': 3.12.1 @@ -13475,6 +14111,15 @@ snapshots: real-require@0.2.0: {} + redis@4.7.1: + dependencies: + '@redis/bloom': 1.2.0(@redis/client@1.6.1) + '@redis/client': 1.6.1 + '@redis/graph': 1.1.1(@redis/client@1.6.1) + '@redis/json': 1.0.7(@redis/client@1.6.1) + '@redis/search': 1.2.0(@redis/client@1.6.1) + '@redis/time-series': 1.1.0(@redis/client@1.6.1) + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.9 @@ -13497,6 +14142,8 @@ snapshots: require-directory@2.1.1: {} + require-from-string@2.0.2: {} + require-main-filename@2.0.0: {} resolve-from@4.0.0: {} @@ -13545,6 +14192,16 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.60.2 fsevents: 2.3.3 + router@2.2.0: + dependencies: + debug: 4.4.3 + depd: 2.0.0 + is-promise: 4.0.0 + parseurl: 1.3.3 + path-to-regexp: 8.4.2 + transitivePeerDependencies: + - supports-color + rpc-websockets@9.3.8: dependencies: '@swc/helpers': 0.5.21 @@ -13587,6 +14244,8 @@ snapshots: safe-stable-stringify@2.5.0: {} + safer-buffer@2.1.2: {} + scheduler@0.27.0: {} secure-json-parse@2.7.0: {} @@ -13599,6 +14258,31 @@ snapshots: semver@7.7.4: {} + send@1.2.1: + dependencies: + debug: 4.4.3 + encodeurl: 2.0.0 + escape-html: 1.0.3 + etag: 1.8.1 + fresh: 2.0.0 + http-errors: 2.0.1 + mime-types: 3.0.2 + ms: 2.1.3 + on-finished: 2.4.1 + range-parser: 1.2.1 + statuses: 2.0.2 + transitivePeerDependencies: + - supports-color + + serve-static@2.2.1: + dependencies: + encodeurl: 2.0.0 + escape-html: 1.0.3 + parseurl: 1.3.3 + send: 1.2.1 + transitivePeerDependencies: + - supports-color + set-blocking@2.0.0: {} set-cookie-parser@2.7.2: {} @@ -13625,6 +14309,8 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + setprototypeof@1.2.0: {} + sha.js@2.4.12: dependencies: inherits: 2.0.4 @@ -13755,6 +14441,8 @@ snapshots: stackback@0.0.2: {} + statuses@2.0.2: {} + std-env@3.10.0: {} stop-iteration-iterator@1.1.0: @@ -13915,6 +14603,8 @@ snapshots: dependencies: is-number: 7.0.0 + toidentifier@1.0.1: {} + toml@3.0.0: {} tr46@0.0.3: {} @@ -13947,6 +14637,12 @@ snapshots: dependencies: prelude-ls: 1.2.1 + type-is@2.0.1: + dependencies: + content-type: 1.0.5 + media-typer: 1.1.0 + mime-types: 3.0.2 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -14024,6 +14720,8 @@ snapshots: undici-types@8.2.0: {} + unpipe@1.0.0: {} + unrs-resolver@1.11.1: dependencies: napi-postinstall: 0.3.4 @@ -14048,7 +14746,7 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - unstorage@1.17.5(idb-keyval@6.2.2): + unstorage@1.17.5(@upstash/redis@1.38.0)(idb-keyval@6.2.2): dependencies: anymatch: 3.1.3 chokidar: 5.0.0 @@ -14059,6 +14757,7 @@ snapshots: ofetch: 1.5.1 ufo: 1.6.3 optionalDependencies: + '@upstash/redis': 1.38.0 idb-keyval: 6.2.2 update-browserslist-db@1.2.3(browserslist@4.28.2): @@ -14124,6 +14823,8 @@ snapshots: '@types/react': 19.2.14 react: 19.2.4 + vary@1.1.2: {} + viem@2.23.2(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76): dependencies: '@noble/curves': 1.8.1 @@ -14424,10 +15125,10 @@ snapshots: - supports-color - terser - wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6): + wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6): dependencies: '@tanstack/react-query': 5.100.6(react@19.2.4) - '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76))(zod@3.25.76) + '@wagmi/connectors': 6.2.0(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(@wagmi/core@2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)))(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(wagmi@2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@3.25.76))(zod@3.25.76) '@wagmi/core': 2.22.1(@tanstack/query-core@5.100.6)(@types/react@19.2.14)(react@19.2.4)(typescript@5.9.3)(use-sync-external-store@1.4.0(react@19.2.4))(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6)) react: 19.2.4 use-sync-external-store: 1.4.0(react@19.2.4) @@ -14580,7 +15281,7 @@ snapshots: bufferutil: 4.1.0 utf-8-validate: 6.0.6 - x402@0.7.3(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6): + x402@0.7.3(@solana/sysvars@6.9.0(typescript@5.9.3))(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6): dependencies: '@scure/base': 1.2.6 '@solana-program/compute-budget': 0.11.0(@solana/kit@5.5.1(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)) @@ -14593,7 +15294,7 @@ snapshots: '@wallet-standard/base': 1.1.0 '@wallet-standard/features': 1.1.0 viem: 2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@3.25.76) - wagmi: 2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) + wagmi: 2.19.5(@tanstack/query-core@5.100.6)(@tanstack/react-query@5.100.6(react@19.2.4))(@types/react@19.2.14)(@upstash/redis@1.38.0)(bufferutil@4.1.0)(react@19.2.4)(typescript@5.9.3)(utf-8-validate@6.0.6)(viem@2.47.12(bufferutil@4.1.0)(typescript@5.9.3)(utf-8-validate@6.0.6)(zod@4.3.6))(zod@4.3.6) zod: 3.25.76 transitivePeerDependencies: - '@azure/app-configuration' @@ -14641,6 +15342,8 @@ snapshots: yallist@3.1.1: {} + yallist@4.0.0: {} + yargs-parser@18.1.3: dependencies: camelcase: 5.3.1 @@ -14662,9 +15365,13 @@ snapshots: yocto-queue@0.1.0: {} - zod-validation-error@4.0.2(zod@4.3.6): + zod-to-json-schema@3.25.2(zod@3.25.76): dependencies: - zod: 4.3.6 + zod: 3.25.76 + + zod-validation-error@4.0.2(zod@3.25.76): + dependencies: + zod: 3.25.76 zod@3.22.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index e0a208f..c323ae2 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,3 +2,4 @@ packages: - "relayer" - "frontend" - "packages/*" + - "apps/*"