diff --git a/.gitignore b/.gitignore index 9dc1c8ff9a..7b103451ed 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,7 @@ proxy .env __pycache__/ **/.DS_Store +cache/ +out/ +lcov.info +broadcast/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000000..dafa1f3222 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "solidity/deferred-escrow/lib/openzeppelin-contracts"] + path = solidity/deferred-escrow/lib/openzeppelin-contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "solidity/deferred-escrow/lib/forge-std"] + path = solidity/deferred-escrow/lib/forge-std + url = https://github.com/foundry-rs/forge-std +[submodule "solidity/deferred-escrow/lib/safe-singleton-deployer-sol"] + path = solidity/deferred-escrow/lib/safe-singleton-deployer-sol + url = https://github.com/wilsoncusack/safe-singleton-deployer-sol diff --git a/examples/typescript/clients/axios/deferred.ts b/examples/typescript/clients/axios/deferred.ts new file mode 100644 index 0000000000..fa0d9bae71 --- /dev/null +++ b/examples/typescript/clients/axios/deferred.ts @@ -0,0 +1,67 @@ +import axios from "axios"; +import { config } from "dotenv"; +import { + withDeferredPaymentInterceptor, + decodeXPaymentResponse, + createSigner, + type Hex, +} from "x402-axios"; + +config(); + +const privateKey = process.env.PRIVATE_KEY as Hex | string; +const baseURL = process.env.RESOURCE_SERVER_URL as string; // e.g. https://example.com +const endpointPath = process.env.ENDPOINT_PATH as string; // e.g. /weather + +if (!baseURL || !privateKey || !endpointPath) { + console.error("Missing required environment variables"); + process.exit(1); +} + +/** + * This example shows how to use the x402-axios package to make a request to a resource server that requires a payment + * using deferred scheme. + * + * To run this example, you need to set the following environment variables: + * - PRIVATE_KEY: The private key of the signer (buyer) + * - RESOURCE_SERVER_URL: The URL of the resource server + * - ENDPOINT_PATH: The path of the endpoint to call on the resource server + * + */ +async function main(): Promise { + const signer = await createSigner("base-sepolia", privateKey); + const api = withDeferredPaymentInterceptor( + axios.create({ + baseURL, + }), + signer, + // [ + // { + // asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + // assetDomain: { + // name: "USDC", + // version: "2", + // }, + // threshold: "996000", + // amount: "100", + // }, + // ], + ); + + try { + const response = await api.get(endpointPath); + console.log(response.data); + + const xPaymentHeader = response.config.headers["X-PAYMENT"]; + const paymentPayload = JSON.parse(Buffer.from(xPaymentHeader, "base64").toString("utf-8")); + console.log("Deferred voucher details:"); + console.log(paymentPayload.payload.voucher); + + const paymentResponse = decodeXPaymentResponse(response.headers["x-payment-response"]); + console.log(paymentResponse); + } catch (error) { + console.error(error); + } +} + +main(); diff --git a/examples/typescript/clients/axios/package.json b/examples/typescript/clients/axios/package.json index 8d2f89bdb8..998bdaabd8 100644 --- a/examples/typescript/clients/axios/package.json +++ b/examples/typescript/clients/axios/package.json @@ -4,6 +4,7 @@ "type": "module", "scripts": { "dev": "tsx index.ts", + "dev:deferred": "tsx deferred.ts", "dev:multi-network-signer": "tsx multi-network-signer.ts", "format": "prettier -c .prettierrc --write \"**/*.{ts,js,cjs,json,md}\"", "format:check": "prettier -c .prettierrc --check \"**/*.{ts,js,cjs,json,md}\"", @@ -27,4 +28,4 @@ "tsx": "^4.7.0", "typescript": "^5.3.0" } -} +} \ No newline at end of file diff --git a/examples/typescript/clients/axios/tsconfig.json b/examples/typescript/clients/axios/tsconfig.json index 78f9479b1b..227beb61c4 100644 --- a/examples/typescript/clients/axios/tsconfig.json +++ b/examples/typescript/clients/axios/tsconfig.json @@ -9,7 +9,12 @@ "strict": true, "resolveJsonModule": true, "baseUrl": ".", - "types": ["node"] + "types": [ + "node" + ] }, - "include": ["index.ts"] -} + "include": [ + "index.ts", + "deferred.ts" + ] +} \ No newline at end of file diff --git a/examples/typescript/facilitator/.env-local b/examples/typescript/facilitator/.env-local index 63f7008a14..3f861917fa 100644 --- a/examples/typescript/facilitator/.env-local +++ b/examples/typescript/facilitator/.env-local @@ -1,3 +1,3 @@ EVM_PRIVATE_KEY=0xYourPrivateKey SVM_PRIVATE_KEY=base58EncodedSolanaPrivateKey -PORT=3002 \ No newline at end of file +PORT=3002 diff --git a/examples/typescript/facilitator/index.ts b/examples/typescript/facilitator/index.ts index 12bf4b59eb..20d15bdb0c 100644 --- a/examples/typescript/facilitator/index.ts +++ b/examples/typescript/facilitator/index.ts @@ -15,30 +15,45 @@ import { ConnectedClient, SupportedPaymentKind, isSvmSignerWallet, + evm, + X402RequestSchema, + DeferredEvmPayloadSchema, type X402Config, + DeferredEscrowFlushAuthorizationSignedSchema, } from "x402/types"; +import { deferred } from "x402/schemes"; +import { getNetworkName } from "x402/shared"; config(); const EVM_PRIVATE_KEY = process.env.EVM_PRIVATE_KEY || ""; const SVM_PRIVATE_KEY = process.env.SVM_PRIVATE_KEY || ""; const SVM_RPC_URL = process.env.SVM_RPC_URL || ""; +const PORT = process.env.PORT || 3000; if (!EVM_PRIVATE_KEY && !SVM_PRIVATE_KEY) { console.error("Missing required environment variables"); process.exit(1); } -// Create X402 config with custom RPC URL if provided -const x402Config: X402Config | undefined = SVM_RPC_URL - ? { svmConfig: { rpcUrl: SVM_RPC_URL } } - : undefined; - const app = express(); // Configure express to parse JSON bodies app.use(express.json()); +// Initialize the in-memory voucher store for deferred payments +const voucherStore = new deferred.evm.InMemoryVoucherStore(); + +// Create X402 config +const x402Config: X402Config = { + ...(SVM_RPC_URL ? { svmConfig: { rpcUrl: SVM_RPC_URL } } : {}), + schemeContext: { + deferred: { + voucherStore, + }, + }, +}; + type VerifyRequest = { paymentPayload: PaymentPayload; paymentRequirements: PaymentRequirements; @@ -49,6 +64,19 @@ type SettleRequest = { paymentRequirements: PaymentRequirements; }; +type FlushRequest = { + flushAuthorization: { + buyer: string; + seller?: string; + asset?: string; + nonce: string; + expiry: number; + signature: string; + }; + escrow: string; + chainId: number; +}; + app.get("/verify", (req: Request, res: Response) => { res.json({ endpoint: "/verify", @@ -100,13 +128,20 @@ app.get("/settle", (req: Request, res: Response) => { app.get("/supported", async (req: Request, res: Response) => { let kinds: SupportedPaymentKind[] = []; - // evm + // evm exact if (EVM_PRIVATE_KEY) { kinds.push({ x402Version: 1, scheme: "exact", network: "base-sepolia", }); + + // evm deferred + kinds.push({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + }); } // svm @@ -153,6 +188,204 @@ app.post("/settle", async (req: Request, res: Response) => { } }); -app.listen(process.env.PORT || 3000, () => { - console.log(`Server listening at http://localhost:${process.env.PORT || 3000}`); +// Deferred scheme endpoints + +// GET /deferred/vouchers/:id +app.get("/deferred/vouchers/:id", async (req: Request, res: Response) => { + try { + const { id } = req.params; + const vouchers = await voucherStore.getVoucherSeries(id, {}); + res.json(vouchers); + } catch (error) { + console.error("error", error); + res.status(400).json({ error: "Invalid request" }); + } +}); + +// GET /deferred/buyers/:buyer +app.get("/deferred/buyers/:buyer", async (req: Request, res: Response) => { + try { + const { buyer } = req.params; + const { seller, asset, escrow, chainId } = req.query; + + const network = getNetworkName(parseInt(chainId as string, 10)); + const client = evm.createConnectedClient(network); + const response = await deferred.evm.getAccountData( + client, + buyer as `0x${string}`, + seller as `0x${string}`, + asset as `0x${string}`, + escrow as `0x${string}`, + parseInt(chainId as string, 10), + voucherStore, + ); + + if ("error" in response) { + return res.status(404).json({ + error: response.error, + }); + } + + const voucher = await voucherStore.getAvailableVoucher(buyer, seller as `0x${string}`); + + res.json({ ...response, voucher: voucher ?? undefined }); + } catch (error) { + console.error("error", error); + res.status(400).json({ error: "Invalid request" }); + } +}); + +// POST /deferred/vouchers +app.post("/deferred/vouchers", async (req: Request, res: Response) => { + try { + const { paymentPayload, paymentRequirements } = X402RequestSchema.parse(req.body); + + // Verify the voucher + const client = evm.createConnectedClient(paymentPayload.network); + const verifyResponse = await verify(client, paymentPayload, paymentRequirements, x402Config); + + if (!verifyResponse.isValid) { + return res.status(400).json(verifyResponse); + } + + // Extract voucher + const { depositAuthorization, signature, voucher } = DeferredEvmPayloadSchema.parse( + paymentPayload.payload, + ); + const signedVoucher = { ...voucher, signature }; + + // Process deposit authorization + if (depositAuthorization) { + const network = getNetworkName(voucher.chainId); + const signer = evm.createSigner(network, EVM_PRIVATE_KEY as `0x${string}`); + const depositResponse = await deferred.evm.depositWithAuthorization( + signer, + signedVoucher, + depositAuthorization, + false, + ); // Skip reverification - already verified in verify() call above + if (!depositResponse.success) { + return res.status(400).json({ + error: depositResponse.errorReason ?? "Unknown deposit authorization error", + details: { depositAuthorization }, + }); + } + } + + // Store the voucher + const result = await voucherStore.storeVoucher(signedVoucher); + + if (!result.success) { + return res.status(400).json({ + error: result.error ?? "Unknown voucher storage error", + details: { voucher: signedVoucher }, + }); + } + + res.status(201).json(signedVoucher); + } catch (error) { + console.error("error", error); + res.status(400).json({ error: "Invalid request" }); + } +}); + +// POST /deferred/vouchers/:id/:nonce/settle +app.post("/deferred/vouchers/:id/:nonce/settle", async (req: Request, res: Response) => { + try { + const { id, nonce } = req.params; + const nonceNum = parseInt(nonce, 10); + + if (isNaN(nonceNum)) { + return res.status(400).json({ success: false, error: "Invalid nonce" }); + } + + const voucher = await voucherStore.getVoucher(id, nonceNum); + if (!voucher) { + return res.status(404).json({ success: false, error: "Voucher not found" }); + } + + // Get the signer for the voucher's network + const signer = evm.createSigner("base-sepolia", EVM_PRIVATE_KEY as `0x${string}`); + + // Extract signature and voucher data + const { signature, ...voucherData } = voucher; + + // Settle the voucher + const response = await deferred.evm.settleVoucher(signer, voucherData, signature, voucherStore); + + if (!response.success) { + return res.status(400).json({ + success: false, + error: response.errorReason || "Settlement failed", + }); + } + + res.json({ + success: true, + transactionHash: response.transaction, + network: response.network || "base-sepolia", + }); + } catch (error) { + console.error("error", error); + res.status(400).json({ success: false, error: `Settlement failed: ${error}` }); + } +}); + +// POST /deferred/buyers/:buyer/flush +app.post("/deferred/buyers/:buyer/flush", async (req: Request, res: Response) => { + try { + const body: FlushRequest = req.body; + + // Validate request body structure + if (!body.flushAuthorization || !body.escrow || !body.chainId) { + return res.status(400).json({ + success: false, + errorReason: "invalid_request_missing_fields", + transaction: "", + payer: "", + }); + } + + // Parse flush authorization + const flushAuthorization = DeferredEscrowFlushAuthorizationSignedSchema.parse( + body.flushAuthorization, + ); + + // Get the network from chainId + const network = getNetworkName(body.chainId); + + // Create a signer for the network + const signer = evm.createSigner(network, EVM_PRIVATE_KEY as `0x${string}`); + + // Call the flush function + const response = await deferred.evm.flushWithAuthorization( + signer, + flushAuthorization, + body.escrow as `0x${string}`, + ); + + if (!response.success) { + return res.status(400).json(response); + } + + res.json(response); + } catch (error) { + console.error("error", error); + res.status(400).json({ + success: false, + errorReason: "invalid_request", + transaction: "", + payer: "", + }); + } +}); + +app.listen(PORT, () => { + console.log(`Server listening at http://localhost:${PORT}`); + + console.log(`For deferred voucher history: GET http://localhost:${PORT}/deferred/vouchers/:id`); + console.log( + `To settle a deferred voucher: POST http://localhost:${PORT}/deferred/vouchers/:id/:nonce/settle`, + ); + console.log(`To flush escrow account: POST http://localhost:${PORT}/buyers/:buyer/flush`); }); diff --git a/examples/typescript/facilitator/package.json b/examples/typescript/facilitator/package.json index ad7e820081..f79e7749ec 100644 --- a/examples/typescript/facilitator/package.json +++ b/examples/typescript/facilitator/package.json @@ -17,12 +17,13 @@ }, "devDependencies": { "@eslint/js": "^9.24.0", - "eslint": "^9.24.0", - "eslint-plugin-jsdoc": "^50.6.9", - "eslint-plugin-prettier": "^5.2.6", + "@types/express": "^5.0.3", "@typescript-eslint/eslint-plugin": "^8.29.1", "@typescript-eslint/parser": "^8.29.1", + "eslint": "^9.24.0", "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsdoc": "^50.6.9", + "eslint-plugin-prettier": "^5.2.6", "prettier": "3.5.2", "tsx": "^4.7.0", "typescript": "^5.3.0" diff --git a/examples/typescript/fullstack/farcaster-miniapp/package.json b/examples/typescript/fullstack/farcaster-miniapp/package.json index e1302526f2..a56914b5f2 100644 --- a/examples/typescript/fullstack/farcaster-miniapp/package.json +++ b/examples/typescript/fullstack/farcaster-miniapp/package.json @@ -13,7 +13,7 @@ }, "dependencies": { "@coinbase/onchainkit": "latest", - "@coinbase/x402": "latest", + "@coinbase/x402": "workspace:*", "@farcaster/frame-sdk": "^0.0.60", "@tanstack/react-query": "^5", "@upstash/redis": "^1.34.4", @@ -42,4 +42,4 @@ "tailwindcss": "^3.4.1", "typescript": "^5" } -} +} \ No newline at end of file diff --git a/examples/typescript/fullstack/next-advanced/app/paywall/page.tsx b/examples/typescript/fullstack/next-advanced/app/paywall/page.tsx index 60bfffc622..ff7a709b37 100644 --- a/examples/typescript/fullstack/next-advanced/app/paywall/page.tsx +++ b/examples/typescript/fullstack/next-advanced/app/paywall/page.tsx @@ -3,7 +3,7 @@ import { Wallet } from "@coinbase/onchainkit/wallet"; import { useState } from "react"; import { verifyPayment } from "../actions"; -import { PaymentRequirements, PaymentPayload } from "x402/types"; +import { PaymentRequirements, PaymentPayload, UnsignedExactPaymentPayloadSchema, ExactPaymentRequirementsSchema } from "x402/types"; import { preparePaymentHeader } from "x402/client"; import { getNetworkId } from "x402/shared"; import { exact } from "x402/schemes"; @@ -27,11 +27,10 @@ function PaymentForm({ ); } - const unSignedPaymentHeader = preparePaymentHeader( - address, - 1, - paymentRequirements + const unSignedPaymentHeader = UnsignedExactPaymentPayloadSchema.parse( + preparePaymentHeader(address, 1, paymentRequirements) ); + paymentRequirements = ExactPaymentRequirementsSchema.parse(paymentRequirements); const eip712Data = { types: { diff --git a/examples/typescript/pnpm-lock.yaml b/examples/typescript/pnpm-lock.yaml index 6579798328..6c0820b206 100644 --- a/examples/typescript/pnpm-lock.yaml +++ b/examples/typescript/pnpm-lock.yaml @@ -245,6 +245,9 @@ importers: vitest: specifier: ^3.0.5 version: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.4)(yaml@2.8.1) + vitest-mock-extended: + specifier: ^3.1.0 + version: 3.1.0(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.4)(yaml@2.8.1)) ../../typescript/packages/x402-express: dependencies: @@ -518,16 +521,16 @@ importers: version: 0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) '@coinbase/agentkit-langchain': specifier: ^0.3.0 - version: 0.3.0(@coinbase/agentkit@0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10))(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + version: 0.3.0(@coinbase/agentkit@0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10))(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/core': specifier: ^0.3.43 - version: 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + version: 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) '@langchain/langgraph': specifier: ^0.2.62 - version: 0.2.74(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod-to-json-schema@3.24.6(zod@3.25.76)) + version: 0.2.74(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod-to-json-schema@3.24.6(zod@3.25.76)) '@langchain/openai': specifier: ^0.5.2 - version: 0.5.18(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.5.18(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) dotenv: specifier: ^16.4.7 version: 16.6.1 @@ -686,7 +689,7 @@ importers: version: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) x402: specifier: file:../../../../typescript/packages/x402 - version: file:../../typescript/packages/x402(@solana/sysvars@2.3.0(typescript@5.9.2))(@tanstack/react-query@5.85.5(react@19.1.1))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: file:../../typescript/packages/x402(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))(@tanstack/react-query@5.85.5(react@19.1.1))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) x402-axios: specifier: workspace:* version: link:../../../../typescript/packages/x402-axios @@ -872,6 +875,9 @@ importers: '@eslint/js': specifier: ^9.24.0 version: 9.33.0 + '@types/express': + specifier: ^5.0.3 + version: 5.0.3 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 version: 8.40.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2) @@ -956,10 +962,10 @@ importers: dependencies: '@coinbase/onchainkit': specifier: latest - version: 0.38.19(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + version: 1.1.1(@tanstack/query-core@5.85.5)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) '@coinbase/x402': - specifier: latest - version: 0.5.1(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10) + specifier: workspace:* + version: link:../../../../typescript/packages/coinbase-x402 '@farcaster/frame-sdk': specifier: ^0.0.60 version: 0.0.60(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -1187,7 +1193,7 @@ importers: dependencies: '@coinbase/onchainkit': specifier: latest - version: 0.38.19(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 1.1.1(@tanstack/query-core@5.85.5)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76) '@tanstack/react-query': specifier: ^5 version: 5.85.5(react@19.1.1) @@ -1205,7 +1211,7 @@ importers: version: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: specifier: ^2.15.6 - version: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) x402: specifier: workspace:* version: link:../../../../typescript/packages/x402 @@ -1404,12 +1410,18 @@ importers: servers/express: dependencies: + axios: + specifier: ^1.11.0 + version: 1.11.0 dotenv: specifier: ^16.4.7 version: 16.6.1 express: specifier: ^4.18.2 version: 4.21.2 + x402-axios: + specifier: workspace:* + version: link:../../../../typescript/packages/x402-axios x402-express: specifier: workspace:* version: link:../../../../typescript/packages/x402-express @@ -2214,6 +2226,14 @@ packages: react: ^18 || ^19 react-dom: ^18 || ^19 + '@coinbase/onchainkit@1.1.1': + resolution: {integrity: sha512-kQW852v5/SW0EXo2MY9uQCXrQLWmOOLObSUwAxsOLwL72GYfeg+GUzVKguydSwUEa08thBvdKFB/JvX4OA48Og==} + peerDependencies: + react: ^19 + react-dom: ^19 + viem: ^2.27 + wagmi: ^2.16 + '@coinbase/wallet-sdk@3.9.3': resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} @@ -2223,9 +2243,6 @@ packages: '@coinbase/x402@0.3.8': resolution: {integrity: sha512-qEjxQRefXCyTfxXf4LJtB5CQZAVqjVulbol5fjcYMrA6buA1ZCq7J+l2IJVVYfeJTv70s5atLEi35xxWNobLTQ==} - '@coinbase/x402@0.5.1': - resolution: {integrity: sha512-zKT0gFQ6LR8UKasVtUeAnJVHEdJUAHOicxd3VumR0rxKYWnRU/yaXVQ6iq5XZvtMAs+rfpyTUbKAgIyPfV9VxQ==} - '@colors/colors@1.6.0': resolution: {integrity: sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==} engines: {node: '>=0.1.90'} @@ -2732,12 +2749,27 @@ packages: '@floating-ui/dom@1.7.3': resolution: {integrity: sha512-uZA413QEpNuhtb3/iIKoYMSK07keHPYeXF02Zhd6e213j+d1NamLix/mCLxBUDW/Gx52sPH2m+chlUsyaBs/Ag==} + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + '@floating-ui/react-dom@2.1.5': resolution: {integrity: sha512-HDO/1/1oH9fjj4eLgegrlH3dklZpHtUYYFiVwMUwfGvk9jWDRWqkklA2/NFScknrcNSspbV868WjXORvreDX+Q==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.27.16': + resolution: {integrity: sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==} + peerDependencies: + react: '>=17.0.0' + react-dom: '>=17.0.0' + '@floating-ui/utils@0.2.10': resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} @@ -8878,9 +8910,15 @@ packages: resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} engines: {node: ^14.18.0 || >=16.0.0} + tabbable@6.2.0: + resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==} + tailwind-merge@2.6.0: resolution: {integrity: sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==} + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + tailwindcss@3.4.17: resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} engines: {node: '>=14.0.0'} @@ -9008,6 +9046,14 @@ packages: ts-case-convert@2.1.0: resolution: {integrity: sha512-Ye79el/pHYXfoew6kqhMwCoxp4NWjKNcm2kBzpmEMIU9dd9aBmHNNFtZ+WTm0rz1ngyDmfqDXDlyUnBXayiD0w==} + ts-essentials@10.1.1: + resolution: {integrity: sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==} + peerDependencies: + typescript: '>=4.5.0' + peerDependenciesMeta: + typescript: + optional: true + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -9339,6 +9385,12 @@ packages: peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + usehooks-ts@3.1.1: + resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + utf-8-validate@5.0.10: resolution: {integrity: sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==} engines: {node: '>=6.14.2'} @@ -9500,6 +9552,12 @@ packages: yaml: optional: true + vitest-mock-extended@3.1.0: + resolution: {integrity: sha512-vCM0VkuocOUBwwqwV7JB7YStw07pqeKvEIrZnR8l3PtwYi6rAAJAyJACeC1UYNfbQWi85nz7EdiXWBFI5hll2g==} + peerDependencies: + typescript: 3.x || 4.x || 5.x + vitest: '>=3.0.0' + vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -9719,9 +9777,6 @@ packages: x402@0.4.3: resolution: {integrity: sha512-GbIgX2EZYAOZX1ZrEYIVXtjDKLtNuwp7lUlM3dCJAF5TqELP+bDrLy3UsypzNcJoHPVvNa+jB4BPZ83/W2nZDA==} - x402@0.5.3: - resolution: {integrity: sha512-2cML/exWWnusJjmrzanILGAtSoatVR5U8w4Xf6RNpqpOO1xCakyEDgoSCKWAu+KAYoO27xmCXmbIy/ao2sBCMw==} - x402@file:../../typescript/packages/x402: resolution: {directory: ../../typescript/packages/x402, type: directory} @@ -10697,10 +10752,10 @@ snapshots: '@cfworker/json-schema@4.1.1': {} - '@coinbase/agentkit-langchain@0.3.0(@coinbase/agentkit@0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10))(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@coinbase/agentkit-langchain@0.3.0(@coinbase/agentkit@0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10))(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@coinbase/agentkit': 0.5.0(bufferutil@4.0.9)(encoding@0.1.13)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@langchain/core': 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) zod: 3.25.76 transitivePeerDependencies: - '@opentelemetry/api' @@ -10886,10 +10941,16 @@ snapshots: - utf-8-validate - zod - '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/onchainkit@1.1.1(@tanstack/query-core@5.85.5)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': dependencies: - '@farcaster/frame-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@floating-ui/react': 0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) '@tanstack/react-query': 5.85.5(react@19.1.1) '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) clsx: 2.1.1 @@ -10898,36 +10959,52 @@ snapshots: qrcode: 1.5.4 react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - tailwind-merge: 2.6.0 + tailwind-merge: 3.3.1 + usehooks-ts: 3.1.1(react@19.1.1) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + wagmi: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@farcaster/miniapp-sdk' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - '@tanstack/query-core' - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch + - '@types/react-dom' + - bufferutil + - encoding + - immer + - typescript + - use-sync-external-store + - utf-8-validate + - zod + + '@coinbase/onchainkit@1.1.1(@tanstack/query-core@5.85.5)(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76))(zod@3.25.76)': + dependencies: + '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@floating-ui/react': 0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-popover': 1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-tabs': 1.1.13(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-toast': 1.2.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@tanstack/react-query': 5.85.5(react@19.1.1) + '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + clsx: 2.1.1 + graphql: 16.11.0 + graphql-request: 6.1.0(encoding@0.1.13)(graphql@16.11.0) + qrcode: 1.5.4 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + tailwind-merge: 3.3.1 + usehooks-ts: 3.1.1(react@19.1.1) + viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + wagmi: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + transitivePeerDependencies: + - '@tanstack/query-core' + - '@types/react' + - '@types/react-dom' - bufferutil - - db0 - encoding - immer - - ioredis - - supports-color - typescript - - uploadthing - use-sync-external-store - utf-8-validate - zod @@ -11000,44 +11077,6 @@ snapshots: - typescript - utf-8-validate - '@coinbase/x402@0.5.1(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)': - dependencies: - '@coinbase/cdp-sdk': 1.36.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10) - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - x402: 0.5.3(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10) - zod: 3.25.76 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/query-core' - - '@tanstack/react-query' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - debug - - encoding - - fastestsmallesttextencoderdecoder - - immer - - ioredis - - react - - supports-color - - typescript - - uploadthing - - utf-8-validate - '@colors/colors@1.6.0': {} '@craftamap/esbuild-plugin-html@0.9.0(bufferutil@4.0.9)(esbuild@0.25.9)(utf-8-validate@5.0.10)': @@ -11516,16 +11555,16 @@ snapshots: - utf-8-validate - zod - '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': dependencies: '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@farcaster/quick-auth@0.0.5(typescript@5.9.2)': @@ -11549,12 +11588,37 @@ snapshots: '@floating-ui/core': 1.7.3 '@floating-ui/utils': 0.2.10 + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + '@floating-ui/react-dom@2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/dom': 1.7.3 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) + '@floating-ui/react-dom@2.1.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/dom': 1.7.3 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@floating-ui/react-dom@2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + + '@floating-ui/react@0.27.16(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@floating-ui/utils': 0.2.10 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + tabbable: 6.2.0 + '@floating-ui/utils@0.2.10': {} '@gar/promisify@1.1.3': {} @@ -11729,14 +11793,14 @@ snapshots: '@jup-ag/api@6.0.44': {} - '@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': + '@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))': dependencies: '@cfworker/json-schema': 4.1.1 ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.21 - langsmith: 0.3.62(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + langsmith: 0.3.62(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -11749,27 +11813,27 @@ snapshots: - '@opentelemetry/sdk-trace-base' - openai - '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': + '@langchain/langgraph-checkpoint@0.0.18(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))': dependencies: - '@langchain/core': 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) uuid: 10.0.0 - '@langchain/langgraph-sdk@0.0.109(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + '@langchain/langgraph-sdk@0.0.109(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': dependencies: '@types/json-schema': 7.0.15 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) react: 19.1.1 react-dom: 19.1.1(react@19.1.1) - '@langchain/langgraph@0.2.74(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod-to-json-schema@3.24.6(zod@3.25.76))': + '@langchain/langgraph@0.2.74(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(zod-to-json-schema@3.24.6(zod@3.25.76))': dependencies: - '@langchain/core': 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) - '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) - '@langchain/langgraph-sdk': 0.0.109(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@langchain/core': 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/langgraph-checkpoint': 0.0.18(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76))) + '@langchain/langgraph-sdk': 0.0.109(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(react-dom@19.1.1(react@19.1.1))(react@19.1.1) uuid: 10.0.0 zod: 3.25.76 optionalDependencies: @@ -11778,11 +11842,11 @@ snapshots: - react - react-dom - '@langchain/openai@0.5.18(@langchain/core@0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@langchain/openai@0.5.18(@langchain/core@0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@langchain/core': 0.3.72(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) + '@langchain/core': 0.3.72(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)) js-tiktoken: 1.0.21 - openai: 5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) zod: 3.25.76 transitivePeerDependencies: - ws @@ -11937,7 +12001,7 @@ snapshots: '@metamask/safe-event-emitter@3.1.2': {} - '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0(encoding@0.1.13))(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@metamask/sdk-communication-layer@0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: bufferutil: 4.0.9 cross-fetch: 4.1.0(encoding@0.1.13) @@ -11961,7 +12025,7 @@ snapshots: '@babel/runtime': 7.28.3 '@metamask/onboarding': 1.0.1 '@metamask/providers': 16.1.0 - '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0(encoding@0.1.13))(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@metamask/sdk-install-modal-web': 0.32.0 '@paulmillr/qr': 0.2.1 bowser: 2.12.0 @@ -12270,6 +12334,15 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-aspect-ratio@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12336,12 +12409,30 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.10)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-context-menu@2.2.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12362,6 +12453,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-context@1.1.2(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12384,12 +12481,40 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + aria-hidden: 1.2.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-direction@1.1.1(@types/react@19.1.10)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-direction@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12403,6 +12528,19 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12418,12 +12556,33 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.10)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) @@ -12435,6 +12594,17 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-form@0.1.8(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12477,6 +12647,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-id@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-label@2.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12512,6 +12689,32 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + aria-hidden: 1.2.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-menubar@1.1.16(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12611,6 +12814,29 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + aria-hidden: 1.2.6 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + react-remove-scroll: 2.7.1(@types/react@19.1.10)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@floating-ui/react-dom': 2.1.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12629,6 +12855,24 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@floating-ui/react-dom': 2.1.5(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/rect': 1.1.1 + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12639,6 +12883,16 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@18.3.1) @@ -12649,6 +12903,16 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@18.3.1) @@ -12658,7 +12922,16 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) - '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + + '@radix-ui/react-progress@1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@18.3.1) '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12703,6 +12976,23 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/number': 1.1.1 @@ -12784,6 +13074,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-slot@1.2.3(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12815,6 +13112,22 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-direction': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-id': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12835,6 +13148,26 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-toast@1.2.15(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-context': 1.1.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-toggle-group@1.1.11(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/primitive': 1.1.3 @@ -12902,6 +13235,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.10)(react@18.3.1)': dependencies: '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.10)(react@18.3.1) @@ -12910,6 +13249,14 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.1.10)(react@19.1.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.10)(react@18.3.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) @@ -12917,6 +13264,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.10)(react@18.3.1)': dependencies: '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@18.3.1) @@ -12924,6 +13278,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.1.10)(react@18.3.1)': dependencies: react: 18.3.1 @@ -12937,6 +13298,12 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-previous@1.1.1(@types/react@19.1.10)(react@18.3.1)': dependencies: react: 18.3.1 @@ -12950,6 +13317,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-rect@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.10)(react@18.3.1)': dependencies: '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@18.3.1) @@ -12957,6 +13331,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + '@radix-ui/react-use-size@1.1.1(@types/react@19.1.10)(react@19.1.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.10)(react@19.1.1) + react: 19.1.1 + optionalDependencies: + '@types/react': 19.1.10 + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': dependencies: '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -12966,6 +13347,15 @@ snapshots: '@types/react': 19.1.10 '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + react: 19.1.1 + react-dom: 19.1.1(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + '@types/react-dom': 19.1.7(@types/react@19.1.10) + '@radix-ui/rect@1.1.1': {} '@radix-ui/themes@3.2.1(@types/react-dom@19.1.7(@types/react@19.1.10))(@types/react@19.1.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': @@ -13036,45 +13426,11 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@reown/appkit-controllers@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(react@19.1.1) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -13139,41 +13495,6 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76) - lit: 3.3.0 - valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@reown/appkit-pay@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13249,42 +13570,6 @@ snapshots: - valtio - zod - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - lit: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - valtio - - zod - '@reown/appkit-scaffold-ui@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(react@19.1.1))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13355,40 +13640,6 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-ui@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - lit: 3.3.0 - qrcode: 1.5.3 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@reown/appkit-ui@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13460,43 +13711,6 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@reown/appkit-utils@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(react@19.1.1))(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13504,7 +13718,7 @@ snapshots: '@reown/appkit-polyfills': 1.7.8 '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) valtio: 1.13.2(react@19.1.1) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) transitivePeerDependencies: @@ -13587,48 +13801,6 @@ snapshots: - utf-8-validate - zod - '@reown/appkit@1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.10)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.10)(react@19.1.1) - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@reown/appkit@1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13640,7 +13812,7 @@ snapshots: '@reown/appkit-utils': 1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(react@19.1.1))(zod@3.25.76) '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3) - '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) bs58: 6.0.0 valtio: 1.13.2(react@19.1.1) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -13827,27 +13999,27 @@ snapshots: dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(typescript@5.9.2))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(typescript@5.9.2))(@solana/sysvars@2.3.0(typescript@5.9.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(typescript@5.9.2))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(typescript@5.9.2) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: @@ -14054,31 +14226,6 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(typescript@5.9.2)': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/nominal-types@2.3.0(typescript@5.9.2)': dependencies: typescript: 5.9.2 @@ -14394,23 +14541,6 @@ snapshots: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(typescript@5.9.2)': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 - transitivePeerDependencies: - - fastestsmallesttextencoderdecoder - - ws - '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': dependencies: '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) @@ -15046,58 +15176,15 @@ snapshots: loupe: 3.2.0 tinyrainbow: 2.0.0 - '@wagmi/connectors@5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': - dependencies: - '@base-org/account': 1.1.1(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.2.0(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - immer - - ioredis - - react - - supports-color - - uploadthing - - use-sync-external-store - - utf-8-validate - - zod - - '@wagmi/connectors@5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': dependencies: - '@base-org/account': 1.1.1(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@base-org/account': 1.1.1(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) '@gemini-wallet/core': 0.2.0(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -15132,16 +15219,16 @@ snapshots: - utf-8-validate - zod - '@wagmi/connectors@5.9.4(@types/react@19.1.10)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': dependencies: - '@base-org/account': 1.1.1(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@base-org/account': 1.1.1(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.10)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) '@gemini-wallet/core': 0.2.0(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(encoding@0.1.13)(utf-8-validate@5.0.10) '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) optionalDependencies: @@ -15378,46 +15465,6 @@ snapshots: - utf-8-validate - zod - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@reown/appkit': 1.7.8(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3) - '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3) - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - react - - typescript - - uploadthing - - utf-8-validate - - zod - '@walletconnect/ethereum-provider@2.21.1(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@reown/appkit': 1.7.8(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) @@ -15428,7 +15475,7 @@ snapshots: '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3) '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3) - '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) events: 3.3.0 transitivePeerDependencies: @@ -15719,45 +15766,6 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3) - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.0(@upstash/redis@1.35.3) - '@walletconnect/utils': 2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - '@walletconnect/universal-provider@2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@walletconnect/events': 1.0.1 @@ -15797,45 +15805,6 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(encoding@0.1.13)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@walletconnect/events': 1.0.1 - '@walletconnect/jsonrpc-http-connection': 1.0.8(encoding@0.1.13) - '@walletconnect/jsonrpc-provider': 1.0.14 - '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/jsonrpc-utils': 1.0.8 - '@walletconnect/keyvaluestorage': 1.1.1(@upstash/redis@1.35.3) - '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.1(@upstash/redis@1.35.3) - '@walletconnect/utils': 2.21.1(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - es-toolkit: 1.33.0 - events: 3.3.0 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - ioredis - - typescript - - uploadthing - - utf-8-validate - - zod - '@walletconnect/utils@2.21.0(@upstash/redis@1.35.3)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': dependencies: '@noble/ciphers': 1.2.1 @@ -17344,7 +17313,7 @@ snapshots: '@typescript-eslint/parser': 8.40.0(eslint@8.57.1)(typescript@5.9.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) @@ -17373,44 +17342,44 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7)))(eslint@9.33.0(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 - eslint: 8.57.1 + eslint: 9.33.0(jiti@1.21.7) get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.33.0(jiti@1.21.7)))(eslint@9.33.0(jiti@1.21.7)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 - eslint: 9.33.0(jiti@1.21.7) + eslint: 8.57.1 get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.14 unrs-resolver: 1.11.1 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@9.33.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.33.0(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: '@typescript-eslint/parser': 8.40.0(eslint@8.57.1)(typescript@5.9.2) eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color @@ -17436,7 +17405,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint@8.57.1))(eslint@8.57.1))(eslint@8.57.1) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.40.0(eslint@8.57.1)(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -18769,7 +18738,7 @@ snapshots: kuler@2.0.0: {} - langsmith@0.3.62(openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): + langsmith@0.3.62(openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76)): dependencies: '@types/uuid': 10.0.0 chalk: 4.1.2 @@ -18779,7 +18748,7 @@ snapshots: semver: 7.7.2 uuid: 10.0.0 optionalDependencies: - openai: 5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) + openai: 5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76) language-subtag-registry@0.3.23: {} @@ -19274,9 +19243,9 @@ snapshots: transitivePeerDependencies: - encoding - openai@5.13.1(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): + openai@5.13.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.25.76): optionalDependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) zod: 3.25.76 opensea-js@7.2.1(bufferutil@4.0.9)(utf-8-validate@5.0.10): @@ -19826,6 +19795,14 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + react-remove-scroll-bar@2.3.8(@types/react@19.1.10)(react@19.1.1): + dependencies: + react: 19.1.1 + react-style-singleton: 2.2.3(@types/react@19.1.10)(react@19.1.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.10 + react-remove-scroll@2.7.1(@types/react@19.1.10)(react@18.3.1): dependencies: react: 18.3.1 @@ -19837,6 +19814,17 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + react-remove-scroll@2.7.1(@types/react@19.1.10)(react@19.1.1): + dependencies: + react: 19.1.1 + react-remove-scroll-bar: 2.3.8(@types/react@19.1.10)(react@19.1.1) + react-style-singleton: 2.2.3(@types/react@19.1.10)(react@19.1.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.1.10)(react@19.1.1) + use-sidecar: 1.1.3(@types/react@19.1.10)(react@19.1.1) + optionalDependencies: + '@types/react': 19.1.10 + react-style-singleton@2.2.3(@types/react@19.1.10)(react@18.3.1): dependencies: get-nonce: 1.0.1 @@ -19845,6 +19833,14 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + react-style-singleton@2.2.3(@types/react@19.1.10)(react@19.1.1): + dependencies: + get-nonce: 1.0.1 + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.10 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -20571,8 +20567,12 @@ snapshots: dependencies: '@pkgr/core': 0.2.9 + tabbable@6.2.0: {} + tailwind-merge@2.6.0: {} + tailwind-merge@3.3.1: {} + tailwindcss@3.4.17: dependencies: '@alloc/quick-lru': 5.2.0 @@ -20715,6 +20715,10 @@ snapshots: ts-case-convert@2.1.0: {} + ts-essentials@10.1.1(typescript@5.9.2): + optionalDependencies: + typescript: 5.9.2 + ts-interface-checker@0.1.13: {} tsconfck@3.1.6(typescript@5.9.2): @@ -20994,6 +20998,13 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + use-callback-ref@1.3.3(@types/react@19.1.10)(react@19.1.1): + dependencies: + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.10 + use-sidecar@1.1.3(@types/react@19.1.10)(react@18.3.1): dependencies: detect-node-es: 1.1.0 @@ -21002,6 +21013,14 @@ snapshots: optionalDependencies: '@types/react': 19.1.10 + use-sidecar@1.1.3(@types/react@19.1.10)(react@19.1.1): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.1.10 + use-sync-external-store@1.2.0(react@19.1.1): dependencies: react: 19.1.1 @@ -21019,6 +21038,11 @@ snapshots: react: 19.1.1 optional: true + usehooks-ts@3.1.1(react@19.1.1): + dependencies: + lodash.debounce: 4.0.8 + react: 19.1.1 + utf-8-validate@5.0.10: dependencies: node-gyp-build: 4.8.4 @@ -21184,6 +21208,12 @@ snapshots: tsx: 4.20.4 yaml: 2.8.1 + vitest-mock-extended@3.1.0(typescript@5.9.2)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.4)(yaml@2.8.1)): + dependencies: + ts-essentials: 10.1.1(typescript@5.9.2) + typescript: 5.9.2 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.4)(yaml@2.8.1) + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.17.2)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.4)(yaml@2.8.1): dependencies: '@types/chai': 5.2.2 @@ -21234,45 +21264,7 @@ snapshots: wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): dependencies: '@tanstack/react-query': 5.85.5(react@19.1.1) - '@wagmi/connectors': 5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.5.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) - '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - react: 19.1.1 - use-sync-external-store: 1.4.0(react@19.1.1) - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/query-core' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - immer - - ioredis - - supports-color - - uploadthing - - utf-8-validate - - zod - - wagmi@2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): - dependencies: - '@tanstack/react-query': 5.85.5(react@19.1.1) - '@wagmi/connectors': 5.9.4(@types/react@19.1.10)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + '@wagmi/connectors': 5.9.4(@types/react@19.1.10)(@upstash/redis@1.35.3)(@wagmi/core@2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) '@wagmi/core': 2.19.0(@tanstack/query-core@5.85.5)(@types/react@19.1.10)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) react: 19.1.1 use-sync-external-store: 1.4.0(react@19.1.1) @@ -21645,49 +21637,14 @@ snapshots: - uploadthing - utf-8-validate - x402@0.5.3(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10): - dependencies: - viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.16.4(@tanstack/query-core@5.85.5)(@tanstack/react-query@5.85.5(react@19.1.1))(@types/react@19.1.10)(@upstash/redis@1.35.3)(bufferutil@4.0.9)(encoding@0.1.13)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - '@azure/app-configuration' - - '@azure/cosmos' - - '@azure/data-tables' - - '@azure/identity' - - '@azure/keyvault-secrets' - - '@azure/storage-blob' - - '@capacitor/preferences' - - '@deno/kv' - - '@netlify/blobs' - - '@planetscale/database' - - '@react-native-async-storage/async-storage' - - '@tanstack/query-core' - - '@tanstack/react-query' - - '@types/react' - - '@upstash/redis' - - '@vercel/blob' - - '@vercel/kv' - - aws4fetch - - bufferutil - - db0 - - encoding - - immer - - ioredis - - react - - supports-color - - typescript - - uploadthing - - utf-8-validate - - x402@file:../../typescript/packages/x402(@solana/sysvars@2.3.0(typescript@5.9.2))(@tanstack/react-query@5.85.5(react@19.1.1))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10): + x402@file:../../typescript/packages/x402(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))(@tanstack/react-query@5.85.5(react@19.1.1))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: '@scure/base': 1.2.6 - '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(typescript@5.9.2)) - '@solana-program/token': 0.5.1(@solana/kit@2.3.0(typescript@5.9.2)) - '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(typescript@5.9.2))(@solana/sysvars@2.3.0(typescript@5.9.2)) - '@solana/kit': 2.3.0(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(typescript@5.9.2) + '@solana-program/compute-budget': 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token': 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + '@solana-program/token-2022': 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) viem: 2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) wagmi: 2.16.4(@tanstack/react-query@5.85.5(react@19.1.1))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.34.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) zod: 3.25.76 diff --git a/examples/typescript/servers/express/.env-local b/examples/typescript/servers/express/.env-local index 9e299111bc..5180243d22 100644 --- a/examples/typescript/servers/express/.env-local +++ b/examples/typescript/servers/express/.env-local @@ -1,6 +1,7 @@ FACILITATOR_URL=https://x402.org/facilitator NETWORK=base-sepolia ADDRESS= +DEFERRED_ESCROW=0x6f2abbc75b52bec6ca48959baaf8949b9a6acc9e # required if using the Base mainnet facilitator CDP_API_KEY_ID="Coinbase Developer Platform Key" diff --git a/examples/typescript/servers/express/client.ts b/examples/typescript/servers/express/client.ts new file mode 100644 index 0000000000..522fdae86a --- /dev/null +++ b/examples/typescript/servers/express/client.ts @@ -0,0 +1,25 @@ +import { createWalletClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { withDeferredPaymentInterceptor } from "x402-axios"; +import axios from "axios"; +import { baseSepolia } from "viem/chains"; + +// Create a wallet client +const account = privateKeyToAccount( + "0x7492e7f8c59dd58f73a500c7d38b763cc525273cfb195fdc5862e495b257b41a", +); +const client = createWalletClient({ + account, + transport: http(), + chain: baseSepolia, +}); + +// Create an Axios instance with payment handling +const api = withDeferredPaymentInterceptor( + axios.create({ baseURL: "http://localhost:3002" }), + client, +); + +// Make a request that may require payment +const response = await api.get("/premium-joke"); +console.log(response.data); diff --git a/examples/typescript/servers/express/index.ts b/examples/typescript/servers/express/index.ts index 37e35d27ce..93c2b22d77 100644 --- a/examples/typescript/servers/express/index.ts +++ b/examples/typescript/servers/express/index.ts @@ -1,10 +1,16 @@ import { config } from "dotenv"; import express from "express"; -import { paymentMiddleware, Resource, type SolanaAddress } from "x402-express"; +import { + paymentMiddleware, + deferredPaymentMiddleware, + Resource, + type SolanaAddress, +} from "x402-express"; config(); const facilitatorUrl = process.env.FACILITATOR_URL as Resource; const payTo = process.env.ADDRESS as `0x${string}` | SolanaAddress; +const deferredEscrow = process.env.DEFERRED_ESCROW as `0x${string}`; if (!facilitatorUrl || !payTo) { console.error("Missing required environment variables"); @@ -13,6 +19,7 @@ if (!facilitatorUrl || !payTo) { const app = express(); +// Exact scheme content app.use( paymentMiddleware( payTo, @@ -49,6 +56,25 @@ app.use( ), ); +// Deferred scheme content +if (deferredEscrow) { + app.use( + deferredPaymentMiddleware( + payTo as `0x${string}`, + { + "/deferred/*": { + price: "$0.001", + network: "base-sepolia", + }, + }, + deferredEscrow, + { + url: facilitatorUrl, + }, + ), + ); +} + app.get("/weather", (req, res) => { res.send({ report: { @@ -64,6 +90,20 @@ app.get("/premium/content", (req, res) => { }); }); +app.get("/free", (req, res) => { + res.send({ + content: "This is free content", + }); +}); + +if (deferredEscrow) { + app.get("/deferred/content", (req, res) => { + res.send({ + content: "This is premium content via deferred scheme", + }); + }); +} + app.listen(4021, () => { console.log(`Server listening at http://localhost:${4021}`); }); diff --git a/examples/typescript/servers/express/package.json b/examples/typescript/servers/express/package.json index 0e4cd41af8..0feb65aee5 100644 --- a/examples/typescript/servers/express/package.json +++ b/examples/typescript/servers/express/package.json @@ -10,9 +10,11 @@ "lint:check": "eslint . --ext .ts" }, "dependencies": { + "axios": "^1.11.0", "dotenv": "^16.4.7", "express": "^4.18.2", - "x402-express": "workspace:*" + "x402-express": "workspace:*", + "x402-axios": "workspace:*" }, "devDependencies": { "@eslint/js": "^9.24.0", diff --git a/solidity/deferred-escrow/README.md b/solidity/deferred-escrow/README.md new file mode 100644 index 0000000000..3d9d67abf3 --- /dev/null +++ b/solidity/deferred-escrow/README.md @@ -0,0 +1,68 @@ +## DeferredPaymentEscrow + +Smart contracts implementing escrowing and vouchers mechanisms needed for the `deferred` x402 payment scheme. The main contract is `DeferredPaymentEscrow.sol`. + +## Usage + +### Build + +```shell +$ forge build +``` + +### Test + +```shell +$ forge test +``` + +### Format + +```shell +$ forge fmt +``` + +### Gas Snapshots + +```shell +$ forge snapshot +``` + +### Anvil + +```shell +$ anvil +``` + +### Deploy + +Deploy the DeferredPaymentEscrow contract with UUPS proxy: + +```shell +$ forge script script/DeployDeferredPaymentEscrow.s.sol --rpc-url --private-key --broadcast +``` + +For testnet/mainnet deployment with verification: + +```shell +$ forge script script/DeployDeferredPaymentEscrow.s.sol \ + --rpc-url \ + --private-key \ + --broadcast \ + --verify \ + --etherscan-api-key +``` + +### Cast + +```shell +$ cast +``` + +### Help + +```shell +$ forge --help +$ anvil --help +$ cast --help +``` diff --git a/solidity/deferred-escrow/addresses.json b/solidity/deferred-escrow/addresses.json new file mode 100644 index 0000000000..ddf6c598c0 --- /dev/null +++ b/solidity/deferred-escrow/addresses.json @@ -0,0 +1,5 @@ +{ + "84532": { + "deferredPaymentEscrow": "0x6f2abbc75b52bec6ca48959baaf8949b9a6acc9e" + } +} \ No newline at end of file diff --git a/solidity/deferred-escrow/foundry.toml b/solidity/deferred-escrow/foundry.toml new file mode 100644 index 0000000000..eddafc4796 --- /dev/null +++ b/solidity/deferred-escrow/foundry.toml @@ -0,0 +1,15 @@ +[profile.default] +src = "src" +out = "out" +libs = ["lib"] +remappings = [ + "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/", + "forge-std/=lib/forge-std/src/", + "safe-singleton-deployer/=lib/safe-singleton-deployer-sol/src/" +] +fs_permissions = [{ access = "read", path = "./addresses.json" }] +optimizer = true +optimizer_runs = 200 +via_ir = true + +# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options diff --git a/solidity/deferred-escrow/lib/forge-std b/solidity/deferred-escrow/lib/forge-std new file mode 160000 index 0000000000..8bbcf6e3f8 --- /dev/null +++ b/solidity/deferred-escrow/lib/forge-std @@ -0,0 +1 @@ +Subproject commit 8bbcf6e3f8f62f419e5429a0bd89331c85c37824 diff --git a/solidity/deferred-escrow/lib/openzeppelin-contracts b/solidity/deferred-escrow/lib/openzeppelin-contracts new file mode 160000 index 0000000000..c64a1edb67 --- /dev/null +++ b/solidity/deferred-escrow/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit c64a1edb67b6e3f4a15cca8909c9482ad33a02b0 diff --git a/solidity/deferred-escrow/lib/safe-singleton-deployer-sol b/solidity/deferred-escrow/lib/safe-singleton-deployer-sol new file mode 160000 index 0000000000..cf2b89c33f --- /dev/null +++ b/solidity/deferred-escrow/lib/safe-singleton-deployer-sol @@ -0,0 +1 @@ +Subproject commit cf2b89c33fed536c4dd6fef2fb84f39053068868 diff --git a/solidity/deferred-escrow/script/CalculateStorageSlot.s.sol b/solidity/deferred-escrow/script/CalculateStorageSlot.s.sol new file mode 100644 index 0000000000..a6e1a7240e --- /dev/null +++ b/solidity/deferred-escrow/script/CalculateStorageSlot.s.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {Script, console} from "forge-std/Script.sol"; +import {SlotDerivation} from "@openzeppelin/contracts/utils/SlotDerivation.sol"; + +contract CalculateStorageSlot is Script { + using SlotDerivation for string; + + function run() public { + string memory namespace = "deferred.payment.escrow.main"; + bytes32 slot = namespace.erc7201Slot(); + console.log("Namespace:", namespace); + console.log("Storage slot:"); + console.logBytes32(slot); + } +} diff --git a/solidity/deferred-escrow/script/Deploy.s.sol b/solidity/deferred-escrow/script/Deploy.s.sol new file mode 100644 index 0000000000..a1765b09d7 --- /dev/null +++ b/solidity/deferred-escrow/script/Deploy.s.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {Script, console} from "forge-std/Script.sol"; +import {SafeSingletonDeployer} from "safe-singleton-deployer/SafeSingletonDeployer.sol"; +import {DeferredPaymentEscrow} from "../src/DeferredPaymentEscrow.sol"; + +contract Deploy is Script { + // Use meaningful salt for deterministic addresses across chains + bytes32 constant ESCROW_SALT = keccak256("DeferredPaymentEscrow.v1"); + + function run() external returns (address escrow) { + // Default deployment parameters + uint256 thawingPeriod = 1 days; // 86400 seconds + + console.log("=== Deploying DeferredPaymentEscrow ==="); + console.log("Deployer:", msg.sender); + console.log("Thawing Period:", thawingPeriod); + console.log(""); + + vm.startBroadcast(); + + // Deploy DeferredPaymentEscrow using Safe Singleton Factory + console.log("Deploying DeferredPaymentEscrow..."); + escrow = _deploySingleton( + type(DeferredPaymentEscrow).creationCode, abi.encode(thawingPeriod), ESCROW_SALT, "DeferredPaymentEscrow" + ); + + vm.stopBroadcast(); + + // Verify deployment + DeferredPaymentEscrow escrowContract = DeferredPaymentEscrow(escrow); + require(escrowContract.THAWING_PERIOD() == thawingPeriod, "Deployment failed"); + + // Log deployment summary + console.log(""); + console.log("=== Deployment Summary ==="); + console.log("DeferredPaymentEscrow:", escrow); + console.log(""); + console.log("This address will be consistent across all chains!"); + console.log("========================="); + } + + function _deploySingleton( + bytes memory creationCode, + bytes memory constructorArgs, + bytes32 salt, + string memory contractName + ) internal returns (address deployed) { + // Use SafeSingletonDeployer.deploy (not broadcastDeploy) since we're already broadcasting + deployed = SafeSingletonDeployer.deploy(creationCode, constructorArgs, salt); + + console.log(string.concat(contractName, " deployed at:"), deployed); + + // Verify deployment + require(deployed.code.length > 0, string.concat(contractName, " deployment failed")); + } + + // Helper function to predict addresses without deploying + function predict() external pure { + console.log("=== Predicted Addresses ==="); + + // Predict DeferredPaymentEscrow address + address predictedEscrow = SafeSingletonDeployer.computeAddress( + type(DeferredPaymentEscrow).creationCode, abi.encode(1 days), ESCROW_SALT + ); + console.log("DeferredPaymentEscrow:", predictedEscrow); + + console.log("==========================="); + } +} diff --git a/solidity/deferred-escrow/script/DepositToEscrow.s.sol b/solidity/deferred-escrow/script/DepositToEscrow.s.sol new file mode 100644 index 0000000000..2efb25a6c0 --- /dev/null +++ b/solidity/deferred-escrow/script/DepositToEscrow.s.sol @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {Script, console} from "forge-std/Script.sol"; +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; + +contract DepositToEscrow is Script { + address constant DEFAULT_TOKEN = 0x036CbD53842c5426634e7929541eC2318f3dCF7e; // USDC + uint256 constant DEFAULT_AMOUNT = 10_000; // 0.01 USDC + + function run() external { + // Get escrow address based on chain ID + address escrowAddress = getEscrowAddress(); + require(escrowAddress != address(0), "Escrow address not found for this chain"); + + // Get parameters from environment variables or use defaults + address seller = vm.envOr("SELLER", address(0)); + address token = vm.envOr("TOKEN", DEFAULT_TOKEN); + uint256 amount = vm.envOr("AMOUNT", DEFAULT_AMOUNT); + + // Validate inputs + require(seller != address(0), "Seller address must be provided via SELLER env var"); + require(token != address(0), "Non-zero token address must be provided via TOKEN env var"); + require(amount > 0, "Amount must be provided via AMOUNT env var"); + + // Log deposit parameters + console.log("Depositing to DeferredPaymentEscrow..."); + console.log(" Escrow:", escrowAddress); + console.log(" Seller:", seller); + console.log(" Token:", token); + console.log(" Amount:", amount); + + // Start broadcasting transactions + vm.startBroadcast(); + + // Get token contract + IERC20 tokenContract = IERC20(token); + + // Check current allowance + uint256 currentAllowance = tokenContract.allowance(msg.sender, escrowAddress); + console.log(" Current allowance:", currentAllowance); + + // Approve if needed + if (currentAllowance < amount) { + console.log(" Approving escrow to spend tokens..."); + tokenContract.approve(escrowAddress, amount); + } + + // Get escrow contract + IDeferredPaymentEscrow escrow = IDeferredPaymentEscrow(escrowAddress); + + // Check balance before deposit + IDeferredPaymentEscrow.EscrowAccount memory accountBefore = escrow.getAccount(msg.sender, seller, token); + console.log(" Balance before:", accountBefore.balance); + + // Deposit tokens + escrow.deposit(seller, token, amount); + + // Check balance after deposit + IDeferredPaymentEscrow.EscrowAccount memory accountAfter = escrow.getAccount(msg.sender, seller, token); + console.log(" Balance after:", accountAfter.balance); + + vm.stopBroadcast(); + + console.log("\n=== Deposit Summary ==="); + console.log("Deposited:", amount); + console.log("From buyer:", msg.sender); + console.log("To seller:", seller); + console.log("Token:", token); + console.log("New balance:", accountAfter.balance); + console.log("======================"); + } + + function getEscrowAddress() internal view returns (address) { + uint256 chainId = block.chainid; + + // Read addresses from JSON file + string memory root = vm.projectRoot(); + string memory path = string.concat(root, "/addresses.json"); + string memory json = vm.readFile(path); + + // Parse JSON to get escrow address for current chain + string memory key = string.concat(".", vm.toString(chainId), ".deferredPaymentEscrow"); + address escrowAddress = vm.parseJsonAddress(json, key); + + return escrowAddress; + } +} diff --git a/solidity/deferred-escrow/script/README.md b/solidity/deferred-escrow/script/README.md new file mode 100644 index 0000000000..c33f0e7398 --- /dev/null +++ b/solidity/deferred-escrow/script/README.md @@ -0,0 +1,104 @@ +# Deployment Scripts + +This directory contains deployment scripts for the DeferredPaymentEscrow contract. + +## Scripts + +### Deploy.s.sol +Main deployment script that deploys the DeferredPaymentEscrow contract using Safe Singleton Factory for deterministic addresses across all chains. + +**Default Parameters:** +- Thawing Period: 1 day (immutable, cannot be changed post-deployment) + +**Key Features:** +- No proxy pattern - direct contract deployment +- Deterministic addresses across all chains using Safe Singleton Factory +- Fully permissionless (no owner functionality) +- Immutable thawing period set in constructor + +### CalculateStorageSlot.s.sol +Utility script to calculate ERC-7201 storage slots for namespaced storage. + +### DepositToEscrow.s.sol +Script to deposit ERC20 tokens into the DeferredPaymentEscrow contract for specific sellers. + +### verify.sh +Bash script to verify the deployed contract on Etherscan/Basescan. Follows the same pattern as account-modules for consistency. + +## Usage + +### Local Deployment (Anvil) +```bash +# Start local node +anvil + +# Deploy to local network +forge script script/Deploy.s.sol --rpc-url http://localhost:8545 --broadcast --private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +``` + +### Predict Contract Address +```bash +# Get the deterministic address before deployment +forge script script/Deploy.s.sol --sig "predict()" --rpc-url +``` + +### Testnet/Mainnet Deployment +```bash +# Deploy (verification done separately) +forge script script/Deploy.s.sol \ + --rpc-url \ + --private-key \ + --broadcast + +# Verify separately using verify.sh script +./script/verify.sh base-sepolia +``` + +### Depositing Funds + +To deposit tokens for a seller: +```bash +SELLER= TOKEN= AMOUNT= \ +forge script script/DepositToEscrow.s.sol \ + --rpc-url \ + --private-key \ + --broadcast +``` + +Example: +```bash +# Deposit 100 USDC (6 decimals) to a seller +SELLER=0x1234...5678 TOKEN=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 AMOUNT=100000000 \ +forge script script/DepositToEscrow.s.sol \ + --rpc-url https://mainnet.infura.io/v3/YOUR_KEY \ + --private-key $PRIVATE_KEY \ + --broadcast +``` + +## Output + +The deployment script will output: +- Single contract address (no proxy/implementation split) +- Note that the address will be consistent across all chains due to deterministic deployment + +The deposit script will output: +- Escrow address being used +- Seller, token, and amount details +- Balance before and after deposit +- Transaction summary + +## Important Notes + +1. **Deterministic Deployment**: The DeferredPaymentEscrow contract uses Safe Singleton Factory, ensuring the same address across all chains regardless of deployer. Use the `predict()` function to get the address before deployment. + +2. **Immutable Configuration**: The thawing period is set during deployment and cannot be changed. There is no owner or admin functionality. + +3. **Verification**: Use the provided `verify.sh` script for contract verification. The script includes constructor arguments for the thawing period. + +4. **Library Dependencies**: The contract uses EscrowSignatureLib which Forge deploys automatically. The verify script handles this correctly. + +5. For the deposit script: + - The script automatically reads the deployed escrow address from `addresses.json` based on the current chain ID + - Token approval is handled automatically if needed + - For batch deposits, ensure the arrays have the same length + - The depositor must have sufficient token balance and the tokens must be ERC20 compliant diff --git a/solidity/deferred-escrow/script/verify.sh b/solidity/deferred-escrow/script/verify.sh new file mode 100755 index 0000000000..1d072439db --- /dev/null +++ b/solidity/deferred-escrow/script/verify.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +# Script to verify deployed contracts on Basescan/Etherscan +# Usage: ./verify.sh [network] +# Example: ./verify.sh base-sepolia + +NETWORK=${1:-base-sepolia} + +# Contract address (same on all chains due to Safe Singleton Factory) +ESCROW_ADDRESS="0xF1308b39EdB10E5163581C1f8D0Bf8E26404A11f" + +echo "Verifying contracts on $NETWORK..." +echo "" + +# Verify DeferredPaymentEscrow +echo "Verifying DeferredPaymentEscrow at $ESCROW_ADDRESS..." +forge verify-contract \ + --chain $NETWORK \ + --num-of-optimizations 200 \ + --compiler-version v0.8.30 \ + --constructor-args $(cast abi-encode "constructor(uint256)" 86400) \ + $ESCROW_ADDRESS \ + src/DeferredPaymentEscrow.sol:DeferredPaymentEscrow + +echo "" +echo "Verification complete!" +echo "" +echo "View verified contract:" +echo "- DeferredPaymentEscrow: https://sepolia.basescan.org/address/$ESCROW_ADDRESS#code" diff --git a/solidity/deferred-escrow/src/DeferredPaymentEscrow.sol b/solidity/deferred-escrow/src/DeferredPaymentEscrow.sol new file mode 100644 index 0000000000..ad25111095 --- /dev/null +++ b/solidity/deferred-escrow/src/DeferredPaymentEscrow.sol @@ -0,0 +1,799 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; +import { IDeferredPaymentEscrow } from "./IDeferredPaymentEscrow.sol"; +import { EscrowSignatureLib } from "./libraries/EscrowSignatureLib.sol"; + +/** + * @title DeferredPaymentEscrow + * @notice Multi-token escrow system supporting off-chain vouchers for micropayments + * @dev Implements EIP-712 signed vouchers with ERC-1271 smart account support + */ +contract DeferredPaymentEscrow is ReentrancyGuard, EIP712, IDeferredPaymentEscrow { + using SafeERC20 for IERC20; + using EnumerableSet for EnumerableSet.Bytes32Set; + + /// @notice Maximum allowed thawing period (30 days) + uint256 public constant MAX_THAWING_PERIOD = 30 days; + + /// @notice Immutable thawing period for withdrawals + uint256 public immutable THAWING_PERIOD; + + /// @notice EIP-712 type hash for voucher structure + bytes32 public constant VOUCHER_TYPEHASH = EscrowSignatureLib.VOUCHER_TYPEHASH; + + /// @notice EIP-712 type hash for deposit authorization structure + bytes32 public constant DEPOSIT_AUTHORIZATION_TYPEHASH = EscrowSignatureLib.DEPOSIT_AUTHORIZATION_TYPEHASH; + + /// @notice EIP-712 type hash for flush authorization structure + bytes32 public constant FLUSH_AUTHORIZATION_TYPEHASH = EscrowSignatureLib.FLUSH_AUTHORIZATION_TYPEHASH; + + /// @notice EIP-712 type hash for flush all authorization structure + bytes32 public constant FLUSH_ALL_AUTHORIZATION_TYPEHASH = EscrowSignatureLib.FLUSH_ALL_AUTHORIZATION_TYPEHASH; + + /// @notice Struct to store seller and asset information for escrow keys + struct EscrowKey { + address seller; + address asset; + } + + /// @custom:storage-location erc7201:deferred.payment.escrow.main + struct MainStorage { + /// @notice Triple-nested mapping: buyer => seller => asset => EscrowAccount + mapping(address buyer => mapping(address seller => mapping(address asset => IDeferredPaymentEscrow.EscrowAccount))) accounts; + /// @notice Quadruple-nested mapping: buyer => seller => asset => voucherId => collected amount + mapping(address buyer => mapping(address seller => mapping(address asset => mapping(bytes32 voucherId => uint256)))) voucherCollected; + /// @notice Set of hashed (seller, asset) pairs per buyer for account tracking + mapping(address buyer => EnumerableSet.Bytes32Set) buyerEscrowKeys; + /// @notice Decode hash back to (seller, asset) + mapping(bytes32 keyHash => EscrowKey) escrowKeyToInfo; + /// @notice Track used deposit authorization nonces by buyer and nonce + mapping(address buyer => mapping(bytes32 nonce => bool)) usedDepositNonces; + /// @notice Track used flush authorization nonces by buyer and nonce + mapping(address buyer => mapping(bytes32 nonce => bool)) usedFlushNonces; + /// @notice Track used flush all authorization nonces by buyer and nonce + mapping(address buyer => mapping(bytes32 nonce => bool)) usedFlushAllNonces; + } + + // keccak256(abi.encode(uint256(keccak256("deferred.payment.escrow.main")) - 1)) & ~bytes32(uint256(0xff)) + bytes32 constant MAIN_STORAGE_LOCATION = 0x4cf6ea8df9d6256fc1076c222eae360b1d94159d5580e17aba1e651f33b72300; + + function _getMainStorage() private pure returns (MainStorage storage $) { + assembly { + $.slot := MAIN_STORAGE_LOCATION + } + } + + /** + * @notice Constructor + * @param _thawingPeriod Thawing period in seconds + */ + constructor(uint256 _thawingPeriod) EIP712("DeferredPaymentEscrow", "1") { + require(_thawingPeriod <= MAX_THAWING_PERIOD, InvalidThawingPeriod(_thawingPeriod, MAX_THAWING_PERIOD)); + + THAWING_PERIOD = _thawingPeriod; + } + + /** + * @notice Deposit tokens into escrow for a specific seller and asset + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + */ + function deposit(address seller, address asset, uint256 amount) external nonReentrant { + _deposit(msg.sender, seller, asset, amount, msg.sender); + } + + /** + * @notice Deposit tokens into escrow on behalf of a buyer + * @param buyer Address of the buyer who will own the escrow + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + */ + function depositTo(address buyer, address seller, address asset, uint256 amount) external nonReentrant { + require(buyer != address(0), InvalidAddress(buyer)); + _deposit(buyer, seller, asset, amount, msg.sender); + } + + /** + * @notice Deposit tokens for multiple sellers with a single asset in a single transaction + * @param asset ERC-20 token address + * @param deposits Array of deposit inputs + */ + function depositMany(address asset, DepositInput[] calldata deposits) external nonReentrant { + require(asset != address(0), InvalidAsset(asset)); + require(deposits.length != 0, NoDepositsProvided()); + + MainStorage storage $ = _getMainStorage(); + uint256 totalAmount = 0; + + // Single loop: validate inputs, calculate total, and update balances + for (uint256 i = 0; i < deposits.length; i++) { + DepositInput calldata depositInput = deposits[i]; + require(depositInput.seller != address(0), InvalidAddress(depositInput.seller)); + require(depositInput.amount != 0, InvalidAmount(depositInput.amount)); + + totalAmount += depositInput.amount; + + // Update account balance + EscrowAccount storage account = $.accounts[msg.sender][depositInput.seller][asset]; + account.balance += depositInput.amount; + + // Track account in buyerEscrowKeys set + bytes32 key = keccak256(abi.encodePacked(depositInput.seller, asset)); + if ($.buyerEscrowKeys[msg.sender].add(key)) { + $.escrowKeyToInfo[key] = EscrowKey(depositInput.seller, asset); + } + + emit Deposited(msg.sender, depositInput.seller, asset, depositInput.amount, account.balance); + } + + // Single token transfer for all deposits + IERC20(asset).safeTransferFrom(msg.sender, address(this), totalAmount); + } + + /** + * @notice Deposit tokens using EIP-712 signed authorization + * @param auth The deposit authorization struct + * @param signature Buyer's signature for the authorization + */ + function depositWithAuthorization( + DepositAuthorization calldata auth, + bytes calldata signature + ) external nonReentrant { + // Check expiry + require(block.timestamp <= auth.expiry, AuthorizationExpired(auth.expiry, block.timestamp)); + + // Check and mark nonce as used + MainStorage storage $ = _getMainStorage(); + require(!$.usedDepositNonces[auth.buyer][auth.nonce], NonceAlreadyUsed(auth.nonce)); + $.usedDepositNonces[auth.buyer][auth.nonce] = true; + + // Validate signature + require( + EscrowSignatureLib.isDepositAuthorizationValid(auth, signature, _domainSeparatorV4()), + InvalidAuthorization() + ); + + // Use internal _deposit function for consistency + _deposit(auth.buyer, auth.seller, auth.asset, auth.amount, auth.buyer); + + // Emit authorization-specific event + emit DepositAuthorized(auth.buyer, auth.seller, auth.asset, auth.amount, auth.nonce); + } + + /** + * @notice Initiate or increase withdrawal thawing amount (starts/resets thawing period) + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to add to thawing + */ + function thaw(address seller, address asset, uint256 amount) external { + require(seller != address(0), InvalidAddress(seller)); + require(asset != address(0), InvalidAsset(asset)); + require(amount != 0, InvalidAmount(amount)); + + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[msg.sender][seller][asset]; + + // Check if the requested thaw amount can be accommodated + uint256 newThawingAmount = account.thawingAmount + amount; + require(account.balance >= newThawingAmount, InsufficientBalance(account.balance, newThawingAmount)); + + _thaw(msg.sender, seller, asset, amount); + } + + /** + * @notice Cancel an ongoing thawing process + * @param seller Address of the seller + * @param asset ERC-20 token address + */ + function cancelThaw(address seller, address asset) external { + require(seller != address(0), InvalidAddress(seller)); + require(asset != address(0), InvalidAsset(asset)); + + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[msg.sender][seller][asset]; + require(account.thawingAmount != 0, NoThawingInProgress(msg.sender, seller, asset)); + + uint256 thawingAmount = account.thawingAmount; + + // Cancel thawing (no balance change needed) + account.thawingAmount = 0; + account.thawEndTime = 0; + + emit ThawCancelled(msg.sender, seller, asset, thawingAmount); + } + + /** + * @notice Complete withdrawal after thawing period + * @param seller Address of the seller + * @param asset ERC-20 token address + */ + function withdraw(address seller, address asset) external nonReentrant { + require(seller != address(0), InvalidAddress(seller)); + require(asset != address(0), InvalidAsset(asset)); + + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[msg.sender][seller][asset]; + + // Check if there's thawing in progress + require(account.thawingAmount > 0, NoThawingInProgress(msg.sender, seller, asset)); + + // Check if thawing period is complete + require(block.timestamp >= account.thawEndTime, ThawingPeriodNotCompleted(block.timestamp, account.thawEndTime)); + + // Perform the withdrawal + _withdraw(msg.sender, seller, asset); + } + + /** + * @notice Initiate or complete flush using EIP-712 signed authorization + * @dev "Flush" performs two operations on a specific escrow account: + * 1. Withdraws any funds that have completed their thawing period (ready to withdraw) + * 2. Initiates thawing for any remaining balance that isn't already thawing + * This allows a Facilitator to help a buyer recover their funds with just a signature. + * @param auth The flush authorization struct containing buyer, seller, asset, nonce, and expiry + * @param signature Buyer's signature for the authorization + */ + function flushWithAuthorization(FlushAuthorization calldata auth, bytes calldata signature) external nonReentrant { + // Check expiry + require(block.timestamp <= auth.expiry, AuthorizationExpired(auth.expiry, block.timestamp)); + + // Check and mark nonce as used + MainStorage storage $ = _getMainStorage(); + require(!$.usedFlushNonces[auth.buyer][auth.nonce], NonceAlreadyUsed(auth.nonce)); + $.usedFlushNonces[auth.buyer][auth.nonce] = true; + + // Validate signature + require( + EscrowSignatureLib.isFlushAuthorizationValid(auth, signature, _domainSeparatorV4()), + InvalidAuthorization() + ); + + // First, withdraw any funds that are ready + _withdraw(auth.buyer, auth.seller, auth.asset); + + // Then, calculate and thaw any remaining balance that isn't already thawing + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[auth.buyer][auth.seller][auth.asset]; + uint256 availableToThaw = account.balance > account.thawingAmount ? account.balance - account.thawingAmount : 0; + + uint256 thawedAmount = 0; + if (availableToThaw > 0) { + thawedAmount = _thaw(auth.buyer, auth.seller, auth.asset, availableToThaw); + } + + // Emit the flush event (even if nothing happened - idempotent operation) + emit FlushAuthorized(auth.buyer, auth.seller, auth.asset, auth.nonce, thawedAmount > 0); + } + + /** + * @notice Flush all escrows for a buyer using EIP-712 signed authorization + * @dev "Flush all" performs a flush operation on ALL of a buyer's escrow accounts: + * For each account: + * 1. Withdraws any funds that have completed their thawing period (ready to withdraw) + * 2. Initiates thawing for any remaining balance that isn't already thawing + * This allows a Facilitator to help a buyer recover all their escrowed funds across + * all sellers and assets with just a single signature. + * @param auth The flush all authorization struct containing buyer, nonce, and expiry + * @param signature Buyer's signature for the authorization + */ + function flushAllWithAuthorization( + FlushAllAuthorization calldata auth, + bytes calldata signature + ) external nonReentrant { + // Check expiry + require(block.timestamp <= auth.expiry, AuthorizationExpired(auth.expiry, block.timestamp)); + + // Check and mark nonce as used + MainStorage storage $ = _getMainStorage(); + require(!$.usedFlushAllNonces[auth.buyer][auth.nonce], NonceAlreadyUsed(auth.nonce)); + $.usedFlushAllNonces[auth.buyer][auth.nonce] = true; + + // Validate signature + require( + EscrowSignatureLib.isFlushAllAuthorizationValid(auth, signature, _domainSeparatorV4()), + InvalidAuthorization() + ); + + uint256 accountsFlushed = 0; + + // Get all escrow keys for this buyer + EnumerableSet.Bytes32Set storage escrowKeys = $.buyerEscrowKeys[auth.buyer]; + uint256 keysLength = escrowKeys.length(); + + // Process each account: withdraw ready funds AND thaw remaining balance + // Iterate backwards to handle removals safely + for (uint256 i = keysLength; i > 0; i--) { + bytes32 escrowKey = escrowKeys.at(i - 1); + EscrowKey storage keyInfo = $.escrowKeyToInfo[escrowKey]; + + // First, withdraw any funds that are ready + uint256 withdrawnAmount = _withdraw(auth.buyer, keyInfo.seller, keyInfo.asset); + + // Then, calculate and thaw any remaining balance that isn't already thawing + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[auth.buyer][keyInfo.seller][keyInfo.asset]; + uint256 availableToThaw = account.balance > account.thawingAmount ? account.balance - account.thawingAmount : 0; + + uint256 thawedAmount = 0; + if (availableToThaw > 0) { + thawedAmount = _thaw(auth.buyer, keyInfo.seller, keyInfo.asset, availableToThaw); + } + + // Count accounts that had activity + if (withdrawnAmount > 0 || thawedAmount > 0) { + accountsFlushed++; + } + } + + emit FlushAllAuthorized(auth.buyer, auth.nonce, accountsFlushed); + } + + /** + * @notice Collect a single voucher + * @param voucher The voucher to collect + * @param signature Buyer's signature for the voucher + */ + function collect(Voucher calldata voucher, bytes calldata signature) external nonReentrant { + _collectVoucher(voucher, signature); + } + + /** + * @notice Collect multiple vouchers in a single transaction + * @param vouchers Array of signed vouchers + */ + function collectMany(SignedVoucher[] calldata vouchers) external nonReentrant { + require(vouchers.length != 0, NoVouchersProvided()); + + for (uint256 i = 0; i < vouchers.length; i++) { + _collectVoucher(vouchers[i].voucher, vouchers[i].signature); + } + } + + /** + * @notice Get escrow account details for a buyer-seller-asset combination + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @return EscrowAccount struct with balance, thawing amount, and thaw end time + */ + function getAccount(address buyer, address seller, address asset) external view returns (EscrowAccount memory) { + return _getMainStorage().accounts[buyer][seller][asset]; + } + + /** + * @notice Batch read account data including balance after deducting outstanding vouchers + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param voucherIds Unique identifiers of the vouchers + * @param valueAggregates Value aggregates of the vouchers, order must match voucherIds + * @return balance Available balance after deducting outstanding vouchers + * @return allowance Allowance from asset contract + * @return nonce Nonce from asset contract + */ + function getAccountData( + address buyer, + address seller, + address asset, + bytes32[] memory voucherIds, + uint256[] memory valueAggregates + ) external view returns (uint256 balance, uint256 allowance, uint256 nonce) { + EscrowAccount memory account = _getMainStorage().accounts[buyer][seller][asset]; + balance = account.balance - account.thawingAmount; + for (uint256 i = 0; i < voucherIds.length; i++) { + uint256 alreadyCollected = _getMainStorage().voucherCollected[buyer][seller][asset][voucherIds[i]]; + uint256 toCollect = valueAggregates[i] > alreadyCollected ? valueAggregates[i] - alreadyCollected : 0; + if (balance >= toCollect) { + balance -= toCollect; + } else { + balance = 0; + break; + } + } + allowance = IERC20(asset).allowance(buyer, address(this)); + nonce = IERC20Permit(asset).nonces(buyer); + } + + /** + * @notice Batch read all data needed for x402 verification in a single call + * @param voucher The voucher to verify + * @param depositAuthNonce The deposit authorization nonce (pass bytes32(0) if not using deposit auth) + * @return voucherOutstanding Outstanding amount for the voucher + * @return voucherCollectable Collectable amount for the voucher + * @return balance Balance of the escrow account + * @return availableBalance Available balance (balance minus thawing amount) + * @return allowance Allowance from asset contract + * @return nonce Nonce from asset contract + * @return isDepositNonceUsed Whether the deposit authorization nonce has been used + */ + function getVerificationData( + Voucher calldata voucher, + bytes32 depositAuthNonce + ) + external + view + returns ( + uint256 voucherOutstanding, + uint256 voucherCollectable, + uint256 balance, + uint256 availableBalance, + uint256 allowance, + uint256 nonce, + bool isDepositNonceUsed + ) + { + MainStorage storage $ = _getMainStorage(); + + // Get voucher amounts + uint256 alreadyCollected = $.voucherCollected[voucher.buyer][voucher.seller][voucher.asset][voucher.id]; + (voucherOutstanding, voucherCollectable) = _getOutstandingAndCollectableAmount(voucher, alreadyCollected); + + // Get balance and available balance (balance minus thawing amount) + EscrowAccount memory account = $.accounts[voucher.buyer][voucher.seller][voucher.asset]; + balance = account.balance; + availableBalance = account.balance - account.thawingAmount; + + // Get both allowance and nonce from asset contract + allowance = IERC20(voucher.asset).allowance(voucher.buyer, address(this)); + nonce = IERC20Permit(voucher.asset).nonces(voucher.buyer); + + // Check if deposit authorization nonce is used + isDepositNonceUsed = $.usedDepositNonces[voucher.buyer][depositAuthNonce]; + } + + /** + * @notice Get the amount already collected for a specific voucher + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param voucherId Unique identifier of the voucher + * @return Amount already collected + */ + function getVoucherCollected( + address buyer, + address seller, + address asset, + bytes32 voucherId + ) external view returns (uint256) { + return _getMainStorage().voucherCollected[buyer][seller][asset][voucherId]; + } + + /** + * @notice Calculate the outstanding and collectable amounts for a voucher + * @param voucher The voucher to check + * @return outstanding Total amount still owed on the voucher + * @return collectable Amount that can actually be collected now (considering available balance) + */ + function getOutstandingAndCollectableAmount( + Voucher calldata voucher + ) external view returns (uint256 outstanding, uint256 collectable) { + MainStorage storage $ = _getMainStorage(); + uint256 alreadyCollected = $.voucherCollected[voucher.buyer][voucher.seller][voucher.asset][voucher.id]; + return _getOutstandingAndCollectableAmount(voucher, alreadyCollected); + } + + /** + * @notice Validate a voucher signature + * @param voucher The voucher to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isVoucherSignatureValid(Voucher calldata voucher, bytes calldata signature) external view returns (bool) { + return EscrowSignatureLib.isVoucherSignatureValid(voucher, signature, _domainSeparatorV4()); + } + + /** + * @notice Validate a deposit authorization signature + * @param auth The deposit authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isDepositAuthorizationValid( + DepositAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool) { + return EscrowSignatureLib.isDepositAuthorizationValid(auth, signature, _domainSeparatorV4()); + } + + /** + * @notice Validate a flush authorization signature + * @param auth The flush authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isFlushAuthorizationValid( + FlushAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool) { + return EscrowSignatureLib.isFlushAuthorizationValid(auth, signature, _domainSeparatorV4()); + } + + /** + * @notice Validate a flush all authorization signature + * @param auth The flush all authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isFlushAllAuthorizationValid( + FlushAllAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool) { + return EscrowSignatureLib.isFlushAllAuthorizationValid(auth, signature, _domainSeparatorV4()); + } + + /** + * @notice Validate a deposit authorization nonce + * @param buyer Address of the buyer + * @param nonce The nonce to validate + * @return True if nonce is used + */ + function isDepositAuthorizationNonceUsed(address buyer, bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedDepositNonces[buyer][nonce]; + } + + /** + * @notice Validate a flush authorization nonce + * @param buyer Address of the buyer + * @param nonce The nonce to validate + * @return True if nonce is used + */ + function isFlushAuthorizationNonceUsed(address buyer, bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedFlushNonces[buyer][nonce]; + } + + /** + * @notice Validate a flush all authorization nonce + * @param buyer Address of the buyer + * @param nonce The nonce to validate + * @return True if nonce is used + */ + function isFlushAllAuthorizationNonceUsed(address buyer, bytes32 nonce) external view returns (bool) { + return _getMainStorage().usedFlushAllNonces[buyer][nonce]; + } + + /** + * @notice Get the EIP-712 domain separator + * @return Domain separator hash + */ + function DOMAIN_SEPARATOR() external view returns (bytes32) { + return _domainSeparatorV4(); + } + + /** + * @notice Internal function to collect a voucher + * @param voucher The voucher to collect + * @param signature Buyer's signature for the voucher + */ + function _collectVoucher(Voucher calldata voucher, bytes calldata signature) internal { + // Validate basic voucher parameters + require(voucher.buyer != address(0), InvalidAddress(voucher.buyer)); + require(voucher.asset != address(0), InvalidAsset(voucher.asset)); + require(voucher.escrow == address(this), InvalidEscrow(voucher.escrow, address(this))); + require(voucher.chainId == block.chainid, InvalidChainId(voucher.chainId, block.chainid)); + require(voucher.valueAggregate != 0, InvalidAmount(voucher.valueAggregate)); + require(block.timestamp <= voucher.expiry, VoucherExpired(voucher.id, block.timestamp, voucher.expiry)); + + // Validate signature + require( + EscrowSignatureLib.isVoucherSignatureValid(voucher, signature, _domainSeparatorV4()), + InvalidSignature(voucher.id, voucher.buyer) + ); + + MainStorage storage $ = _getMainStorage(); + + // Get current collected amount and calculate what can be collected + uint256 alreadyCollected = $.voucherCollected[voucher.buyer][voucher.seller][voucher.asset][voucher.id]; + (uint256 outstanding, uint256 collectAmount) = _getOutstandingAndCollectableAmount(voucher, alreadyCollected); + + if (outstanding == 0) { + // Voucher is already fully collected + emit VoucherAlreadyCollected(voucher.id, voucher.buyer, voucher.seller, voucher.asset, alreadyCollected); + return; + } + + if (collectAmount == 0) { + // Voucher has outstanding amount but no balance available + emit VoucherNoCollectableBalance( + voucher.id, + voucher.buyer, + voucher.seller, + voucher.asset, + outstanding, + alreadyCollected + ); + return; + } + + // Proceed with collection + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[voucher.buyer][voucher.seller][voucher.asset]; + + // Update state + $.voucherCollected[voucher.buyer][voucher.seller][voucher.asset][voucher.id] = alreadyCollected + collectAmount; + + // Deduct from balance + account.balance -= collectAmount; + + // If balance drops below thawing amount, adjust thawing amount + if (account.balance < account.thawingAmount) { + account.thawingAmount = account.balance; + } + + // Transfer tokens directly to seller (no protocol fee) + IERC20(voucher.asset).safeTransfer(voucher.seller, collectAmount); + + emit VoucherCollected( + voucher.id, + voucher.buyer, + voucher.seller, + voucher.asset, + collectAmount, + alreadyCollected + collectAmount + ); + + // Clean up if account is empty + if (account.balance == 0 && account.thawingAmount == 0) { + bytes32 key = keccak256(abi.encodePacked(voucher.seller, voucher.asset)); + $.buyerEscrowKeys[voucher.buyer].remove(key); + } + } + + /** + * @notice Internal function to handle deposits + * @param buyer Address of the buyer who will own the escrow + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + * @param payer Address paying for the deposit + */ + function _deposit(address buyer, address seller, address asset, uint256 amount, address payer) internal { + require(seller != address(0), InvalidAddress(seller)); + require(asset != address(0), InvalidAsset(asset)); + require(amount != 0, InvalidAmount(amount)); + + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[buyer][seller][asset]; + + // Transfer tokens from payer to this contract + IERC20(asset).safeTransferFrom(payer, address(this), amount); + + // Update account balance + account.balance += amount; + + // Track account in buyerEscrowKeys set + bytes32 key = keccak256(abi.encodePacked(seller, asset)); + if ($.buyerEscrowKeys[buyer].add(key)) { + $.escrowKeyToInfo[key] = EscrowKey(seller, asset); + } + + emit Deposited(buyer, seller, asset, amount, account.balance); + } + + /** + * @notice Internal function to initiate or increase thawing + * @dev This function will NOT revert if the requested amount exceeds the available balance. + * Instead, it will cap the thaw amount to the maximum available (balance - already thawing). + * If funds are already thawing, this will ADD to the thawing amount and reset the timer. + * The thaw timer always resets to a full thawing period from the current timestamp, + * regardless of any previous thaw progress. + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to thaw (must be > 0) + * @return thawedAmount The actual amount that was set to thaw (may be less than requested if capped) + */ + function _thaw(address buyer, address seller, address asset, uint256 amount) internal returns (uint256 thawedAmount) { + require(amount > 0, InvalidAmount(amount)); + + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[buyer][seller][asset]; + + // Calculate how much can be thawed + uint256 availableToThaw = account.balance - account.thawingAmount; + if (availableToThaw == 0) { + return 0; // Nothing to thaw + } + + // Cap to available amount + thawedAmount = amount > availableToThaw ? availableToThaw : amount; + + // Store previous values for event + uint256 previousThawingAmount = account.thawingAmount; + uint256 previousThawEndTime = account.thawEndTime; + + // Update thawing state + account.thawingAmount = previousThawingAmount + thawedAmount; + account.thawEndTime = uint64(block.timestamp + THAWING_PERIOD); + + emit ThawInitiated( + buyer, + seller, + asset, + account.thawingAmount, + previousThawingAmount, + account.thawEndTime, + previousThawEndTime + ); + } + + /** + * @notice Internal function to withdraw funds that have completed thawing + * @dev This function will NOT revert if there are no funds ready to withdraw. + * It will simply return 0 if: + * - No funds are currently thawing (thawingAmount == 0) + * - The thaw period hasn't completed yet (block.timestamp < thawEndTime) + * The function automatically handles edge cases like collections during thaw period + * by capping the withdrawal to the actual balance if needed. + * If the account becomes empty after withdrawal, it is automatically cleaned up + * and removed from the buyer's escrow keys set. + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @return withdrawnAmount The actual amount withdrawn (0 if nothing was ready) + */ + function _withdraw(address buyer, address seller, address asset) internal returns (uint256 withdrawnAmount) { + MainStorage storage $ = _getMainStorage(); + IDeferredPaymentEscrow.EscrowAccount storage account = $.accounts[buyer][seller][asset]; + + // Check if there's anything to withdraw + if (account.thawingAmount == 0 || block.timestamp < account.thawEndTime) { + return 0; // Nothing ready to withdraw + } + + withdrawnAmount = account.thawingAmount; + + // Ensure balance still covers the thawing amount (in case of collections during thaw) + if (withdrawnAmount > account.balance) { + withdrawnAmount = account.balance; + } + + // Update balance and clear thawing state + account.balance -= withdrawnAmount; + account.thawingAmount = 0; + account.thawEndTime = 0; + + // Transfer tokens to buyer + if (withdrawnAmount > 0) { + IERC20(asset).safeTransfer(buyer, withdrawnAmount); + emit Withdrawn(buyer, seller, asset, withdrawnAmount, account.balance); + } + + // Clean up if account is empty + if (account.balance == 0 && account.thawingAmount == 0) { + bytes32 key = keccak256(abi.encodePacked(seller, asset)); + $.buyerEscrowKeys[buyer].remove(key); + } + } + + /** + * @notice Internal function to calculate outstanding and collectable amounts + * @param voucher The voucher to check + * @param alreadyCollected Amount already collected for this voucher + * @return outstanding Total amount still owed on the voucher + * @return collectable Amount that can actually be collected now + */ + function _getOutstandingAndCollectableAmount( + Voucher calldata voucher, + uint256 alreadyCollected + ) internal view returns (uint256 outstanding, uint256 collectable) { + outstanding = 0; + collectable = 0; + + if (voucher.valueAggregate > alreadyCollected) { + outstanding = voucher.valueAggregate - alreadyCollected; + + MainStorage storage $ = _getMainStorage(); + EscrowAccount memory account = $.accounts[voucher.buyer][voucher.seller][voucher.asset]; + + // Full balance is available for collection (thawing doesn't block sellers) + collectable = outstanding > account.balance ? account.balance : outstanding; + } + } +} diff --git a/solidity/deferred-escrow/src/IDeferredPaymentEscrow.sol b/solidity/deferred-escrow/src/IDeferredPaymentEscrow.sol new file mode 100644 index 0000000000..e911e37d05 --- /dev/null +++ b/solidity/deferred-escrow/src/IDeferredPaymentEscrow.sol @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +/** + * @title IDeferredPaymentEscrow + * @notice Interface for a multi-token escrow system supporting off-chain vouchers + * @dev Enables micropayments between buyers and sellers using EIP-712 signed vouchers + */ +interface IDeferredPaymentEscrow { + // ============ ERRORS ============ + + error InvalidAddress(address provided); + error InvalidAmount(uint256 provided); + error InvalidAsset(address provided); + error InvalidThawingPeriod(uint256 provided, uint256 maximum); + error InsufficientBalance(uint256 available, uint256 requested); + error NoThawingInProgress(address buyer, address seller, address asset); + error ThawingPeriodNotCompleted(uint256 currentTime, uint256 thawEndTime); + error InvalidEscrow(address provided, address expected); + error InvalidChainId(uint256 provided, uint256 expected); + error VoucherExpired(bytes32 voucherId, uint256 currentTime, uint256 expiry); + error InvalidSignature(bytes32 voucherId, address buyer); + error NoDepositsProvided(); + error NoVouchersProvided(); + error AuthorizationExpired(uint64 expiry, uint256 currentTime); + error NonceAlreadyUsed(bytes32 nonce); + error InvalidAuthorization(); + + // ============ STRUCTS ============ + + /** + * @notice Represents an escrow account for a specific buyer-seller-asset combination + * @param balance Current deposited balance available for payments + * @param thawingAmount Amount currently in the thawing process + * @param thawEndTime Timestamp when the thawing period completes + */ + struct EscrowAccount { + uint256 balance; + uint256 thawingAmount; + uint64 thawEndTime; + } + + /** + * @notice Represents a payment voucher with all required fields + * @param id Unique identifier for the voucher (unique per buyer-seller pair) + * @param buyer Address of the payment initiator + * @param seller Address of the payment recipient + * @param valueAggregate Total outstanding amount (monotonically increasing) + * @param asset ERC-20 token address + * @param timestamp Last aggregation timestamp + * @param nonce Incremented with each aggregation + * @param escrow Address of this escrow contract + * @param chainId Network chain ID + * @param expiry Expiration timestamp after which voucher cannot be collected + */ + struct Voucher { + bytes32 id; + address buyer; + address seller; + uint256 valueAggregate; + address asset; + uint64 timestamp; + uint256 nonce; + address escrow; + uint256 chainId; + uint64 expiry; + } + + /** + * @notice Input structure for batch deposits + * @param seller Address of the seller to deposit for + * @param amount Amount to deposit + */ + struct DepositInput { + address seller; + uint256 amount; + } + + /** + * @notice Signed voucher (Input structure for batch voucher collections) + * @param voucher The voucher to collect + * @param signature Buyer's signature for the voucher + * @param amount Amount to collect (0 means collect all available) + */ + struct SignedVoucher { + Voucher voucher; + bytes signature; + } + + /** + * @notice Authorization for depositing funds into escrow + * @param buyer Address of the buyer authorizing the deposit + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + * @param nonce Random bytes32 for replay protection + * @param expiry Expiration timestamp + */ + struct DepositAuthorization { + address buyer; + address seller; + address asset; + uint256 amount; + bytes32 nonce; + uint64 expiry; + } + + /** + * @notice Authorization for flushing a specific escrow + * @param buyer Address of the buyer authorizing the flush + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param nonce Random bytes32 for replay protection + * @param expiry Expiration timestamp + */ + struct FlushAuthorization { + address buyer; + address seller; + address asset; + bytes32 nonce; + uint64 expiry; + } + + /** + * @notice Authorization for flushing all escrows for a buyer + * @param buyer Address of the buyer authorizing the flush + * @param nonce Random bytes32 for replay protection + * @param expiry Expiration timestamp + */ + struct FlushAllAuthorization { + address buyer; + bytes32 nonce; + uint64 expiry; + } + + // ============ EVENTS ============ + + /** + * @notice Emitted when funds are deposited into an escrow account + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount deposited + * @param newBalance New total balance for the account + */ + event Deposited( + address indexed buyer, + address indexed seller, + address indexed asset, + uint256 amount, + uint256 newBalance + ); + + /** + * @notice Emitted when a thawing process is initiated or increased + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param newThawingAmount New total amount being thawed + * @param previousThawingAmount Previous amount that was thawing (0 if new thaw) + * @param newThawEndTime New timestamp when thawing completes + * @param previousThawEndTime Previous thaw end time (0 if new thaw) + */ + event ThawInitiated( + address indexed buyer, + address indexed seller, + address indexed asset, + uint256 newThawingAmount, + uint256 previousThawingAmount, + uint256 newThawEndTime, + uint256 previousThawEndTime + ); + + /** + * @notice Emitted when a thawing process is cancelled + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount that was being thawed + */ + event ThawCancelled(address indexed buyer, address indexed seller, address indexed asset, uint256 amount); + + /** + * @notice Emitted when funds are withdrawn from an escrow account + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount withdrawn + * @param remainingBalance Remaining balance after withdrawal + */ + event Withdrawn( + address indexed buyer, + address indexed seller, + address indexed asset, + uint256 amount, + uint256 remainingBalance + ); + + /** + * @notice Emitted when a voucher is collected + * @param voucherId Unique identifier of the voucher + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount collected + * @param totalCollected Total amount collected for this voucher + */ + event VoucherCollected( + bytes32 indexed voucherId, + address indexed buyer, + address indexed seller, + address asset, + uint256 amount, + uint256 totalCollected + ); + + /** + * @notice Emitted when a voucher collection is skipped because it was already fully collected + * @param voucherId Unique identifier of the voucher + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param totalCollected Total amount already collected for this voucher + */ + event VoucherAlreadyCollected( + bytes32 indexed voucherId, + address indexed buyer, + address indexed seller, + address asset, + uint256 totalCollected + ); + + /** + * @notice Emitted when a voucher has outstanding amount but no collectable balance + * @param voucherId Unique identifier of the voucher + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param outstanding Amount still owed on the voucher + * @param alreadyCollected Amount already collected for this voucher + */ + event VoucherNoCollectableBalance( + bytes32 indexed voucherId, + address indexed buyer, + address indexed seller, + address asset, + uint256 outstanding, + uint256 alreadyCollected + ); + + /** + * @notice Emitted when a deposit is made using authorization + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount deposited + * @param nonce Nonce used for the authorization + */ + event DepositAuthorized( + address indexed buyer, + address indexed seller, + address indexed asset, + uint256 amount, + bytes32 nonce + ); + + /** + * @notice Emitted when a flush is initiated or completed using authorization + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param nonce Nonce used for the authorization + * @param thawing True if thawing was initiated, false if withdrawal completed + */ + event FlushAuthorized( + address indexed buyer, + address indexed seller, + address indexed asset, + bytes32 nonce, + bool thawing + ); + + /** + * @notice Emitted when all escrows are flushed using authorization + * @param buyer Address of the buyer + * @param nonce Nonce used for the authorization + * @param accountsFlushed Number of accounts affected + */ + event FlushAllAuthorized(address indexed buyer, bytes32 nonce, uint256 accountsFlushed); + + // ============ DEPOSIT FUNCTIONS ============ + + /** + * @notice Deposit tokens into escrow for a specific seller and asset + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + */ + function deposit(address seller, address asset, uint256 amount) external; + + /** + * @notice Deposit tokens into escrow on behalf of a buyer + * @param buyer Address of the buyer who will own the escrow + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to deposit + */ + function depositTo(address buyer, address seller, address asset, uint256 amount) external; + + /** + * @notice Deposit tokens for multiple sellers with a single asset in a single transaction + * @param asset ERC-20 token address + * @param deposits Array of deposit inputs + */ + function depositMany(address asset, DepositInput[] calldata deposits) external; + + /** + * @notice Deposit tokens using EIP-712 signed authorization + * @param auth The deposit authorization struct + * @param signature Buyer's signature for the authorization + */ + function depositWithAuthorization(DepositAuthorization calldata auth, bytes calldata signature) external; + + // ============ WITHDRAWAL FUNCTIONS ============ + + /** + * @notice Initiate withdrawal process (starts thawing period) + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param amount Amount to withdraw + */ + function thaw(address seller, address asset, uint256 amount) external; + + /** + * @notice Cancel an ongoing thawing process + * @param seller Address of the seller + * @param asset ERC-20 token address + */ + function cancelThaw(address seller, address asset) external; + + /** + * @notice Complete withdrawal after thawing period + * @param seller Address of the seller + * @param asset ERC-20 token address + */ + function withdraw(address seller, address asset) external; + + /** + * @notice Initiate or complete flush using EIP-712 signed authorization + * @param auth The flush authorization struct + * @param signature Buyer's signature for the authorization + */ + function flushWithAuthorization(FlushAuthorization calldata auth, bytes calldata signature) external; + + /** + * @notice Flush all escrows for a buyer using EIP-712 signed authorization + * @param auth The flush all authorization struct + * @param signature Buyer's signature for the authorization + */ + function flushAllWithAuthorization(FlushAllAuthorization calldata auth, bytes calldata signature) external; + + // ============ COLLECTION FUNCTIONS ============ + + /** + * @notice Collect a single voucher (partial or full) + * @param voucher The voucher to collect + * @param signature Buyer's signature for the voucher + */ + function collect(Voucher calldata voucher, bytes calldata signature) external; + + /** + * @notice Collect multiple vouchers in a single transaction + * @param vouchers Array of signed vouchers + */ + function collectMany(SignedVoucher[] calldata vouchers) external; + + // ============ VIEW FUNCTIONS ============ + + /** + * @notice Get escrow account details for a buyer-seller-asset combination + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @return EscrowAccount struct with balance, thawing amount, and thaw end time + */ + function getAccount(address buyer, address seller, address asset) external view returns (EscrowAccount memory); + + /** + * @notice Batch read account data including balance after deducting outstanding vouchers + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param voucherIds Unique identifiers of the vouchers + * @param valueAggregates Value aggregates of the vouchers, order must match voucherIds + * @return balance Available balance after deducting outstanding vouchers + * @return allowance Allowance from asset contract + * @return nonce Nonce from asset contract + */ + function getAccountData( + address buyer, + address seller, + address asset, + bytes32[] memory voucherIds, + uint256[] memory valueAggregates + ) external view returns (uint256 balance, uint256 allowance, uint256 nonce); + + /** + * @notice Batch read all data needed for x402 verification in a single call + * @param voucher The voucher to verify + * @param depositAuthNonce The deposit authorization nonce (pass bytes32(0) if not using deposit auth) + * @return voucherOutstanding Outstanding amount for the voucher + * @return voucherCollectable Collectable amount for the voucher + * @return balance Balance of the escrow account + * @return availableBalance Available balance (balance minus thawing amount) + * @return allowance Allowance from asset contract + * @return nonce Nonce from asset contract + * @return isDepositNonceUsed Whether the deposit authorization nonce has been used + */ + function getVerificationData( + Voucher calldata voucher, + bytes32 depositAuthNonce + ) + external + view + returns ( + uint256 voucherOutstanding, + uint256 voucherCollectable, + uint256 balance, + uint256 availableBalance, + uint256 allowance, + uint256 nonce, + bool isDepositNonceUsed + ); + + /** + * @notice Get the amount already collected for a specific voucher + * @param buyer Address of the buyer + * @param seller Address of the seller + * @param asset ERC-20 token address + * @param voucherId Unique identifier of the voucher + * @return Amount already collected + */ + function getVoucherCollected( + address buyer, + address seller, + address asset, + bytes32 voucherId + ) external view returns (uint256); + + /** + * @notice Calculate the outstanding and collectable amounts for a voucher + * @param voucher The voucher to check + * @return outstanding Total amount still owed on the voucher + * @return collectable Amount that can actually be collected now (considering available balance) + */ + function getOutstandingAndCollectableAmount( + Voucher calldata voucher + ) external view returns (uint256 outstanding, uint256 collectable); + + /** + * @notice Validate a voucher signature + * @param voucher The voucher to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isVoucherSignatureValid(Voucher calldata voucher, bytes calldata signature) external view returns (bool); + + /** + * @notice Validate a deposit authorization signature + * @param auth The deposit authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isDepositAuthorizationValid( + DepositAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool); + + /** + * @notice Validate a flush authorization signature + * @param auth The flush authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isFlushAuthorizationValid( + FlushAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool); + + /** + * @notice Validate a flush all authorization signature + * @param auth The flush all authorization to validate + * @param signature The signature to validate + * @return True if signature is valid + */ + function isFlushAllAuthorizationValid( + FlushAllAuthorization calldata auth, + bytes calldata signature + ) external view returns (bool); + + /** + * @notice Get the current thawing period + * @return Thawing period in seconds + */ + function THAWING_PERIOD() external view returns (uint256); + + /** + * @notice Get the EIP-712 domain separator + * @return Domain separator hash + */ + function DOMAIN_SEPARATOR() external view returns (bytes32); + + // ============ CONSTANTS ============ + + /** + * @notice Maximum allowed thawing period + * @return Maximum thawing period in seconds (30 days) + */ + function MAX_THAWING_PERIOD() external view returns (uint256); + + /** + * @notice EIP-712 type hash for voucher structure + * @return Type hash for voucher + */ + function VOUCHER_TYPEHASH() external view returns (bytes32); + + /** + * @notice EIP-712 type hash for deposit authorization structure + * @return Type hash for deposit authorization + */ + function DEPOSIT_AUTHORIZATION_TYPEHASH() external view returns (bytes32); + + /** + * @notice EIP-712 type hash for flush authorization structure + * @return Type hash for flush authorization + */ + function FLUSH_AUTHORIZATION_TYPEHASH() external view returns (bytes32); + + /** + * @notice EIP-712 type hash for flush all authorization structure + * @return Type hash for flush all authorization + */ + function FLUSH_ALL_AUTHORIZATION_TYPEHASH() external view returns (bytes32); +} diff --git a/solidity/deferred-escrow/src/libraries/EscrowSignatureLib.sol b/solidity/deferred-escrow/src/libraries/EscrowSignatureLib.sol new file mode 100644 index 0000000000..c8d08a37b0 --- /dev/null +++ b/solidity/deferred-escrow/src/libraries/EscrowSignatureLib.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {SignatureChecker} from "../../lib/openzeppelin-contracts/contracts/utils/cryptography/SignatureChecker.sol"; +import {IDeferredPaymentEscrow} from "../IDeferredPaymentEscrow.sol"; + +/** + * @title EscrowSignatureLib + * @notice Library for escrow-related signature validation using EIP-712 + */ +library EscrowSignatureLib { + /// @notice EIP-712 type hash for voucher structure + bytes32 public constant VOUCHER_TYPEHASH = keccak256( + "Voucher(bytes32 id,address buyer,address seller,uint256 valueAggregate,address asset,uint64 timestamp,uint256 nonce,address escrow,uint256 chainId,uint64 expiry)" + ); + + /// @notice EIP-712 type hash for deposit authorization + bytes32 public constant DEPOSIT_AUTHORIZATION_TYPEHASH = keccak256( + "DepositAuthorization(address buyer,address seller,address asset,uint256 amount,bytes32 nonce,uint64 expiry)" + ); + + /// @notice EIP-712 type hash for flush authorization + bytes32 public constant FLUSH_AUTHORIZATION_TYPEHASH = + keccak256("FlushAuthorization(address buyer,address seller,address asset,bytes32 nonce,uint64 expiry)"); + + /// @notice EIP-712 type hash for flush all authorization + bytes32 public constant FLUSH_ALL_AUTHORIZATION_TYPEHASH = + keccak256("FlushAllAuthorization(address buyer,bytes32 nonce,uint64 expiry)"); + + /** + * @notice Validate voucher signature + * @param voucher The voucher to validate + * @param signature The signature to validate + * @param domainSeparator The EIP-712 domain separator + * @return True if signature is valid + */ + function isVoucherSignatureValid( + IDeferredPaymentEscrow.Voucher calldata voucher, + bytes calldata signature, + bytes32 domainSeparator + ) external view returns (bool) { + bytes32 structHash = keccak256( + abi.encode( + VOUCHER_TYPEHASH, + voucher.id, + voucher.buyer, + voucher.seller, + voucher.valueAggregate, + voucher.asset, + voucher.timestamp, + voucher.nonce, + voucher.escrow, + voucher.chainId, + voucher.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + + // Use OpenZeppelin's SignatureChecker for both EOA and ERC-1271 + return SignatureChecker.isValidSignatureNow(voucher.buyer, digest, signature); + } + + /** + * @notice Validate deposit authorization signature + * @param auth The deposit authorization to validate + * @param signature The signature to validate + * @param domainSeparator The EIP-712 domain separator + * @return True if signature is valid + */ + function isDepositAuthorizationValid( + IDeferredPaymentEscrow.DepositAuthorization calldata auth, + bytes calldata signature, + bytes32 domainSeparator + ) external view returns (bool) { + bytes32 structHash = keccak256( + abi.encode( + DEPOSIT_AUTHORIZATION_TYPEHASH, + auth.buyer, + auth.seller, + auth.asset, + auth.amount, + auth.nonce, + auth.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + + // Use OpenZeppelin's SignatureChecker for both EOA and ERC-1271 + return SignatureChecker.isValidSignatureNow(auth.buyer, digest, signature); + } + + /** + * @notice Validate flush authorization signature + * @param auth The flush authorization to validate + * @param signature The signature to validate + * @param domainSeparator The EIP-712 domain separator + * @return True if signature is valid + */ + function isFlushAuthorizationValid( + IDeferredPaymentEscrow.FlushAuthorization calldata auth, + bytes calldata signature, + bytes32 domainSeparator + ) external view returns (bool) { + bytes32 structHash = keccak256( + abi.encode(FLUSH_AUTHORIZATION_TYPEHASH, auth.buyer, auth.seller, auth.asset, auth.nonce, auth.expiry) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + + // Use OpenZeppelin's SignatureChecker for both EOA and ERC-1271 + return SignatureChecker.isValidSignatureNow(auth.buyer, digest, signature); + } + + /** + * @notice Validate flush all authorization signature + * @param auth The flush all authorization to validate + * @param signature The signature to validate + * @param domainSeparator The EIP-712 domain separator + * @return True if signature is valid + */ + function isFlushAllAuthorizationValid( + IDeferredPaymentEscrow.FlushAllAuthorization calldata auth, + bytes calldata signature, + bytes32 domainSeparator + ) external view returns (bool) { + bytes32 structHash = + keccak256(abi.encode(FLUSH_ALL_AUTHORIZATION_TYPEHASH, auth.buyer, auth.nonce, auth.expiry)); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); + + // Use OpenZeppelin's SignatureChecker for both EOA and ERC-1271 + return SignatureChecker.isValidSignatureNow(auth.buyer, digest, signature); + } +} diff --git a/solidity/deferred-escrow/test/BaseTest.sol b/solidity/deferred-escrow/test/BaseTest.sol new file mode 100644 index 0000000000..75e4a36cef --- /dev/null +++ b/solidity/deferred-escrow/test/BaseTest.sol @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {Test} from "forge-std/Test.sol"; +import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; + +import {DeferredPaymentEscrow} from "../src/DeferredPaymentEscrow.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; +import {MockERC20} from "./mocks/MockERC20.sol"; +import {MockERC1271} from "./mocks/MockERC1271.sol"; +import {MockNonStandardERC20} from "./mocks/MockNonStandardERC20.sol"; + +contract BaseTest is Test { + using ECDSA for bytes32; + + DeferredPaymentEscrow public escrow; + MockERC20 public usdc; + MockERC20 public usdt; + MockERC1271 public smartWallet; + + address public buyer = makeAddr("buyer"); + address public seller = makeAddr("seller"); + + uint256 public buyerPrivateKey = 0x12345; + address public buyerFromPrivateKey; + + uint256 public constant THAWING_PERIOD = 7 days; + uint256 public constant INITIAL_BALANCE = 1000000e18; + + // Test voucher data + bytes32 public constant VOUCHER_ID = keccak256("test-voucher-1"); + uint256 public constant VOUCHER_VALUE = 1000e18; + uint64 public voucherTimestamp; + uint64 public voucherExpiry; + + // ============ EVENTS ============ + + event Deposited( + address indexed buyer, address indexed seller, address indexed asset, uint256 amount, uint256 newBalance + ); + + event ThawInitiated( + address indexed buyer, + address indexed seller, + address indexed asset, + uint256 newThawingAmount, + uint256 previousThawingAmount, + uint256 newThawEndTime, + uint256 previousThawEndTime + ); + + event VoucherCollected( + bytes32 indexed voucherId, + address indexed buyer, + address indexed seller, + address asset, + uint256 amount, + uint256 totalCollected + ); + + event VoucherAlreadyCollected( + bytes32 indexed voucherId, address indexed buyer, address indexed seller, address asset, uint256 totalCollected + ); + + event VoucherNoCollectableBalance( + bytes32 indexed voucherId, + address indexed buyer, + address indexed seller, + address asset, + uint256 outstanding, + uint256 alreadyCollected + ); + + event DepositAuthorized( + address indexed buyer, address indexed seller, address indexed asset, uint256 amount, bytes32 nonce + ); + + event FlushAuthorized( + address indexed buyer, address indexed seller, address indexed asset, bytes32 nonce, bool thawing + ); + + event FlushAllAuthorized(address indexed buyer, bytes32 nonce, uint256 accountsFlushed); + + event Withdrawn( + address indexed buyer, address indexed seller, address indexed asset, uint256 amount, uint256 remainingBalance + ); + + function setUp() public virtual { + buyerFromPrivateKey = vm.addr(buyerPrivateKey); + voucherTimestamp = uint64(block.timestamp); + voucherExpiry = uint64(block.timestamp + 30 days); + + // Deploy mock tokens + usdc = new MockERC20("USD Coin", "USDC", 6); + usdt = new MockERC20("Tether USD", "USDT", 6); + smartWallet = new MockERC1271(); + + // Deploy escrow directly with constructor + escrow = new DeferredPaymentEscrow(THAWING_PERIOD); + + // Mint tokens to test accounts + usdc.mint(buyer, INITIAL_BALANCE); + usdc.mint(buyerFromPrivateKey, INITIAL_BALANCE); + usdt.mint(buyer, INITIAL_BALANCE); + usdt.mint(buyerFromPrivateKey, INITIAL_BALANCE); + + // Approve escrow to spend tokens + vm.startPrank(buyer); + usdc.approve(address(escrow), type(uint256).max); + usdt.approve(address(escrow), type(uint256).max); + vm.stopPrank(); + + vm.startPrank(buyerFromPrivateKey); + usdc.approve(address(escrow), type(uint256).max); + usdt.approve(address(escrow), type(uint256).max); + vm.stopPrank(); + } + + // ============ HELPER FUNCTIONS ============ + + function createVoucher( + bytes32 id, + address buyerAddr, + address sellerAddr, + uint256 value, + address asset, + uint64 timestamp, + uint256 nonce, + uint64 expiry + ) internal view returns (IDeferredPaymentEscrow.Voucher memory) { + return IDeferredPaymentEscrow.Voucher({ + id: id, + buyer: buyerAddr, + seller: sellerAddr, + valueAggregate: value, + asset: asset, + timestamp: timestamp, + nonce: nonce, + escrow: address(escrow), + chainId: block.chainid, + expiry: expiry + }); + } + + function signVoucher(IDeferredPaymentEscrow.Voucher memory voucher, uint256 privateKey) + internal + view + returns (bytes memory) + { + bytes32 structHash = keccak256( + abi.encode( + escrow.VOUCHER_TYPEHASH(), + voucher.id, + voucher.buyer, + voucher.seller, + voucher.valueAggregate, + voucher.asset, + voucher.timestamp, + voucher.nonce, + voucher.escrow, + voucher.chainId, + voucher.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signFlushAuthorization(IDeferredPaymentEscrow.FlushAuthorization memory auth, uint256 privateKey) + internal + view + returns (bytes memory) + { + bytes32 structHash = keccak256( + abi.encode( + keccak256("FlushAuthorization(address buyer,address seller,address asset,bytes32 nonce,uint64 expiry)"), + auth.buyer, + auth.seller, + auth.asset, + auth.nonce, + auth.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signFlushAllAuthorization(IDeferredPaymentEscrow.FlushAllAuthorization memory auth, uint256 privateKey) + internal + view + returns (bytes memory) + { + bytes32 structHash = keccak256( + abi.encode( + keccak256("FlushAllAuthorization(address buyer,bytes32 nonce,uint64 expiry)"), + auth.buyer, + auth.nonce, + auth.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } + + function signDepositAuthorization(IDeferredPaymentEscrow.DepositAuthorization memory auth, uint256 privateKey) + internal + view + returns (bytes memory) + { + bytes32 structHash = keccak256( + abi.encode( + keccak256( + "DepositAuthorization(address buyer,address seller,address asset,uint256 amount,bytes32 nonce,uint64 expiry)" + ), + auth.buyer, + auth.seller, + auth.asset, + auth.amount, + auth.nonce, + auth.expiry + ) + ); + + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + (uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest); + return abi.encodePacked(r, s, v); + } +} diff --git a/solidity/deferred-escrow/test/DepositTest.t.sol b/solidity/deferred-escrow/test/DepositTest.t.sol new file mode 100644 index 0000000000..b86482d747 --- /dev/null +++ b/solidity/deferred-escrow/test/DepositTest.t.sol @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {BaseTest} from "./BaseTest.sol"; +import {DeferredPaymentEscrow} from "../src/DeferredPaymentEscrow.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; + +contract DepositTest is BaseTest { + // ============ INITIALIZATION TESTS ============ + + function test_Initialize() public { + assertEq(escrow.THAWING_PERIOD(), THAWING_PERIOD); + } + + function test_Initialize_InvalidThawingPeriod() public { + uint256 invalidPeriod = 31 days; // Greater than MAX_THAWING_PERIOD (30 days) + vm.expectRevert( + abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidThawingPeriod.selector, invalidPeriod, 30 days) + ); + new DeferredPaymentEscrow(invalidPeriod); + } + + // ============ DEPOSIT TESTS ============ + + function test_Deposit() public { + uint256 amount = 1000e6; + + vm.expectEmit(true, true, true, true); + emit Deposited(buyer, seller, address(usdc), amount, amount); + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), amount); + + IDeferredPaymentEscrow.EscrowAccount memory account = escrow.getAccount(buyer, seller, address(usdc)); + assertEq(account.balance, amount); + assertEq(account.thawingAmount, 0); + assertEq(account.thawEndTime, 0); + assertEq(usdc.balanceOf(address(escrow)), amount); + } + + function test_Deposit_InvalidSeller() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.deposit(address(0), address(usdc), 1000e6); + } + + function test_Deposit_InvalidAsset() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.deposit(seller, address(0), 1000e6); + } + + function test_Deposit_ZeroAmount() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAmount.selector, 0)); + escrow.deposit(seller, address(usdc), 0); + } + + function test_DepositMany() public { + address seller2 = makeAddr("seller2"); + uint256 amount1 = 1000e6; + uint256 amount2 = 2000e6; + uint256 totalAmount = amount1 + amount2; + + IDeferredPaymentEscrow.DepositInput[] memory deposits = new IDeferredPaymentEscrow.DepositInput[](2); + deposits[0] = IDeferredPaymentEscrow.DepositInput({seller: seller, amount: amount1}); + deposits[1] = IDeferredPaymentEscrow.DepositInput({seller: seller2, amount: amount2}); + + vm.expectEmit(true, true, true, true); + emit Deposited(buyer, seller, address(usdc), amount1, amount1); + vm.expectEmit(true, true, true, true); + emit Deposited(buyer, seller2, address(usdc), amount2, amount2); + + vm.prank(buyer); + escrow.depositMany(address(usdc), deposits); + + assertEq(escrow.getAccount(buyer, seller, address(usdc)).balance, amount1); + assertEq(escrow.getAccount(buyer, seller2, address(usdc)).balance, amount2); + assertEq(usdc.balanceOf(address(escrow)), totalAmount); + } + + function test_DepositMany_InvalidAsset() public { + IDeferredPaymentEscrow.DepositInput[] memory deposits = new IDeferredPaymentEscrow.DepositInput[](1); + deposits[0] = IDeferredPaymentEscrow.DepositInput({seller: seller, amount: 1000e6}); + + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.depositMany(address(0), deposits); + } + + function test_DepositMany_EmptyDeposits() public { + IDeferredPaymentEscrow.DepositInput[] memory deposits = new IDeferredPaymentEscrow.DepositInput[](0); + + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.NoDepositsProvided.selector)); + escrow.depositMany(address(usdc), deposits); + } + + function test_DepositTo() public { + uint256 amount = 1000e6; + address beneficiary = address(0x1234); + + usdc.mint(address(this), amount); + usdc.approve(address(escrow), amount); + + // Deposit on behalf of beneficiary + vm.expectEmit(true, true, true, true); + emit Deposited(beneficiary, seller, address(usdc), amount, amount); + + escrow.depositTo(beneficiary, seller, address(usdc), amount); + + // Check that beneficiary owns the escrow, not msg.sender + assertEq(escrow.getAccount(beneficiary, seller, address(usdc)).balance, amount); + assertEq(escrow.getAccount(address(this), seller, address(usdc)).balance, 0); + assertEq(usdc.balanceOf(address(escrow)), amount); + } + + function test_DepositTo_InvalidBuyer() public { + uint256 amount = 1000e6; + + usdc.mint(address(this), amount); + usdc.approve(address(escrow), amount); + + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.depositTo(address(0), seller, address(usdc), amount); + } + + // ============ DEPOSIT AUTHORIZATION TESTS ============ + + function test_DepositWithAuthorization_Success() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("test-nonce-1"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create authorization + IDeferredPaymentEscrow.DepositAuthorization memory auth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + amount: depositAmount, + nonce: nonce, + expiry: expiry + }); + + // Sign authorization + bytes memory signature = signDepositAuthorization(auth, buyerPrivateKey); + + // Approve escrow + vm.prank(buyerFromPrivateKey); + usdc.approve(address(escrow), depositAmount); + + // Execute deposit + vm.expectEmit(true, true, true, true); + emit Deposited(buyerFromPrivateKey, seller, address(usdc), depositAmount, depositAmount); + vm.expectEmit(true, true, true, true); + emit DepositAuthorized(buyerFromPrivateKey, seller, address(usdc), depositAmount, nonce); + + escrow.depositWithAuthorization(auth, signature); + + // Verify state + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(account.balance, depositAmount); + } + + function test_DepositWithAuthorization_ExpiredReverts() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("test-nonce-2"); + uint64 expiry = uint64(block.timestamp - 1); // Already expired + + IDeferredPaymentEscrow.DepositAuthorization memory auth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + amount: depositAmount, + nonce: nonce, + expiry: expiry + }); + + // Sign authorization + bytes memory signature = signDepositAuthorization(auth, buyerPrivateKey); + + vm.expectRevert( + abi.encodeWithSelector(IDeferredPaymentEscrow.AuthorizationExpired.selector, expiry, block.timestamp) + ); + escrow.depositWithAuthorization(auth, signature); + } + + function test_DepositWithAuthorization_NonceReplayReverts() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("test-nonce-3"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + IDeferredPaymentEscrow.DepositAuthorization memory auth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + amount: depositAmount, + nonce: nonce, + expiry: expiry + }); + + // Sign authorization + bytes memory signature = signDepositAuthorization(auth, buyerPrivateKey); + + // Approve and execute first deposit + vm.prank(buyerFromPrivateKey); + usdc.approve(address(escrow), depositAmount * 2); + escrow.depositWithAuthorization(auth, signature); + + // Try to replay the same authorization + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.NonceAlreadyUsed.selector, nonce)); + escrow.depositWithAuthorization(auth, signature); + } + + function test_DepositWithAuthorization_InvalidSignatureReverts() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("test-nonce-4"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + IDeferredPaymentEscrow.DepositAuthorization memory auth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + amount: depositAmount, + nonce: nonce, + expiry: expiry + }); + + // Sign with wrong private key + uint256 wrongPrivateKey = 0x54321; + bytes memory signature = signDepositAuthorization(auth, wrongPrivateKey); + + vm.expectRevert(IDeferredPaymentEscrow.InvalidAuthorization.selector); + escrow.depositWithAuthorization(auth, signature); + } + + function test_DepositWithAuthorization_SmartWalletSignature() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("test-nonce-5"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Fund smart wallet + usdc.mint(address(smartWallet), depositAmount); + vm.prank(address(smartWallet)); + usdc.approve(address(escrow), depositAmount); + + IDeferredPaymentEscrow.DepositAuthorization memory auth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: address(smartWallet), + seller: seller, + asset: address(usdc), + amount: depositAmount, + nonce: nonce, + expiry: expiry + }); + + // Create digest for smart wallet using helper function + bytes memory signature = signDepositAuthorization(auth, buyerPrivateKey); + bytes32 structHash = keccak256( + abi.encode( + keccak256( + "DepositAuthorization(address buyer,address seller,address asset,uint256 amount,bytes32 nonce,uint64 expiry)" + ), + auth.buyer, + auth.seller, + auth.asset, + auth.amount, + auth.nonce, + auth.expiry + ) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + // Set the smart wallet to accept this digest + smartWallet.setValidHash(digest, true); + signature = hex"1234"; // Dummy signature, smart wallet will validate + + // Execute deposit + escrow.depositWithAuthorization(auth, signature); + + // Verify state + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(address(smartWallet), seller, address(usdc)); + assertEq(account.balance, depositAmount); + } +} diff --git a/solidity/deferred-escrow/test/ThawWithdrawTest.t.sol b/solidity/deferred-escrow/test/ThawWithdrawTest.t.sol new file mode 100644 index 0000000000..1f7e9589c2 --- /dev/null +++ b/solidity/deferred-escrow/test/ThawWithdrawTest.t.sol @@ -0,0 +1,594 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {BaseTest} from "./BaseTest.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; + +contract ThawWithdrawTest is BaseTest { + // ============ THAWING TESTS ============ + + function test_Thaw() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 500e6; + + // First deposit + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + // Then thaw + vm.expectEmit(true, true, true, true); + emit ThawInitiated( + buyer, + seller, + address(usdc), + thawAmount, + 0, // previousThawingAmount + uint256(block.timestamp + THAWING_PERIOD), + 0 // previousThawEndTime + ); + + vm.prank(buyer); + escrow.thaw(seller, address(usdc), thawAmount); + + IDeferredPaymentEscrow.EscrowAccount memory account = escrow.getAccount(buyer, seller, address(usdc)); + assertEq(account.balance, depositAmount); // Balance unchanged in new model + assertEq(account.thawingAmount, thawAmount); + assertEq(account.thawEndTime, block.timestamp + THAWING_PERIOD); + } + + function test_Thaw_InsufficientBalance() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 2000e6; + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InsufficientBalance.selector, 1000e6, 2000e6)); + escrow.thaw(seller, address(usdc), thawAmount); + } + + function test_Thaw_IncreaseAmount() public { + uint256 depositAmount = 1000e6; + uint256 firstThawAmount = 300e6; + uint256 additionalThawAmount = 200e6; + uint256 totalThawAmount = 500e6; + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + // First thaw + vm.prank(buyer); + escrow.thaw(seller, address(usdc), firstThawAmount); + + uint256 firstThawEndTime = block.timestamp + THAWING_PERIOD; + + // Move forward in time + vm.warp(block.timestamp + 100); + + // Second thaw should increase amount and reset timer + vm.expectEmit(true, true, true, true); + emit ThawInitiated( + buyer, + seller, + address(usdc), + totalThawAmount, + firstThawAmount, + uint256(vm.getBlockTimestamp() + THAWING_PERIOD), // Use vm.getBlockTimestamp() with --via-ir + firstThawEndTime + ); + + vm.prank(buyer); + escrow.thaw(seller, address(usdc), additionalThawAmount); + + IDeferredPaymentEscrow.EscrowAccount memory account = escrow.getAccount(buyer, seller, address(usdc)); + assertEq(account.balance, depositAmount); + assertEq(account.thawingAmount, totalThawAmount); + assertEq(account.thawEndTime, 101 + THAWING_PERIOD); // Timer reset after warp to timestamp 101 + } + + function test_Thaw_IncreaseAmount_InsufficientBalance() public { + uint256 depositAmount = 1000e6; + uint256 firstThawAmount = 700e6; + uint256 additionalThawAmount = 400e6; // Would total 1100e6, exceeding balance + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + // First thaw + vm.prank(buyer); + escrow.thaw(seller, address(usdc), firstThawAmount); + + // Second thaw should revert due to insufficient balance + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InsufficientBalance.selector, 1000e6, 1100e6)); + escrow.thaw(seller, address(usdc), additionalThawAmount); + } + + function test_CancelThaw() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 500e6; + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + vm.prank(buyer); + escrow.thaw(seller, address(usdc), thawAmount); + + vm.prank(buyer); + escrow.cancelThaw(seller, address(usdc)); + + IDeferredPaymentEscrow.EscrowAccount memory account = escrow.getAccount(buyer, seller, address(usdc)); + assertEq(account.balance, depositAmount); + assertEq(account.thawingAmount, 0); + assertEq(account.thawEndTime, 0); + } + + function test_Withdraw() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 500e6; + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + vm.prank(buyer); + escrow.thaw(seller, address(usdc), thawAmount); + + // Fast forward past thawing period + vm.warp(block.timestamp + THAWING_PERIOD + 1); + + uint256 balanceBefore = usdc.balanceOf(buyer); + + vm.prank(buyer); + escrow.withdraw(seller, address(usdc)); + + assertEq(usdc.balanceOf(buyer), balanceBefore + thawAmount); + + IDeferredPaymentEscrow.EscrowAccount memory account = escrow.getAccount(buyer, seller, address(usdc)); + assertEq(account.balance, depositAmount - thawAmount); + assertEq(account.thawingAmount, 0); + assertEq(account.thawEndTime, 0); + } + + function test_Withdraw_ThawingNotCompleted() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 500e6; + + vm.prank(buyer); + escrow.deposit(seller, address(usdc), depositAmount); + + vm.prank(buyer); + escrow.thaw(seller, address(usdc), thawAmount); + + uint256 thawEndTime = block.timestamp + THAWING_PERIOD; + + // Try to withdraw before thawing period completes + vm.prank(buyer); + vm.expectRevert( + abi.encodeWithSelector( + IDeferredPaymentEscrow.ThawingPeriodNotCompleted.selector, block.timestamp, thawEndTime + ) + ); + escrow.withdraw(seller, address(usdc)); + } + + // ============ INPUT VALIDATION TESTS ============ + + function test_Thaw_InvalidSeller() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.thaw(address(0), address(usdc), 1000e6); + } + + function test_Thaw_InvalidAsset() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.thaw(seller, address(0), 1000e6); + } + + function test_Thaw_ZeroAmount() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAmount.selector, 0)); + escrow.thaw(seller, address(usdc), 0); + } + + function test_CancelThaw_InvalidSeller() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.cancelThaw(address(0), address(usdc)); + } + + function test_CancelThaw_InvalidAsset() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.cancelThaw(seller, address(0)); + } + + function test_Withdraw_InvalidSeller() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.withdraw(address(0), address(usdc)); + } + + function test_Withdraw_InvalidAsset() public { + vm.prank(buyer); + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.withdraw(seller, address(0)); + } + + // ============ FLUSH AUTHORIZATION TESTS ============ + + function test_FlushWithAuthorization_WithdrawReady() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("flush-nonce-1"); + + // Setup: deposit and thaw funds using buyerFromPrivateKey + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + escrow.thaw(seller, address(usdc), depositAmount); + vm.stopPrank(); + + // Warp to after thawing period + vm.warp(block.timestamp + THAWING_PERIOD + 1); + + // Set expiry AFTER warping to avoid expiry issues + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush authorization + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + // Sign with buyer's private key using helper + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // Execute flush - should withdraw + uint256 balanceBefore = usdc.balanceOf(buyerFromPrivateKey); + + // Expect both Withdrawn and FlushAuthorized events + vm.expectEmit(true, true, true, true); + emit Withdrawn(buyerFromPrivateKey, seller, address(usdc), depositAmount, 0); + vm.expectEmit(true, true, true, false); + emit FlushAuthorized(buyerFromPrivateKey, seller, address(usdc), nonce, false); // false = no thawing + + escrow.flushWithAuthorization(auth, signature); + + // Verify funds were withdrawn + assertEq(usdc.balanceOf(buyerFromPrivateKey), balanceBefore + depositAmount); + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(account.balance, 0); + assertEq(account.thawingAmount, 0); + } + + function test_FlushWithAuthorization_NonceReplayReverts() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("flush-nonce-replay"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Setup: deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create flush authorization + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + // Sign authorization + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // Execute first flush + escrow.flushWithAuthorization(auth, signature); + + // Try to replay the same authorization + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.NonceAlreadyUsed.selector, nonce)); + escrow.flushWithAuthorization(auth, signature); + } + + function test_FlushWithAuthorization_ThawNotReady() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("flush-nonce-2"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Setup: deposit funds (not thawed) + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create flush authorization + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + // Sign with buyer's private key + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // Execute flush - should initiate thaw + vm.expectEmit(true, true, true, false); + emit FlushAuthorized(buyerFromPrivateKey, seller, address(usdc), nonce, true); // true = thawing initiated + + escrow.flushWithAuthorization(auth, signature); + + // Verify thaw was initiated + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(account.balance, depositAmount); + assertEq(account.thawingAmount, depositAmount); + assert(account.thawEndTime > block.timestamp); + } + + function test_FlushAllWithAuthorization_EmptyAccountSet() public { + bytes32 nonce = keccak256("flush-all-empty"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush all authorization without any accounts + IDeferredPaymentEscrow.FlushAllAuthorization memory auth = + IDeferredPaymentEscrow.FlushAllAuthorization({buyer: buyerFromPrivateKey, nonce: nonce, expiry: expiry}); + + // Sign authorization + bytes memory signature = signFlushAllAuthorization(auth, buyerPrivateKey); + + // Execute flush all - should work but do nothing + vm.expectEmit(true, false, false, true); + emit FlushAllAuthorized(buyerFromPrivateKey, nonce, 0); // 0 accounts flushed + + escrow.flushAllWithAuthorization(auth, signature); + } + + function test_FlushAllWithAuthorization_NonceReplayReverts() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("flush-all-replay"); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Setup: deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create flush all authorization + IDeferredPaymentEscrow.FlushAllAuthorization memory auth = + IDeferredPaymentEscrow.FlushAllAuthorization({buyer: buyerFromPrivateKey, nonce: nonce, expiry: expiry}); + + // Sign authorization + bytes memory signature = signFlushAllAuthorization(auth, buyerPrivateKey); + + // Execute first flush all + escrow.flushAllWithAuthorization(auth, signature); + + // Try to replay the same authorization + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.NonceAlreadyUsed.selector, nonce)); + escrow.flushAllWithAuthorization(auth, signature); + } + + function test_FlushWithAuthorization_Idempotent() public { + uint256 depositAmount = 1000e6; + bytes32 nonce = keccak256("flush-idempotent"); + + // Setup: deposit and thaw funds + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + escrow.thaw(seller, address(usdc), depositAmount); + vm.stopPrank(); + + // Warp to after thawing period + vm.warp(block.timestamp + THAWING_PERIOD + 1); + + // Set expiry AFTER warping to avoid expiry issues + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush authorization with updated expiry + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // First flush - should withdraw + escrow.flushWithAuthorization(auth, signature); + + // Second flush with same nonce should fail (nonce replay protection) + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.NonceAlreadyUsed.selector, nonce)); + escrow.flushWithAuthorization(auth, signature); + } + + function test_FlushAllWithAuthorization_MultipleAccounts() public { + bytes32 nonce = keccak256("flush-all-nonce-1"); + + // Setup multiple escrow accounts + address seller2 = makeAddr("seller2"); + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), 1000e6); + escrow.deposit(seller2, address(usdc), 500e6); + escrow.deposit(seller, address(usdt), 800e6); // Different asset + + // Thaw some funds + escrow.thaw(seller, address(usdc), 1000e6); + vm.stopPrank(); + + // Warp to after thawing + vm.warp(block.timestamp + THAWING_PERIOD + 1); + + // Set expiry AFTER warping to avoid expiry issues + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush all authorization + IDeferredPaymentEscrow.FlushAllAuthorization memory auth = + IDeferredPaymentEscrow.FlushAllAuthorization({buyer: buyerFromPrivateKey, nonce: nonce, expiry: expiry}); + + // Sign with buyer's private key + bytes memory signature = signFlushAllAuthorization(auth, buyerPrivateKey); + + // Execute flush all + uint256 usdcBalanceBefore = usdc.balanceOf(buyerFromPrivateKey); + vm.expectEmit(true, false, false, true); + emit FlushAllAuthorized(buyerFromPrivateKey, nonce, 3); // 3 accounts affected + + escrow.flushAllWithAuthorization(auth, signature); + + // Verify results + assertEq(usdc.balanceOf(buyerFromPrivateKey), usdcBalanceBefore + 1000e6); // Withdrawn from ready account + + // Check that other accounts had thawing initiated + IDeferredPaymentEscrow.EscrowAccount memory account2 = + escrow.getAccount(buyerFromPrivateKey, seller2, address(usdc)); + assertEq(account2.thawingAmount, 500e6); // Should have thaw initiated + + IDeferredPaymentEscrow.EscrowAccount memory account3 = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdt)); + assertEq(account3.thawingAmount, 800e6); // Should have thaw initiated + } + + function test_FlushWithAuthorization_WithdrawAndThaw() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 400e6; + bytes32 nonce = keccak256("flush-nonce-3"); + + // Setup: deposit, partially thaw + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + escrow.thaw(seller, address(usdc), thawAmount); + vm.stopPrank(); + + // Warp to after thawing period + vm.warp(block.timestamp + THAWING_PERIOD + 1); + + // Set expiry AFTER warping to avoid expiry issues + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush authorization + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + // Sign with buyer's private key + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // Execute flush - should withdraw ready funds AND thaw remaining + uint256 balanceBefore = usdc.balanceOf(buyerFromPrivateKey); + vm.expectEmit(true, true, true, false); + emit FlushAuthorized(buyerFromPrivateKey, seller, address(usdc), nonce, true); // true = also thawed remaining + + escrow.flushWithAuthorization(auth, signature); + + // Verify: withdrew 400, thawed remaining 600 + assertEq(usdc.balanceOf(buyerFromPrivateKey), balanceBefore + thawAmount); + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(account.balance, depositAmount - thawAmount); + assertEq(account.thawingAmount, depositAmount - thawAmount); + } + + function test_FlushWithAuthorization_ResetsThawTimer() public { + uint256 depositAmount = 1000e6; + uint256 thawAmount = 600e6; + bytes32 nonce = keccak256("flush-reset-timer"); + + // Setup: deposit and thaw funds + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + escrow.thaw(seller, address(usdc), thawAmount); + vm.stopPrank(); + + uint256 originalThawEndTime = escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)).thawEndTime; + + // Wait some time but not full thawing period + vm.warp(block.timestamp + THAWING_PERIOD / 2); + + // Set expiry + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush authorization + IDeferredPaymentEscrow.FlushAuthorization memory auth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: nonce, + expiry: expiry + }); + + // Sign authorization + bytes memory signature = signFlushAuthorization(auth, buyerPrivateKey); + + // Execute flush - should thaw remaining balance and reset timer + escrow.flushWithAuthorization(auth, signature); + + // Verify timer was reset + IDeferredPaymentEscrow.EscrowAccount memory account = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + uint256 newThawEndTime = account.thawEndTime; + + // New timer should be later than original (reset to full period from current time) + assert(newThawEndTime > originalThawEndTime); + assertEq(account.thawingAmount, depositAmount); // All funds now thawing + } + + function test_FlushAllWithAuthorization_MultipleAssets_ComplexScenario() public { + bytes32 nonce = keccak256("flush-all-complex"); + + // Setup complex scenario with multiple sellers and assets + address seller2 = makeAddr("seller2"); + uint256 usdcAmount1 = 1000e6; + uint256 usdcAmount2 = 500e6; + uint256 usdtAmount = 800e6; + + vm.startPrank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), usdcAmount1); + escrow.deposit(seller2, address(usdc), usdcAmount2); + escrow.deposit(seller, address(usdt), usdtAmount); + + // Thaw funds from first account only + escrow.thaw(seller, address(usdc), usdcAmount1); + vm.stopPrank(); + + // Warp to after thawing period + vm.warp(block.timestamp + THAWING_PERIOD + 1); + uint64 expiry = uint64(block.timestamp + 1 hours); + + // Create flush all authorization + IDeferredPaymentEscrow.FlushAllAuthorization memory auth = + IDeferredPaymentEscrow.FlushAllAuthorization({buyer: buyerFromPrivateKey, nonce: nonce, expiry: expiry}); + + bytes memory signature = signFlushAllAuthorization(auth, buyerPrivateKey); + + // Execute flush all + uint256 usdcBalanceBefore = usdc.balanceOf(buyerFromPrivateKey); + uint256 usdtBalanceBefore = usdt.balanceOf(buyerFromPrivateKey); + + vm.expectEmit(true, false, false, true); + emit FlushAllAuthorized(buyerFromPrivateKey, nonce, 3); // 3 accounts affected + + escrow.flushAllWithAuthorization(auth, signature); + + // Verify withdrawn from ready account + assertEq(usdc.balanceOf(buyerFromPrivateKey), usdcBalanceBefore + usdcAmount1); + assertEq(usdt.balanceOf(buyerFromPrivateKey), usdtBalanceBefore); // No withdrawal from USDT + + // Verify thawing initiated for other accounts + IDeferredPaymentEscrow.EscrowAccount memory account2 = + escrow.getAccount(buyerFromPrivateKey, seller2, address(usdc)); + assertEq(account2.thawingAmount, usdcAmount2); + + IDeferredPaymentEscrow.EscrowAccount memory account3 = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdt)); + assertEq(account3.thawingAmount, usdtAmount); + } +} diff --git a/solidity/deferred-escrow/test/TokenCompatibilityTest.t.sol b/solidity/deferred-escrow/test/TokenCompatibilityTest.t.sol new file mode 100644 index 0000000000..41f18c8259 --- /dev/null +++ b/solidity/deferred-escrow/test/TokenCompatibilityTest.t.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {BaseTest} from "./BaseTest.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; +import {MockERC20} from "./mocks/MockERC20.sol"; +import {MockNonStandardERC20} from "./mocks/MockNonStandardERC20.sol"; + +contract TokenCompatibilityTest is BaseTest { + // ============ TOKEN COMPATIBILITY TESTS ============ + + function test_DifferentTokenDecimals() public { + // Create tokens with different decimals + MockERC20 token6 = new MockERC20("USDC", "USDC", 6); // 6 decimals like real USDC + MockERC20 token18 = new MockERC20("DAI", "DAI", 18); // 18 decimals like DAI + MockERC20 token8 = new MockERC20("WBTC", "WBTC", 8); // 8 decimals like WBTC + + // Mint tokens with appropriate amounts for each decimal + uint256 amount6 = 1000e6; // 1000 USDC + uint256 amount18 = 500e18; // 500 DAI + uint256 amount8 = 1e8; // 1 WBTC + + token6.mint(buyer, amount6); + token18.mint(buyer, amount18); + token8.mint(buyer, amount8); + + // Approve escrow + vm.startPrank(buyer); + token6.approve(address(escrow), amount6); + token18.approve(address(escrow), amount18); + token8.approve(address(escrow), amount8); + + // Deposit all tokens to same seller + escrow.deposit(seller, address(token6), amount6); + escrow.deposit(seller, address(token18), amount18); + escrow.deposit(seller, address(token8), amount8); + vm.stopPrank(); + + // Verify all deposits + assertEq(escrow.getAccount(buyer, seller, address(token6)).balance, amount6); + assertEq(escrow.getAccount(buyer, seller, address(token18)).balance, amount18); + assertEq(escrow.getAccount(buyer, seller, address(token8)).balance, amount8); + + // Create vouchers for each token + bytes32 voucher6Id = keccak256("voucher-usdc"); + bytes32 voucher18Id = keccak256("voucher-dai"); + bytes32 voucher8Id = keccak256("voucher-wbtc"); + + IDeferredPaymentEscrow.Voucher memory voucher6 = + createVoucher(voucher6Id, buyer, seller, amount6, address(token6), voucherTimestamp, 1, voucherExpiry); + IDeferredPaymentEscrow.Voucher memory voucher18 = + createVoucher(voucher18Id, buyer, seller, amount18, address(token18), voucherTimestamp, 1, voucherExpiry); + IDeferredPaymentEscrow.Voucher memory voucher8 = + createVoucher(voucher8Id, buyer, seller, amount8, address(token8), voucherTimestamp, 1, voucherExpiry); + + // We'll use buyer's address directly (not buyerFromPrivateKey) for this test + // So we need to get the buyer's private key from the test framework + uint256 buyerPk = uint256(keccak256(abi.encodePacked("buyer"))); + + bytes memory signature6 = signVoucher(voucher6, buyerPk); + bytes memory signature18 = signVoucher(voucher18, buyerPk); + bytes memory signature8 = signVoucher(voucher8, buyerPk); + + uint256 sellerBalance6Before = token6.balanceOf(seller); + uint256 sellerBalance18Before = token18.balanceOf(seller); + uint256 sellerBalance8Before = token8.balanceOf(seller); + + // Collect all vouchers + vm.startPrank(seller); + escrow.collect(voucher6, signature6); + escrow.collect(voucher18, signature18); + escrow.collect(voucher8, signature8); + vm.stopPrank(); + + // Verify collections + assertEq(token6.balanceOf(seller), sellerBalance6Before + amount6); + assertEq(token18.balanceOf(seller), sellerBalance18Before + amount18); + assertEq(token8.balanceOf(seller), sellerBalance8Before + amount8); + } + + function test_NonStandardERC20Token() public { + MockNonStandardERC20 nonStandardToken = new MockNonStandardERC20("Non-Standard", "NST", 18); + uint256 depositAmount = 1000e18; + + // Mint tokens to buyer + nonStandardToken.mint(buyer, depositAmount); + + // Approve and deposit + vm.startPrank(buyer); + nonStandardToken.approve(address(escrow), depositAmount); + escrow.deposit(seller, address(nonStandardToken), depositAmount); + vm.stopPrank(); + + // Verify deposit + assertEq(escrow.getAccount(buyer, seller, address(nonStandardToken)).balance, depositAmount); + + // Create and collect voucher + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyer, seller, VOUCHER_VALUE, address(nonStandardToken), voucherTimestamp, 1, voucherExpiry + ); + + uint256 buyerPk = uint256(keccak256(abi.encodePacked("buyer"))); + bytes memory signature = signVoucher(voucher, buyerPk); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // Verify collection worked with non-standard token + assertEq(nonStandardToken.balanceOf(seller), VOUCHER_VALUE); + } + + function test_Collect_ERC1271() public { + uint256 depositAmount = 1000e18; + + // Mint tokens to smart wallet + usdc.mint(address(smartWallet), depositAmount); + + // Smart wallet approves escrow + vm.prank(address(smartWallet)); + usdc.approve(address(escrow), depositAmount); + + // Smart wallet deposits + vm.prank(address(smartWallet)); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create voucher for smart wallet + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, address(smartWallet), seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + // Create digest for smart wallet validation + bytes32 structHash = keccak256( + abi.encode( + escrow.VOUCHER_TYPEHASH(), + voucher.id, + voucher.buyer, + voucher.seller, + voucher.valueAggregate, + voucher.asset, + voucher.timestamp, + voucher.nonce, + voucher.escrow, + voucher.chainId, + voucher.expiry + ) + ); + bytes32 digest = keccak256(abi.encodePacked("\x19\x01", escrow.DOMAIN_SEPARATOR(), structHash)); + + // Set smart wallet to accept this digest + smartWallet.setValidHash(digest, true); + bytes memory signature = abi.encodePacked("valid"); + + vm.prank(seller); + escrow.collect(voucher, signature); + + assertEq(escrow.getVoucherCollected(address(smartWallet), seller, address(usdc), VOUCHER_ID), VOUCHER_VALUE); + } + + function test_DepositMany_DifferentTokenDecimals() public { + // Create tokens with different decimals + MockERC20 token6 = new MockERC20("USDC", "USDC", 6); + MockERC20 token18 = new MockERC20("DAI", "DAI", 18); + + // Test amounts appropriate for each decimal + uint256 amount6 = 1000e6; // 1000 USDC + uint256 amount18 = 500e18; // 500 DAI + + // Test with 6-decimal token + token6.mint(buyer, amount6 * 3); + vm.startPrank(buyer); + token6.approve(address(escrow), amount6 * 3); + + address seller2 = makeAddr("seller2"); + address seller3 = makeAddr("seller3"); + + IDeferredPaymentEscrow.DepositInput[] memory deposits6 = new IDeferredPaymentEscrow.DepositInput[](3); + deposits6[0] = IDeferredPaymentEscrow.DepositInput({seller: seller, amount: amount6}); + deposits6[1] = IDeferredPaymentEscrow.DepositInput({seller: seller2, amount: amount6}); + deposits6[2] = IDeferredPaymentEscrow.DepositInput({seller: seller3, amount: amount6}); + + escrow.depositMany(address(token6), deposits6); + vm.stopPrank(); + + // Test with 18-decimal token + token18.mint(buyer, amount18 * 2); + vm.startPrank(buyer); + token18.approve(address(escrow), amount18 * 2); + + IDeferredPaymentEscrow.DepositInput[] memory deposits18 = new IDeferredPaymentEscrow.DepositInput[](2); + deposits18[0] = IDeferredPaymentEscrow.DepositInput({seller: seller, amount: amount18}); + deposits18[1] = IDeferredPaymentEscrow.DepositInput({seller: seller2, amount: amount18}); + + escrow.depositMany(address(token18), deposits18); + vm.stopPrank(); + + // Verify all deposits + assertEq(escrow.getAccount(buyer, seller, address(token6)).balance, amount6); + assertEq(escrow.getAccount(buyer, seller2, address(token6)).balance, amount6); + assertEq(escrow.getAccount(buyer, seller3, address(token6)).balance, amount6); + assertEq(escrow.getAccount(buyer, seller, address(token18)).balance, amount18); + assertEq(escrow.getAccount(buyer, seller2, address(token18)).balance, amount18); + } +} diff --git a/solidity/deferred-escrow/test/ViewFunctionTest.t.sol b/solidity/deferred-escrow/test/ViewFunctionTest.t.sol new file mode 100644 index 0000000000..943bd6b8a4 --- /dev/null +++ b/solidity/deferred-escrow/test/ViewFunctionTest.t.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {BaseTest} from "./BaseTest.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; + +contract ViewFunctionTest is BaseTest { + // ============ VIEW FUNCTION TESTS ============ + + function test_GetOutstandingAndCollectableAmount() public { + uint256 depositAmount = 1000e18; + uint256 voucherAmount = 2000e18; + + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + (uint256 outstanding, uint256 collectable) = escrow.getOutstandingAndCollectableAmount(voucher); + assertEq(outstanding, voucherAmount); // Full voucher amount is outstanding + assertEq(collectable, depositAmount); // Limited by balance + } + + function test_GetOutstandingAndCollectableAmount_PartialBalance() public { + uint256 depositAmount = 500e18; + uint256 voucherAmount = 1000e18; + + // Deposit partial amount + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + (uint256 outstanding, uint256 collectable) = escrow.getOutstandingAndCollectableAmount(voucher); + assertEq(outstanding, voucherAmount); + assertEq(collectable, depositAmount); // Limited by available balance + } + + function test_GetOutstandingAndCollectableAmount_FullyCollected() public { + uint256 depositAmount = 1000e18; + + // Deposit and collect + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // Check after collection + (uint256 outstanding, uint256 collectable) = escrow.getOutstandingAndCollectableAmount(voucher); + assertEq(outstanding, 0); + assertEq(collectable, 0); + } + + function test_IsSignatureValid() public { + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + assertTrue(escrow.isVoucherSignatureValid(voucher, signature)); + + bytes memory invalidSignature = abi.encodePacked(uint256(123), uint256(456), uint8(27)); + assertFalse(escrow.isVoucherSignatureValid(voucher, invalidSignature)); + } + + function test_AlternativeViewFunctions() public { + // Test all the signature validation functions + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory voucherSignature = signVoucher(voucher, buyerPrivateKey); + assertTrue(escrow.isVoucherSignatureValid(voucher, voucherSignature)); + + // Test deposit authorization validation + IDeferredPaymentEscrow.DepositAuthorization memory depositAuth = IDeferredPaymentEscrow.DepositAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + amount: 1000e6, + nonce: keccak256("deposit-nonce"), + expiry: voucherExpiry + }); + + bytes memory depositSignature = signDepositAuthorization(depositAuth, buyerPrivateKey); + assertTrue(escrow.isDepositAuthorizationValid(depositAuth, depositSignature)); + + // Test flush authorization validation + IDeferredPaymentEscrow.FlushAuthorization memory flushAuth = IDeferredPaymentEscrow.FlushAuthorization({ + buyer: buyerFromPrivateKey, + seller: seller, + asset: address(usdc), + nonce: keccak256("flush-nonce"), + expiry: voucherExpiry + }); + + bytes memory flushSignature = signFlushAuthorization(flushAuth, buyerPrivateKey); + assertTrue(escrow.isFlushAuthorizationValid(flushAuth, flushSignature)); + + // Test flush all authorization validation + IDeferredPaymentEscrow.FlushAllAuthorization memory flushAllAuth = IDeferredPaymentEscrow.FlushAllAuthorization({ + buyer: buyerFromPrivateKey, + nonce: keccak256("flush-all-nonce"), + expiry: voucherExpiry + }); + + bytes memory flushAllSignature = signFlushAllAuthorization(flushAllAuth, buyerPrivateKey); + assertTrue(escrow.isFlushAllAuthorizationValid(flushAllAuth, flushAllSignature)); + } +} diff --git a/solidity/deferred-escrow/test/VoucherCollectionTest.t.sol b/solidity/deferred-escrow/test/VoucherCollectionTest.t.sol new file mode 100644 index 0000000000..f3f6a77052 --- /dev/null +++ b/solidity/deferred-escrow/test/VoucherCollectionTest.t.sol @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {BaseTest} from "./BaseTest.sol"; +import {IDeferredPaymentEscrow} from "../src/IDeferredPaymentEscrow.sol"; + +contract VoucherCollectionTest is BaseTest { + // ============ VOUCHER COLLECTION TESTS ============ + + function test_Collect() public { + uint256 depositAmount = 2000e18; + uint256 collectAmount = 1000e18; + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create and sign voucher + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, collectAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.expectEmit(true, true, true, true); + emit VoucherCollected(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), collectAmount, collectAmount); + + uint256 sellerBalanceBefore = usdc.balanceOf(seller); + + vm.prank(seller); + escrow.collect(voucher, signature); + + assertEq(usdc.balanceOf(seller), sellerBalanceBefore + collectAmount); // Seller gets full amount + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), collectAmount); + } + + function test_Collect_AdjustsThawingAmount() public { + uint256 depositAmount = 1000e18; + uint256 thawAmount = 800e18; + uint256 voucherAmount = 600e18; + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Start thawing most of the funds + vm.prank(buyerFromPrivateKey); + escrow.thaw(seller, address(usdc), thawAmount); + + // Create voucher that will bring balance below thawing amount + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // Collect + vm.prank(seller); + escrow.collect(voucher, signature); + + // Check that thawing amount was adjusted down + IDeferredPaymentEscrow.EscrowAccount memory accountAfter = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(accountAfter.balance, 400e18); // 1000e18 - 600e18 + assertEq(accountAfter.thawingAmount, 400e18); // Adjusted down to match balance + } + + function test_Collect_FullBalanceAvailableDuringThaw() public { + uint256 depositAmount = 1000e18; + uint256 thawAmount = 800e18; + uint256 voucherAmount = 900e18; // More than thawing amount + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Start thawing most of the funds + vm.prank(buyerFromPrivateKey); + escrow.thaw(seller, address(usdc), thawAmount); + + // Verify initial state - balance unchanged, thawing active + IDeferredPaymentEscrow.EscrowAccount memory accountBefore = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(accountBefore.balance, 1000e18); + assertEq(accountBefore.thawingAmount, 800e18); + + // Create voucher for amount greater than thawing (but less than balance) + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // Collect should succeed - full balance is available + vm.prank(seller); + escrow.collect(voucher, signature); + + // Verify collection succeeded and thawing was adjusted + IDeferredPaymentEscrow.EscrowAccount memory accountAfter = + escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)); + assertEq(accountAfter.balance, 100e18); // 1000e18 - 900e18 + assertEq(accountAfter.thawingAmount, 100e18); // Adjusted to match remaining balance + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), voucherAmount); + } + + function test_Collect_PartialCollection() public { + uint256 depositAmount = 500e18; + uint256 voucherAmount = 1000e18; + + // Deposit less than voucher amount + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // Should only collect the available balance + vm.expectEmit(true, true, true, true); + emit VoucherCollected(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), depositAmount, depositAmount); + + vm.prank(seller); + escrow.collect(voucher, signature); + + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), depositAmount); + assertEq(escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)).balance, 0); + } + + function test_Collect_PermissionlessCollection() public { + uint256 depositAmount = 1000e18; + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // Anyone can collect on behalf of the seller + address randomUser = makeAddr("randomUser"); + vm.prank(randomUser); + escrow.collect(voucher, signature); + + // Seller still gets the funds + assertEq(usdc.balanceOf(seller), VOUCHER_VALUE); + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), VOUCHER_VALUE); + } + + function test_Collect_IdempotentBehavior() public { + uint256 depositAmount = 1000e18; + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // First collection + vm.prank(seller); + escrow.collect(voucher, signature); + + uint256 sellerBalanceAfterFirst = usdc.balanceOf(seller); + + // Second collection - should emit VoucherAlreadyCollected and do nothing + vm.expectEmit(true, true, true, true); + emit VoucherAlreadyCollected(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), VOUCHER_VALUE); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // No additional transfer + assertEq(usdc.balanceOf(seller), sellerBalanceAfterFirst); + } + + function test_CollectMany_IdempotentBehavior() public { + uint256 depositAmount = 2000e18; + + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + // Create multiple vouchers + bytes32 voucher1Id = keccak256("voucher-1"); + bytes32 voucher2Id = keccak256("voucher-2"); + + IDeferredPaymentEscrow.Voucher memory voucher1 = createVoucher( + voucher1Id, buyerFromPrivateKey, seller, 800e18, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + IDeferredPaymentEscrow.Voucher memory voucher2 = createVoucher( + voucher2Id, buyerFromPrivateKey, seller, 700e18, address(usdc), voucherTimestamp, 2, voucherExpiry + ); + + IDeferredPaymentEscrow.SignedVoucher[] memory signedVouchers = new IDeferredPaymentEscrow.SignedVoucher[](2); + signedVouchers[0] = + IDeferredPaymentEscrow.SignedVoucher({voucher: voucher1, signature: signVoucher(voucher1, buyerPrivateKey)}); + signedVouchers[1] = + IDeferredPaymentEscrow.SignedVoucher({voucher: voucher2, signature: signVoucher(voucher2, buyerPrivateKey)}); + + // First batch collection + vm.prank(seller); + escrow.collectMany(signedVouchers); + + uint256 sellerBalanceAfterFirst = usdc.balanceOf(seller); + + // Second batch collection - should be idempotent + vm.prank(seller); + escrow.collectMany(signedVouchers); + + // No additional transfer + assertEq(usdc.balanceOf(seller), sellerBalanceAfterFirst); + } + + // ============ COLLECTION VALIDATION TESTS ============ + + function test_Collect_ExpiredVoucher() public { + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), 1000e18); + + // Create expired voucher + IDeferredPaymentEscrow.Voucher memory expiredVoucher = createVoucher( + VOUCHER_ID, + buyerFromPrivateKey, + seller, + VOUCHER_VALUE, + address(usdc), + voucherTimestamp, + 1, + uint64(block.timestamp - 1) // Already expired + ); + + bytes memory signature = signVoucher(expiredVoucher, buyerPrivateKey); + + vm.prank(seller); + vm.expectRevert( + abi.encodeWithSelector( + IDeferredPaymentEscrow.VoucherExpired.selector, VOUCHER_ID, block.timestamp, expiredVoucher.expiry + ) + ); + escrow.collect(expiredVoucher, signature); + } + + function test_Collect_InvalidSignature() public { + // Deposit funds + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), 1000e18); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + // Wrong signature + bytes memory wrongSignature = hex"1234567890abcdef"; + + vm.prank(seller); + vm.expectRevert( + abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidSignature.selector, VOUCHER_ID, buyerFromPrivateKey) + ); + escrow.collect(voucher, wrongSignature); + } + + function test_CollectMany_EmptyVouchers() public { + IDeferredPaymentEscrow.SignedVoucher[] memory emptyVouchers = new IDeferredPaymentEscrow.SignedVoucher[](0); + + vm.prank(seller); + vm.expectRevert(IDeferredPaymentEscrow.NoVouchersProvided.selector); + escrow.collectMany(emptyVouchers); + } + + function test_Collect_InvalidVoucherBuyer() public { + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, address(0), seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAddress.selector, address(0))); + escrow.collect(voucher, signature); + } + + function test_Collect_InvalidVoucherAsset() public { + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(0), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAsset.selector, address(0))); + escrow.collect(voucher, signature); + } + + function test_Collect_ZeroVoucherValue() public { + IDeferredPaymentEscrow.Voucher memory voucher = + createVoucher(VOUCHER_ID, buyerFromPrivateKey, seller, 0, address(usdc), voucherTimestamp, 1, voucherExpiry); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.expectRevert(abi.encodeWithSelector(IDeferredPaymentEscrow.InvalidAmount.selector, 0)); + escrow.collect(voucher, signature); + } + + function test_Collect_ZeroFundsAvailable() public { + // Don't deposit any funds, just try to collect + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, VOUCHER_VALUE, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + vm.expectEmit(true, true, true, true); + emit VoucherNoCollectableBalance(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), VOUCHER_VALUE, 0); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // No funds should be transferred + assertEq(usdc.balanceOf(seller), 0); + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), 0); + } + + function test_Collect_PartialThenZeroBalance() public { + uint256 depositAmount = 500e18; + uint256 voucherAmount = 1000e18; + + // Deposit partial amount + vm.prank(buyerFromPrivateKey); + escrow.deposit(seller, address(usdc), depositAmount); + + IDeferredPaymentEscrow.Voucher memory voucher = createVoucher( + VOUCHER_ID, buyerFromPrivateKey, seller, voucherAmount, address(usdc), voucherTimestamp, 1, voucherExpiry + ); + + bytes memory signature = signVoucher(voucher, buyerPrivateKey); + + // First collection - partial + vm.expectEmit(true, true, true, true); + emit VoucherCollected(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), depositAmount, depositAmount); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // Second collection attempt - should emit NoCollectableBalance + vm.expectEmit(true, true, true, true); + emit VoucherNoCollectableBalance(VOUCHER_ID, buyerFromPrivateKey, seller, address(usdc), 500e18, depositAmount); + + vm.prank(seller); + escrow.collect(voucher, signature); + + // Verify state + assertEq(escrow.getVoucherCollected(buyerFromPrivateKey, seller, address(usdc), VOUCHER_ID), depositAmount); + assertEq(escrow.getAccount(buyerFromPrivateKey, seller, address(usdc)).balance, 0); + } +} diff --git a/solidity/deferred-escrow/test/mocks/MockERC1271.sol b/solidity/deferred-escrow/test/mocks/MockERC1271.sol new file mode 100644 index 0000000000..fdce651eab --- /dev/null +++ b/solidity/deferred-escrow/test/mocks/MockERC1271.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {IERC1271} from "@openzeppelin/contracts/interfaces/IERC1271.sol"; + +/** + * @title MockERC1271 + * @notice Mock smart contract wallet for testing ERC-1271 signature validation + */ +contract MockERC1271 is IERC1271 { + bytes4 internal constant MAGICVALUE = 0x1626ba7e; + bytes4 internal constant INVALID_SIGNATURE = 0xffffffff; + + mapping(bytes32 => bool) public validHashes; + + function setValidHash(bytes32 hash, bool isValid) external { + validHashes[hash] = isValid; + } + + function isValidSignature(bytes32 hash, bytes memory) external view override returns (bytes4 magicValue) { + return validHashes[hash] ? MAGICVALUE : INVALID_SIGNATURE; + } +} diff --git a/solidity/deferred-escrow/test/mocks/MockERC20.sol b/solidity/deferred-escrow/test/mocks/MockERC20.sol new file mode 100644 index 0000000000..ed9d522452 --- /dev/null +++ b/solidity/deferred-escrow/test/mocks/MockERC20.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +/** + * @title MockERC20 + * @notice Mock ERC20 token for testing purposes + */ +contract MockERC20 is ERC20 { + uint8 private _decimals; + + constructor(string memory name, string memory symbol, uint8 decimals_) ERC20(name, symbol) { + _decimals = decimals_; + } + + function decimals() public view override returns (uint8) { + return _decimals; + } + + function mint(address to, uint256 amount) external { + _mint(to, amount); + } + + function burn(address from, uint256 amount) external { + _burn(from, amount); + } +} diff --git a/solidity/deferred-escrow/test/mocks/MockNonStandardERC20.sol b/solidity/deferred-escrow/test/mocks/MockNonStandardERC20.sol new file mode 100644 index 0000000000..4a18f72ed1 --- /dev/null +++ b/solidity/deferred-escrow/test/mocks/MockNonStandardERC20.sol @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.30; + +/** + * @title MockNonStandardERC20 + * @notice Mock ERC20 token that doesn't return bool from transfer/transferFrom + * @dev Mimics tokens like USDT that don't return bool from transfer functions + */ +contract MockNonStandardERC20 { + string public name; + string public symbol; + uint8 public decimals; + uint256 public totalSupply; + + mapping(address => uint256) public balanceOf; + mapping(address => mapping(address => uint256)) public allowance; + + event Transfer(address indexed from, address indexed to, uint256 value); + event Approval(address indexed owner, address indexed spender, uint256 value); + + constructor(string memory _name, string memory _symbol, uint8 _decimals) { + name = _name; + symbol = _symbol; + decimals = _decimals; + } + + function mint(address to, uint256 amount) external { + balanceOf[to] += amount; + totalSupply += amount; + emit Transfer(address(0), to, amount); + } + + function burn(address from, uint256 amount) external { + balanceOf[from] -= amount; + totalSupply -= amount; + emit Transfer(from, address(0), amount); + } + + // Non-standard: doesn't return bool + function transfer(address to, uint256 amount) external { + require(balanceOf[msg.sender] >= amount, "Insufficient balance"); + balanceOf[msg.sender] -= amount; + balanceOf[to] += amount; + emit Transfer(msg.sender, to, amount); + } + + // Non-standard: doesn't return bool + function transferFrom(address from, address to, uint256 amount) external { + require(balanceOf[from] >= amount, "Insufficient balance"); + require(allowance[from][msg.sender] >= amount, "Insufficient allowance"); + + balanceOf[from] -= amount; + balanceOf[to] += amount; + allowance[from][msg.sender] -= amount; + + emit Transfer(from, to, amount); + } + + function approve(address spender, uint256 amount) external returns (bool) { + allowance[msg.sender][spender] = amount; + emit Approval(msg.sender, spender, amount); + return true; + } +} diff --git a/static/x402-deferred-protocol-flow.png b/static/x402-deferred-protocol-flow.png new file mode 100644 index 0000000000..9c04851f69 Binary files /dev/null and b/static/x402-deferred-protocol-flow.png differ diff --git a/typescript/packages/x402-axios/README.md b/typescript/packages/x402-axios/README.md index 9c9bb4b011..896e2ce4e9 100644 --- a/typescript/packages/x402-axios/README.md +++ b/typescript/packages/x402-axios/README.md @@ -10,6 +10,8 @@ npm install x402-axios ## Quick Start +### Exact Scheme + ```typescript import { createWalletClient, http } from "viem"; import { privateKeyToAccount } from "viem/accounts"; @@ -30,7 +32,37 @@ const api = withPaymentInterceptor( axios.create({ baseURL: "https://api.example.com", }), - client + client, +); + +// Make a request that may require payment +const response = await api.get("/paid-endpoint"); +console.log(response.data); +``` + +### Deferred Scheme + +```typescript +import { createWalletClient, http } from "viem"; +import { privateKeyToAccount } from "viem/accounts"; +import { withDeferredPaymentInterceptor } from "x402-axios"; +import axios from "axios"; +import { baseSepolia } from "viem/chains"; + +// Create a wallet client +const account = privateKeyToAccount("0xYourPrivateKey"); +const client = createWalletClient({ + account, + transport: http(), + chain: baseSepolia, +}); + +// Create an Axios instance with payment handling +const api = withDeferredPaymentInterceptor( + axios.create({ + baseURL: "https://api.example.com", + }), + client, ); // Make a request that may require payment @@ -59,6 +91,30 @@ Adds a response interceptor to an Axios instance to handle 402 Payment Required #### Returns The modified Axios instance with the payment interceptor that will: + +1. Intercept 402 responses +2. Parse the payment requirements +3. Create a payment header using the provided wallet client +4. Retry the original request with the payment header +5. Expose the X-PAYMENT-RESPONSE header in the final response + +### `withDeferredPaymentInterceptor(axiosClient, walletClient)` + +Adds a response interceptor to an Axios instance to handle 402 deferred Payment Required responses automatically. + +#### When to use + +The `withDeferredPaymentInterceptor` method should be used if the facilitator/seller being called is configured to allow for deferred/aggregated payments using a configured escrow account. The biggest difference is that the `withPaymentInterceptor` method, if a 402 response is returned, will process the payment for each request. The `withDeferredPaymentInterceptor` and `deferred` payment schemes allow the buyer (the user of this library) to make _multiple_ requests where the amount is aggregated and then paid off with **one** onchain transaciton covering all requests on a given voucher. + +#### Parameters + +- `axiosClient`: The Axios instance to add the interceptor to +- `walletClient`: The wallet client used to sign payment messages (must implement the x402 wallet interface) + +#### Returns + +The modified Axios instance with the payment interceptor that will: + 1. Intercept 402 responses 2. Parse the payment requirements 3. Create a payment header using the provided wallet client diff --git a/typescript/packages/x402-axios/package.json b/typescript/packages/x402-axios/package.json index 8f4b948b36..4a63669744 100644 --- a/typescript/packages/x402-axios/package.json +++ b/typescript/packages/x402-axios/package.json @@ -35,6 +35,7 @@ "typescript": "^5.7.3", "vite-tsconfig-paths": "^5.1.4", "vitest": "^3.0.5", + "vitest-mock-extended": "^3.1.0", "vite": "^6.2.6" }, "dependencies": { diff --git a/typescript/packages/x402-axios/src/index.test.ts b/typescript/packages/x402-axios/src/index.test.ts index 489570b552..adbc6e91e3 100644 --- a/typescript/packages/x402-axios/src/index.test.ts +++ b/typescript/packages/x402-axios/src/index.test.ts @@ -6,8 +6,18 @@ import { InternalAxiosRequestConfig, } from "axios"; import { beforeEach, describe, expect, it, vi } from "vitest"; -import { evm, PaymentRequirements, ChainIdToNetwork, Signer, MultiNetworkSigner } from "x402/types"; -import { withPaymentInterceptor } from "./index"; +import { mock } from "vitest-mock-extended"; +import { + evm, + PaymentRequirements, + ChainIdToNetwork, + Signer, + MultiNetworkSigner, + DeferredPaymentRequirements, + DEFERRRED_SCHEME, +} from "x402/types"; + +import { withDeferredPaymentInterceptor, withPaymentInterceptor } from "./index"; // Mock the createPaymentHeader function vi.mock("x402/client", () => ({ @@ -15,6 +25,48 @@ vi.mock("x402/client", () => ({ selectPaymentRequirements: vi.fn(), })); +vi.mock("x402/schemes", () => ({ + deferred: { + evm: { + createPaymentExtraPayload: vi.fn(), + }, + }, +})); + +const createErrorConfig = ( + isRetry = false, + headers = new AxiosHeaders(), +): InternalAxiosRequestConfig => + ({ + headers, + url: "https://api.example.com", + method: "GET", + ...(isRetry ? { __is402Retry: true } : {}), + }) as InternalAxiosRequestConfig; + +const createAxiosError = ( + status: number, + config?: InternalAxiosRequestConfig, + data?: { accepts: PaymentRequirements[]; x402Version: number }, + headers?: AxiosHeaders, +): AxiosError => { + return new AxiosError( + "Error", + "ERROR", + config, + { + headers: headers ?? {}, + }, + { + status, + statusText: status === 402 ? "Payment Required" : "Not Found", + data, + headers: headers ?? {}, + config: config || createErrorConfig(), + }, + ); +}; + describe("withPaymentInterceptor()", () => { let mockAxiosClient: AxiosInstance; let mockWalletClient: typeof evm.SignerWallet; @@ -34,34 +86,6 @@ describe("withPaymentInterceptor()", () => { }, ]; - const createErrorConfig = (isRetry = false): InternalAxiosRequestConfig => - ({ - headers: new AxiosHeaders(), - url: "https://api.example.com", - method: "GET", - ...(isRetry ? { __is402Retry: true } : {}), - }) as InternalAxiosRequestConfig; - - const createAxiosError = ( - status: number, - config?: InternalAxiosRequestConfig, - data?: { accepts: PaymentRequirements[]; x402Version: number }, - ): AxiosError => { - return new AxiosError( - "Error", - "ERROR", - config, - {}, - { - status, - statusText: status === 402 ? "Payment Required" : "Not Found", - data, - headers: {}, - config: config || createErrorConfig(), - }, - ); - }; - beforeEach(async () => { // Reset mocks before each test vi.resetAllMocks(); @@ -213,6 +237,177 @@ describe("withPaymentInterceptor()", () => { }); }); +describe("withDeferredPaymentInterceptor", () => { + let mockAxiosClient: AxiosInstance; + let mockWalletClient: typeof evm.SignerWallet; + let requestInterceptor: ( + request: InternalAxiosRequestConfig, + ) => Promise; + let responseInterceptor: (error: AxiosError) => Promise; + + const escrowAddress = "0xffffff12345678901234567890123456789fffff"; + const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; + const validPaymentRequirements: Array = [ + { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "100000", // 0.1 USDC in base units + resource: "https://api.example.com/resource", + description: "Test payment", + mimeType: "application/json", + payTo: "0x1234567890123456789012345678901234567890", + maxTimeoutSeconds: 300, + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // USDC on base-sepolia + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }, + ]; + + beforeEach(async () => { + // Reset mocks before each test + vi.resetAllMocks(); + + // Mock axios client + mockAxiosClient = { + interceptors: { + response: { + use: vi.fn(), + }, + request: { + use: vi.fn(), + }, + }, + request: vi.fn(), + } as unknown as AxiosInstance; + + // Mock wallet client + mockWalletClient = { + signMessage: vi.fn(), + address: "0x1234567890123456789012345678901234567890", + } as unknown as typeof evm.SignerWallet; + + // Mock payment requirements selector + const { selectPaymentRequirements } = await import("x402/client"); + (selectPaymentRequirements as ReturnType).mockImplementation( + (requirements, _) => requirements[0], + ); + + // Set up the interceptor + withDeferredPaymentInterceptor(mockAxiosClient, mockWalletClient); + requestInterceptor = (mockAxiosClient.interceptors.request.use as ReturnType).mock + .calls[0][0]; + responseInterceptor = (mockAxiosClient.interceptors.response.use as ReturnType) + .mock.calls[0][1]; + }); + + it("should return the axios client instance", () => { + const result = withDeferredPaymentInterceptor(mockAxiosClient, mockWalletClient); + expect(result).toBe(mockAxiosClient); + }); + + it("should set up request and response interceptor", () => { + expect(mockAxiosClient.interceptors.request.use).toHaveBeenCalled(); + expect(mockAxiosClient.interceptors.response.use).toHaveBeenCalled(); + }); + + it("should not handle non-402 errors", async () => { + const error = createAxiosError(404); + await expect(responseInterceptor(error)).rejects.toBe(error); + }); + + it("should send the X-PAYMENT-BUYER with the request headers", async () => { + const request = mock({ + headers: new AxiosHeaders().set("X-PAYMENT-BUYER", mockWalletClient.address), + }); + const updatedRequest = await requestInterceptor(request); + expect(updatedRequest.headers.has("X-PAYMENT-BUYER")); + const xPaymentBuyer = updatedRequest.headers.get("X-PAYMENT-BUYER"); + expect(xPaymentBuyer).not.toBeNull(); + expect(xPaymentBuyer).toEqual(mockWalletClient.address); + }); + + it("should handle 402 errors and retry with payment header", async () => { + const paymentHeader = "payment-header-value"; + const extraPayload = { some: "payload" }; + const successResponse = { + data: "success", + headers: new AxiosHeaders().set("X-PAYMENT-BUYER", mockWalletClient.address), + } as AxiosResponse; + + const { createPaymentHeader, selectPaymentRequirements } = await import("x402/client"); + const { deferred } = await import("x402/schemes"); + (createPaymentHeader as ReturnType).mockResolvedValue(paymentHeader); + (selectPaymentRequirements as ReturnType).mockImplementation( + (requirements, _) => requirements[0], + ); + (deferred.evm.createPaymentExtraPayload as ReturnType).mockResolvedValue( + extraPayload, + ); + (mockAxiosClient.request as ReturnType).mockResolvedValue(successResponse); + + const error = createAxiosError(402, createErrorConfig(false), { + accepts: validPaymentRequirements, + x402Version: 1, + }); + + const result = await responseInterceptor(error); + + expect(result).toBe(successResponse); + expect(selectPaymentRequirements).toHaveBeenCalledWith( + validPaymentRequirements, + undefined, + DEFERRRED_SCHEME, + ); + expect(createPaymentHeader).toHaveBeenCalledWith( + mockWalletClient, + 1, + validPaymentRequirements[0], + { extraPayload }, + ); + + const actualCall = (mockAxiosClient.request as ReturnType).mock.calls[0][0]; + expect(actualCall).toMatchObject({ + ...error.config, + __is402Retry: true, + }); + expect(actualCall.headers.get("X-PAYMENT")).toBe(paymentHeader); + expect(actualCall.headers.get("Access-Control-Expose-Headers")).toBe("X-PAYMENT-RESPONSE"); + }); + + it("should not retry if already retried", async () => { + const error = createAxiosError(402, createErrorConfig(true), { + accepts: validPaymentRequirements, + x402Version: 1, + }); + await expect(responseInterceptor(error)).rejects.toBe(error); + }); + + it("should reject if missing request config", async () => { + const error = createAxiosError(402, undefined, { + accepts: validPaymentRequirements, + x402Version: 1, + }); + await expect(responseInterceptor(error)).rejects.toThrow("Missing axios request configuration"); + }); + + it("should reject if payment header creation fails", async () => { + const paymentError = new Error("Payment failed"); + const { createPaymentHeader } = await import("x402/client"); + (createPaymentHeader as ReturnType).mockRejectedValue(paymentError); + + const error = createAxiosError(402, createErrorConfig(), { + accepts: validPaymentRequirements, + x402Version: 1, + }); + await expect(responseInterceptor(error)).rejects.toBe(paymentError); + }); +}); + describe("withPaymentInterceptor() - SVM and MultiNetwork", () => { beforeEach(() => { vi.resetModules(); diff --git a/typescript/packages/x402-axios/src/index.ts b/typescript/packages/x402-axios/src/index.ts index 2e4f883222..2736a9999f 100644 --- a/typescript/packages/x402-axios/src/index.ts +++ b/typescript/packages/x402-axios/src/index.ts @@ -1,21 +1,27 @@ -import { AxiosInstance, AxiosError } from "axios"; +import { AxiosError, AxiosInstance } from "axios"; +import { Client, LocalAccount } from "viem"; +import { + createPaymentHeader, + PaymentRequirementsSelector, + selectPaymentRequirements, +} from "x402/client"; +import { deferred } from "x402/schemes"; import { - ChainIdToNetwork, - PaymentRequirements, - PaymentRequirementsSchema, Signer, MultiNetworkSigner, isMultiNetworkSigner, isSvmSignerWallet, Network, + ChainIdToNetwork, + DeferredPaymentRequirementsSchema, + DEFERRRED_SCHEME, evm, + EXACT_SCHEME, + PaymentRequirements, + PaymentRequirementsSchema, + DeferredEscrowDepositAuthorizationConfig, X402Config, } from "x402/types"; -import { - createPaymentHeader, - PaymentRequirementsSelector, - selectPaymentRequirements, -} from "x402/client"; /** * Enables the payment of APIs using the x402 payment protocol. @@ -88,7 +94,11 @@ export function withPaymentInterceptor( ? (["solana", "solana-devnet"] as Network[]) : undefined; - const selectedPaymentRequirements = paymentRequirementsSelector(parsed, network, "exact"); + const selectedPaymentRequirements = paymentRequirementsSelector( + parsed, + network, + EXACT_SCHEME, + ); const paymentHeader = await createPaymentHeader( walletClient, x402Version, @@ -112,6 +122,120 @@ export function withPaymentInterceptor( return axiosClient; } +/** + * Enables the payment of APIs using the x402 deferred payment protocol. + * + * When a request receives a 402 response: + * 1. Extracts payment requirements from the response + * 2. Creates a payment header using the provided wallet client + * 3. Retries the original request with the payment header + * 4. Exposes the X-PAYMENT-RESPONSE header in the final response + * + * @param axiosClient - The Axios instance to add the interceptor to + * @param walletClient - A wallet client that can sign transactions and create payment headers + * @param autoDepositConfigs - A list of deposit configurations to use for the deferred payment protocol deposit with authorization flow + * - asset: The asset to deposit + * - threshold: The threshold at which to deposit + * - depositAmount: The amount to deposit + * @param paymentRequirementsSelector - A function that selects the payment requirements from the response + * + * @returns The modified Axios instance with the payment interceptor + * + * @example + * ```typescript + * const client = withDeferredPaymentInterceptor( + * axios.create(), + * signer + * ); + * + * // The client will automatically handle 402 responses + * const response = await client.get('https://api.example.com/premium-content'); + * ``` + */ +export function withDeferredPaymentInterceptor( + axiosClient: AxiosInstance, + walletClient: Signer | MultiNetworkSigner, + autoDepositConfigs: DeferredEscrowDepositAuthorizationConfig[] = [], + paymentRequirementsSelector: PaymentRequirementsSelector = selectPaymentRequirements, +) { + // intercept the request to send a `X-PAYMENT-BUYER` header with each request + axiosClient.interceptors.request.use( + request => { + const buyer = + (walletClient as LocalAccount).address || (walletClient as Client).account?.address; + if (buyer) { + request.headers.set("X-PAYMENT-BUYER", buyer); + } + + return request; + }, + error => Promise.reject(error), + ); + axiosClient.interceptors.response.use( + response => response, + async (error: AxiosError) => { + if (!error.response || error.response.status !== 402) { + return Promise.reject(error); + } + + try { + const originalConfig = error.config; + if (!originalConfig || !originalConfig.headers) { + return Promise.reject(new Error("Missing axios request configuration")); + } + + if ((originalConfig as { __is402Retry?: boolean }).__is402Retry) { + return Promise.reject(error); + } + + const { x402Version, accepts } = error.response.data as { + x402Version: number; + accepts: Array; + }; + const parsed = accepts.map(x => PaymentRequirementsSchema.parse(x)); + + const network = isMultiNetworkSigner(walletClient) + ? undefined + : evm.isSignerWallet(walletClient as typeof evm.EvmSigner) + ? ChainIdToNetwork[(walletClient as typeof evm.EvmSigner).chain?.id] + : undefined; + + const selectedPaymentRequirements = paymentRequirementsSelector( + parsed, + network, + DEFERRRED_SCHEME, + ); + const selectedDeferredPaymentRequirements = DeferredPaymentRequirementsSchema.parse( + selectedPaymentRequirements, + ); + const extraPayload = await deferred.evm.createPaymentExtraPayload( + walletClient as typeof evm.EvmSigner, + selectedDeferredPaymentRequirements, + autoDepositConfigs, + ); + const paymentHeader = await createPaymentHeader( + walletClient, + x402Version, + selectedDeferredPaymentRequirements, + { extraPayload }, + ); + + (originalConfig as { __is402Retry?: boolean }).__is402Retry = true; + + originalConfig.headers["X-PAYMENT"] = paymentHeader; + originalConfig.headers["Access-Control-Expose-Headers"] = "X-PAYMENT-RESPONSE"; + + const paymentHeaderSignedResponse = await axiosClient.request(originalConfig); + return paymentHeaderSignedResponse; + } catch (paymentError) { + return Promise.reject(paymentError); + } + }, + ); + + return axiosClient; +} + export { decodeXPaymentResponse } from "x402/shared"; export { createSigner, type Signer, type MultiNetworkSigner, type X402Config } from "x402/types"; export { type PaymentRequirementsSelector } from "x402/client"; diff --git a/typescript/packages/x402-express/src/index.test.ts b/typescript/packages/x402-express/src/index.test.ts index 2af233a3cf..a9e2b97809 100644 --- a/typescript/packages/x402-express/src/index.test.ts +++ b/typescript/packages/x402-express/src/index.test.ts @@ -2,16 +2,17 @@ import { NextFunction, Request, Response } from "express"; import { beforeEach, describe, expect, it, vi } from "vitest"; import { findMatchingRoute } from "x402/shared"; import { getPaywallHtml } from "x402/paywall"; -import { exact } from "x402/schemes"; +import { exact, deferred } from "x402/schemes"; import { PaymentMiddlewareConfig, PaymentPayload, RoutesConfig, FacilitatorConfig, RouteConfig, + DeferredEvmPayloadSchema, } from "x402/types"; import { useFacilitator } from "x402/verify"; -import { paymentMiddleware } from "./index"; +import { paymentMiddleware, deferredPaymentMiddleware } from "./index"; import { Address as SolanaAddress } from "@solana/kit"; // Mock dependencies @@ -21,6 +22,10 @@ vi.mock("x402/verify", () => ({ settle: vi.fn(), supported: vi.fn(), list: vi.fn(), + deferred: { + getAvailableVoucher: vi.fn(), + storeVoucher: vi.fn(), + }, }), })); @@ -84,7 +89,7 @@ vi.mock("x402/shared/evm", () => ({ getUsdcAddressForChain: vi.fn().mockReturnValue("0x036CbD53842c5426634e7929541eC2318f3dCF7e"), })); -// Mock exact.evm.decodePayment +// Mock exact.evm.decodePayment and deferred.evm vi.mock("x402/schemes", () => ({ exact: { evm: { @@ -92,6 +97,14 @@ vi.mock("x402/schemes", () => ({ decodePayment: vi.fn(), }, }, + deferred: { + evm: { + encodePayment: vi.fn(), + decodePayment: vi.fn(), + getPaymentRequirementsExtra: vi.fn(), + VoucherStore: vi.fn(), + }, + }, })); describe("paymentMiddleware()", () => { @@ -707,3 +720,358 @@ describe("paymentMiddleware()", () => { }); }); }); + +describe("deferredPaymentMiddleware()", () => { + let mockReq: Partial; + let mockRes: Partial; + let mockNext: NextFunction; + let middleware: ReturnType; + let mockVerify: ReturnType["verify"]; + let mockDeferredFacilitator: ReturnType["deferred"]; + + const middlewareConfig: PaymentMiddlewareConfig = { + description: "Test deferred payment", + mimeType: "application/json", + maxTimeoutSeconds: 300, + outputSchema: { type: "object" }, + resource: "https://api.example.com/resource", + }; + + const facilitatorConfig: FacilitatorConfig = { + url: "https://facilitator.example.com", + }; + + const payTo = "0x1234567890123456789012345678901234567890"; + const escrow = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"; + + const routesConfig: RoutesConfig = { + "/test": { + price: "$0.001", + network: "base-sepolia", + config: middlewareConfig, + }, + }; + + const validVoucher = { + buyer: "0x1234567890123456789012345678901234567890", + seller: "0x1234567890123456789012345678901234567890", + valueAggregate: "1000", + nonce: 0, + id: "0x1234567890123456789012345678901234567890123456789012345678901234", + asset: "0x1234567890123456789012345678901234567890", + timestamp: 1715769600, + escrow: "0x1234567890123456789012345678901234567890", + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: "0xsignature", + }; + const validDeferredPayment: PaymentPayload = { + scheme: "deferred", + x402Version: 1, + network: "base-sepolia", + payload: { + voucher: validVoucher, + signature: "0xsignature", + }, + }; + const encodedValidDeferredPayment = "encoded-deferred-payment"; + + beforeEach(() => { + vi.resetAllMocks(); + mockReq = { + path: "/test", + method: "GET", + protocol: "https", + headers: { host: "api.example.com" }, + header: function (name: string) { + return this.headers[name.toLowerCase()]; + }, + } as Request; + mockRes = { + status: vi.fn().mockReturnThis(), + json: vi.fn().mockReturnThis(), + send: vi.fn().mockReturnThis(), + setHeader: vi.fn().mockReturnThis(), + end: vi.fn().mockReturnThis(), + headersSent: false, + } as unknown as Response; + mockNext = vi.fn(); + mockVerify = vi.fn(); + mockDeferredFacilitator = { + getAvailableVoucher: vi.fn(), + storeVoucher: vi.fn(), + }; + + vi.mocked(useFacilitator).mockReturnValue({ + verify: mockVerify, + settle: vi.fn(), + supported: vi.fn(), + list: vi.fn(), + deferred: mockDeferredFacilitator, + }); + + // Setup route pattern matching mock + vi.mocked(findMatchingRoute).mockImplementation((routePatterns, path, method) => { + if (path === "/test" && method === "GET") { + return { + pattern: /^\/test$/, + verb: "GET", + config: { + price: "$0.001", + network: "base-sepolia", + config: middlewareConfig, + }, + }; + } + return undefined; + }); + + // Setup deferred.evm mocks + vi.mocked(deferred.evm.encodePayment).mockReturnValue(encodedValidDeferredPayment); + vi.mocked(deferred.evm.decodePayment).mockReturnValue(validDeferredPayment); + vi.mocked(deferred.evm.getPaymentRequirementsExtra).mockResolvedValue({ + type: "new", + voucher: { + id: "0x1234567890123456789012345678901234567890123456789012345678901234", + escrow, + }, + }); + + // Mock DeferredEvmPayloadSchema.parse + vi.spyOn(DeferredEvmPayloadSchema, "parse").mockReturnValue({ + voucher: validVoucher, + signature: "0xsignature", + }); + }); + + it("should return 402 with payment requirements when no payment header is present", async () => { + middleware = deferredPaymentMiddleware(payTo, routesConfig, escrow, facilitatorConfig); + mockReq.headers = { host: "api.example.com" }; + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + error: "X-PAYMENT header is required", + accepts: expect.any(Array), + x402Version: 1, + }), + ); + }); + + it("should return 402 if payment decoding fails", async () => { + middleware = deferredPaymentMiddleware(payTo, routesConfig, escrow, facilitatorConfig); + mockReq.headers = { + "x-payment": "invalid-payment-header", + host: "api.example.com", + }; + (deferred.evm.decodePayment as ReturnType).mockImplementation(() => { + throw new Error("Invalid payment"); + }); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + error: expect.anything(), + accepts: expect.any(Array), + }), + ); + }); + + describe("with facilitator voucher store", () => { + beforeEach(() => { + middleware = deferredPaymentMiddleware(payTo, routesConfig, escrow, facilitatorConfig); + }); + + it("should store voucher via facilitator and proceed if valid", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockDeferredFacilitator.storeVoucher as ReturnType).mockResolvedValue( + undefined, + ); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(deferred.evm.decodePayment).toHaveBeenCalledWith(encodedValidDeferredPayment); + expect(mockDeferredFacilitator.storeVoucher).toHaveBeenCalledWith( + validDeferredPayment, + expect.any(Object), + ); + expect(mockVerify).not.toHaveBeenCalled(); // Skip verify when using facilitator store + expect(mockNext).toHaveBeenCalled(); + }); + + it("should return 402 if facilitator voucher storage fails", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockDeferredFacilitator.storeVoucher as ReturnType).mockRejectedValue( + new Error("Storage failed"), + ); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + error: expect.any(Error), + accepts: expect.any(Array), + }), + ); + }); + + it("should set X-PAYMENT-RESPONSE header on successful payment", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockDeferredFacilitator.storeVoucher as ReturnType).mockResolvedValue( + undefined, + ); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.setHeader).toHaveBeenCalledWith("X-PAYMENT-RESPONSE", expect.any(String)); + expect(mockNext).toHaveBeenCalled(); + }); + + it("should not set X-PAYMENT-RESPONSE if protected route returns status >= 400", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockDeferredFacilitator.storeVoucher as ReturnType).mockResolvedValue( + undefined, + ); + + // Simulate downstream handler setting status 500 + (mockRes.status as ReturnType).mockImplementation(function ( + this: Response, + code: number, + ) { + this.statusCode = code; + return this; + }); + mockRes.statusCode = 500; + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.setHeader).not.toHaveBeenCalledWith("X-PAYMENT-RESPONSE", expect.any(String)); + expect(mockRes.statusCode).toBe(500); + }); + }); + + describe("with custom voucher store", () => { + let mockVoucherStore: { + getAvailableVoucher: ReturnType; + storeVoucher: ReturnType; + }; + + beforeEach(() => { + mockVoucherStore = { + getAvailableVoucher: vi.fn().mockResolvedValue(null), + storeVoucher: vi.fn().mockResolvedValue(undefined), + }; + middleware = deferredPaymentMiddleware( + payTo, + routesConfig, + escrow, + facilitatorConfig, + mockVoucherStore, + ); + }); + + it("should verify payment and store voucher locally when using custom store", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockVerify as ReturnType).mockResolvedValue({ isValid: true }); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(deferred.evm.decodePayment).toHaveBeenCalledWith(encodedValidDeferredPayment); + expect(mockVerify).toHaveBeenCalledWith(validDeferredPayment, expect.any(Object)); + expect(mockVoucherStore.storeVoucher).toHaveBeenCalledWith( + expect.objectContaining({ + signature: "0xsignature", + }), + ); + expect(mockDeferredFacilitator.storeVoucher).not.toHaveBeenCalled(); + expect(mockNext).toHaveBeenCalled(); + }); + + it("should return 402 if payment verification fails with custom store", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockVerify as ReturnType).mockResolvedValue({ + isValid: false, + invalidReason: "insufficient_funds", + payer: "0x123", + }); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + error: "insufficient_funds", + accepts: expect.any(Array), + payer: "0x123", + }), + ); + expect(mockVoucherStore.storeVoucher).not.toHaveBeenCalled(); + }); + + it("should return 402 if verification throws error with custom store", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockVerify as ReturnType).mockRejectedValue(new Error("Verification failed")); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + error: expect.any(Error), + accepts: expect.any(Array), + }), + ); + expect(mockVoucherStore.storeVoucher).not.toHaveBeenCalled(); + }); + + it("should return 402 if local voucher storage fails", async () => { + mockReq.headers = { + "x-payment": encodedValidDeferredPayment, + host: "api.example.com", + }; + (mockVerify as ReturnType).mockResolvedValue({ isValid: true }); + mockVoucherStore.storeVoucher.mockRejectedValue(new Error("Storage failed")); + + await middleware(mockReq as Request, mockRes as Response, mockNext); + + expect(mockRes.status).toHaveBeenCalledWith(402); + expect(mockRes.json).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + error: expect.any(Error), + accepts: expect.any(Array), + }), + ); + }); + }); +}); diff --git a/typescript/packages/x402-express/src/index.ts b/typescript/packages/x402-express/src/index.ts index e275acb88c..af64cb3454 100644 --- a/typescript/packages/x402-express/src/index.ts +++ b/typescript/packages/x402-express/src/index.ts @@ -1,11 +1,12 @@ import { NextFunction, Request, Response } from "express"; import { Address, getAddress } from "viem"; import { Address as SolanaAddress } from "@solana/kit"; -import { exact } from "x402/schemes"; +import { exact, deferred } from "x402/schemes"; import { computeRoutePatterns, findMatchingPaymentRequirements, findMatchingRoute, + getNetworkId, processPriceToAtomicAmount, toJsonSafe, } from "x402/shared"; @@ -15,18 +16,23 @@ import { ERC20TokenAmount, moneySchema, PaymentPayload, - PaymentRequirements, PaywallConfig, Resource, RoutesConfig, settleResponseHeader, SupportedEVMNetworks, SupportedSVMNetworks, + PaymentRequirementsSchema, + PaymentRequirements, + DEFERRRED_SCHEME, + DeferredEvmPayloadSchema, + EXACT_SCHEME, + SettleResponse, } from "x402/types"; import { useFacilitator } from "x402/verify"; /** - * Creates a payment middleware factory for Express + * Creates an exact payment middleware factory for Express * * @param payTo - The address to receive payments * @param routes - Configuration for protected routes and their payment requirements @@ -122,7 +128,7 @@ export function paymentMiddleware( // evm networks if (SupportedEVMNetworks.includes(network)) { paymentRequirements.push({ - scheme: "exact", + scheme: EXACT_SCHEME, network, maxAmountRequired, resource: resourceUrl, @@ -301,7 +307,7 @@ export function paymentMiddleware( }; // Proceed to the next middleware or route handler - await next(); + next(); // If the response from the protected route is >= 400, do not settle payment if (res.statusCode >= 400) { @@ -346,6 +352,273 @@ export function paymentMiddleware( }; } +/** + * Creates a deferred payment middleware factory for Express. + * + * Note: this middleware does not perform the x402 settlement step. In the deferred scheme, + * settlement does not happen as part of the x402 resource request handshake, it happens later + * on in a "deferred" way. + * + * The middleware delegates voucher storage to the facilitator but can be configured to store vouchers + * locally on the resource server. The server needs to implement it's own voucher store and expose two + * main functions for the middleware: + * - getAvailableVoucher: Retrieves the latest voucher for a given buyer/seller + * - storeVoucher: Persists newly created vouchers + * + * If `voucherStore` is not provided, voucher storage is delegated to the facilitator. + * + * @param payTo - The address to receive payments + * @param routes - Configuration for protected routes and their payment requirements + * @param escrow - The escrow contract address + * @param facilitator - Optional configuration for the payment facilitator service + * @param voucherStore - Optional voucher storage implementation with `getAvailableVoucher` and `storeVoucher` methods + * @returns An Express middleware handler + * + * @example + * ```typescript + * // Simple configuration — all endpoints protected by $0.01 of USDC on Base Sepolia + * // Uses facilitator for voucher storage + * app.use( + * deferredPaymentMiddleware( + * '0x123...', // payTo address + * { + * price: '$0.01', // USDC amount in dollars + * network: 'base-sepolia', + * }, + * '0xescrowAddress...', + * // Optional facilitator configuration (defaults to x402.org/facilitator for testnet usage) + * ) + * ); + * + * // Advanced configuration — custom voucher store and facilitator + * app.use( + * deferredPaymentMiddleware( + * '0x123...', // payTo + * { + * '/weather/*': { + * price: '$0.001', // USDC amount in dollars + * network: 'base', + * config: { + * description: 'Access to weather data', + * }, + * }, + * }, + * '0xescrowAddress...', + * { + * url: 'https://facilitator.example.com', + * createAuthHeaders: async () => ({ + * verify: { Authorization: 'Bearer token' }, + * settle: { Authorization: 'Bearer token' }, + * }), + * }, + * { + * getAvailableVoucher: async (buyer, seller) => { + * // Custom implementation to retrieve voucher + * return await db.getVoucher(buyer, seller); + * }, + * storeVoucher: async (voucher) => { + * // Custom implementation to store voucher + * await db.saveVoucher(voucher); + * }, + * } + * ) + * ); + * ``` + */ +export function deferredPaymentMiddleware( + payTo: Address, + routes: RoutesConfig, + escrow: Address, + facilitator: FacilitatorConfig, + voucherStore?: Pick< + InstanceType, + "getAvailableVoucher" | "storeVoucher" + >, +) { + const x402Version = 1; + const { verify, deferred: deferredFacilitator } = useFacilitator(facilitator); + + // Pre-compile route patterns to regex and extract verbs + const routePatterns = computeRoutePatterns(routes); + + return async function paymentMiddleware( + req: Request, + res: Response, + next: NextFunction, + ): Promise { + const matchingRoute = findMatchingRoute(routePatterns, req.path, req.method.toUpperCase()); + + if (!matchingRoute) { + return next(); + } + + const { price, network, config = {} } = matchingRoute.config; + const { description, mimeType, maxTimeoutSeconds, outputSchema, resource } = config; + + const atomicAmountForAsset = processPriceToAtomicAmount(price, network); + if ("error" in atomicAmountForAsset) { + throw new Error(atomicAmountForAsset.error); + } + const { maxAmountRequired, asset } = atomicAmountForAsset; + + const resourceUrl: Resource = + resource || (`${req.protocol}://${req.headers.host}${req.path}` as Resource); + + const payment = req.header("X-PAYMENT"); + const paymentBuyer = req.header("X-PAYMENT-BUYER"); + + const unparsedPaymentRequirements = [ + { + scheme: DEFERRRED_SCHEME, + network, + maxAmountRequired, + resource: resourceUrl, + description: description ?? "", + mimeType: mimeType ?? "", + payTo: getAddress(payTo), + maxTimeoutSeconds: maxTimeoutSeconds ?? 60, + asset: getAddress(asset.address), + outputSchema: outputSchema ?? undefined, + extra: await deferred.evm.getPaymentRequirementsExtra( + payment, + paymentBuyer as `0x${string}` | undefined, + payTo, + escrow, + getAddress(asset.address), + getNetworkId(network), + facilitator, + voucherStore?.getAvailableVoucher, + ), + }, + ]; + + const paymentRequirements = unparsedPaymentRequirements.map(req => { + const parsed = PaymentRequirementsSchema.safeParse(req); + if (!parsed.success) { + throw new Error(`Invalid payment requirements: ${parsed.error.message}`); + } + return parsed.data; + }); + + if (!payment) { + res.status(402).json({ + x402Version, + error: "X-PAYMENT header is required", + accepts: toJsonSafe(paymentRequirements), + }); + return; + } + + let decodedPayment: PaymentPayload; + try { + decodedPayment = deferred.evm.decodePayment(payment); + decodedPayment.x402Version = x402Version; + } catch (error) { + res.status(402).json({ + x402Version, + error: error || "Invalid or malformed payment header", + accepts: toJsonSafe(paymentRequirements), + }); + return; + } + + const selectedPaymentRequirements = findMatchingPaymentRequirements( + paymentRequirements, + decodedPayment, + ); + if (!selectedPaymentRequirements) { + res.status(402).json({ + x402Version, + error: "Unable to find matching payment requirements", + accepts: toJsonSafe(paymentRequirements), + }); + return; + } + + // At this point x402 protocol requires POSTing to /verify the payment requirements + // If we are using the facilitator's voucher store then when posting to store the voucher the facilitator + // will already perform the same verification. So if we use the facilitator's voucher store we can skip /verify. + if (voucherStore) { + try { + // POST /verify to facilitator + const response = await verify(decodedPayment, selectedPaymentRequirements); + if (!response.isValid) { + res.status(402).json({ + x402Version, + error: response.invalidReason, + accepts: toJsonSafe(paymentRequirements), + payer: response.payer, + }); + return; + } + + // Store voucher locally + const { voucher, signature } = DeferredEvmPayloadSchema.parse(decodedPayment.payload); + await voucherStore.storeVoucher({ ...voucher, signature }); + } catch (error) { + console.log(error); + res.status(402).json({ + x402Version, + error, + accepts: toJsonSafe(paymentRequirements), + }); + return; + } + } else { + try { + // skip POST /verify and store voucher in facilitator + await deferredFacilitator.storeVoucher(decodedPayment, selectedPaymentRequirements); + } catch (error) { + console.error(error); + res.status(402).json({ + x402Version, + error, + accepts: toJsonSafe(paymentRequirements), + }); + return; + } + } + + /* eslint-disable @typescript-eslint/no-explicit-any */ + type EndArgs = + | [cb?: () => void] + | [chunk: any, cb?: () => void] + | [chunk: any, encoding: BufferEncoding, cb?: () => void]; + /* eslint-enable @typescript-eslint/no-explicit-any */ + + const originalEnd = res.end.bind(res); + let endArgs: EndArgs | null = null; + + res.end = function (...args: EndArgs) { + endArgs = args; + return res; // maintain correct return type + }; + + // Proceed to the next middleware or route handler + next(); + + // If the response from the protected route is >= 400, do not set X-PAYMENT-RESPONSE + if (res.statusCode >= 400) { + res.end = originalEnd; + if (endArgs) { + originalEnd(...(endArgs as Parameters)); + } + return; + } + + // Return with X-PAYMENT-RESPONSE + const responseHeader = settleResponseHeader({ + success: true, + } as SettleResponse); + res.setHeader("X-PAYMENT-RESPONSE", responseHeader); + + res.end = originalEnd; + if (endArgs) { + originalEnd(...(endArgs as Parameters)); + } + }; +} + export type { Money, Network, diff --git a/typescript/packages/x402/src/client/createPaymentHeader.ts b/typescript/packages/x402/src/client/createPaymentHeader.ts index 6ffe636f33..1667b638b5 100644 --- a/typescript/packages/x402/src/client/createPaymentHeader.ts +++ b/typescript/packages/x402/src/client/createPaymentHeader.ts @@ -1,7 +1,10 @@ import { createPaymentHeader as createPaymentHeaderExactEVM } from "../schemes/exact/evm/client"; import { createPaymentHeader as createPaymentHeaderExactSVM } from "../schemes/exact/svm/client"; import { isEvmSignerWallet, isMultiNetworkSigner, isSvmSignerWallet, MultiNetworkSigner, Signer, SupportedEVMNetworks, SupportedSVMNetworks } from "../types/shared"; +import { createPaymentHeader as createPaymentHeaderDeferredEVM } from "../schemes/deferred/evm/client"; import { PaymentRequirements } from "../types/verify"; +import { DEFERRRED_SCHEME } from "../types/verify/schemes/deferred"; +import { EXACT_SCHEME } from "../types/verify/schemes/exact"; import { X402Config } from "../types/config"; /** @@ -20,7 +23,7 @@ export async function createPaymentHeader( config?: X402Config, ): Promise { // exact scheme - if (paymentRequirements.scheme === "exact") { + if (paymentRequirements.scheme === EXACT_SCHEME) { // evm if (SupportedEVMNetworks.includes(paymentRequirements.network)) { const evmClient = isMultiNetworkSigner(client) ? client.evm : client; @@ -51,5 +54,20 @@ export async function createPaymentHeader( } throw new Error("Unsupported network"); } + + // deferred scheme + if ( + paymentRequirements.scheme === DEFERRRED_SCHEME && + SupportedEVMNetworks.includes(paymentRequirements.network) + ) { + const evmClient = isMultiNetworkSigner(client) ? client.evm : client; + + if (!isEvmSignerWallet(evmClient)) { + throw new Error("Invalid evm wallet client provided"); + } + + return await createPaymentHeaderDeferredEVM(evmClient, x402Version, paymentRequirements, config?.extraPayload); + } + throw new Error("Unsupported scheme"); } \ No newline at end of file diff --git a/typescript/packages/x402/src/client/preparePaymentHeader.ts b/typescript/packages/x402/src/client/preparePaymentHeader.ts index 986c63c95c..7ba7c001e2 100644 --- a/typescript/packages/x402/src/client/preparePaymentHeader.ts +++ b/typescript/packages/x402/src/client/preparePaymentHeader.ts @@ -1,11 +1,15 @@ import { Address } from "viem"; import { preparePaymentHeader as preparePaymentHeaderExactEVM } from "../schemes/exact/evm/client"; +import { preparePaymentHeader as preparePaymentHeaderDeferredEVM } from "../schemes/deferred/evm/client"; import { SupportedEVMNetworks } from "../types/shared"; import { PaymentRequirements, UnsignedPaymentPayload } from "../types/verify"; +import { DEFERRRED_SCHEME } from "../types/verify/schemes/deferred"; +import { EXACT_SCHEME } from "../types/verify/schemes/exact"; /** * Prepares a payment header with the given sender address and payment requirements. - * + * Only supports exact scheme. For deferred scheme, use preparePaymentHeaderAsync. + * * @param from - The sender's address from which the payment will be made * @param x402Version - The version of the X402 protocol to use * @param paymentRequirements - The payment requirements containing scheme and network information @@ -17,7 +21,7 @@ export function preparePaymentHeader( paymentRequirements: PaymentRequirements, ): UnsignedPaymentPayload { if ( - paymentRequirements.scheme === "exact" && + paymentRequirements.scheme === EXACT_SCHEME && SupportedEVMNetworks.includes(paymentRequirements.network) ) { return preparePaymentHeaderExactEVM(from, x402Version, paymentRequirements); @@ -25,3 +29,35 @@ export function preparePaymentHeader( throw new Error("Unsupported scheme"); } + +/** + * Prepares a payment header with the given sender address and payment requirements. + * Async version of preparePaymentHeader that supports exact and deferred schemes. + * + * @param from - The sender's address from which the payment will be made + * @param x402Version - The version of the X402 protocol to use + * @param paymentRequirements - The payment requirements containing scheme and network information + * @returns An unsigned payment payload that can be used to create a payment header + */ +export function preparePaymentHeaderAsync( + from: Address, + x402Version: number, + paymentRequirements: PaymentRequirements, +): Promise { + + if ( + paymentRequirements.scheme === EXACT_SCHEME && + SupportedEVMNetworks.includes(paymentRequirements.network) + ) { + return Promise.resolve(preparePaymentHeaderExactEVM(from, x402Version, paymentRequirements)); + } + + if ( + paymentRequirements.scheme === DEFERRRED_SCHEME && + SupportedEVMNetworks.includes(paymentRequirements.network) + ) { + return preparePaymentHeaderDeferredEVM(from, x402Version, paymentRequirements); + } + + throw new Error("Unsupported scheme"); +} \ No newline at end of file diff --git a/typescript/packages/x402/src/client/selectPaymentRequirements.test.ts b/typescript/packages/x402/src/client/selectPaymentRequirements.test.ts index 7b7ef3f503..cfa8e163b8 100644 --- a/typescript/packages/x402/src/client/selectPaymentRequirements.test.ts +++ b/typescript/packages/x402/src/client/selectPaymentRequirements.test.ts @@ -1,8 +1,6 @@ import { describe, it, expect } from "vitest"; -import { - selectPaymentRequirements, -} from "./selectPaymentRequirements"; -import { PaymentRequirements, Network } from "../types"; +import { selectPaymentRequirements } from "./selectPaymentRequirements"; +import { PaymentRequirements, Network, EXACT_SCHEME, type X402_SCHEMES } from "../types"; import { getUsdcChainConfigForChain } from "../shared/evm"; import { getNetworkId } from "../shared/network"; @@ -12,15 +10,17 @@ import { getNetworkId } from "../shared/network"; * @param network - The network to create the payment requirement for. * @param asset - The asset to create the payment requirement for. * @param overrides - The overrides to apply to the payment requirement. + * @param scheme - The scheme to create the payment requirement for. * @returns The created payment requirement. */ function makeRequirement( network: Network, asset: string, - overrides: Partial = {}, + overrides: Partial> = {}, + scheme: X402_SCHEMES = EXACT_SCHEME, ): PaymentRequirements { return { - scheme: "exact", + scheme, network, maxAmountRequired: "1000", resource: "https://example.com/resource", @@ -30,7 +30,7 @@ function makeRequirement( maxTimeoutSeconds: 300, asset, ...overrides, - }; + } as PaymentRequirements; } describe("selectPaymentRequirements", () => { diff --git a/typescript/packages/x402/src/client/selectPaymentRequirements.ts b/typescript/packages/x402/src/client/selectPaymentRequirements.ts index fa3c9e67e1..d4f60e15f0 100644 --- a/typescript/packages/x402/src/client/selectPaymentRequirements.ts +++ b/typescript/packages/x402/src/client/selectPaymentRequirements.ts @@ -1,6 +1,6 @@ -import { Network, PaymentRequirements } from "../types"; import { getUsdcChainConfigForChain } from "../shared/evm"; import { getNetworkId } from "../shared/network"; +import { Network, PaymentRequirements, type X402_SCHEMES } from "../types"; /** * Default selector for payment requirements. @@ -12,7 +12,11 @@ import { getNetworkId } from "../shared/network"; * @param scheme - The scheme to check against. If not provided, the scheme will not be checked. * @returns The payment requirement that is the most appropriate for the user. */ -export function selectPaymentRequirements(paymentRequirements: PaymentRequirements[], network?: Network | Network[], scheme?: "exact"): PaymentRequirements { +export function selectPaymentRequirements( + paymentRequirements: Array, + network?: Network | Network[], + scheme?: X402_SCHEMES, +): PaymentRequirements { // Sort `base` payment requirements to the front of the list. This is to ensure that base is preferred if available. paymentRequirements.sort((a, b) => { if (a.network === "base" && b.network !== "base") { @@ -37,7 +41,10 @@ export function selectPaymentRequirements(paymentRequirements: PaymentRequiremen // Filter down to USDC requirements const usdcRequirements = broadlyAcceptedPaymentRequirements.filter(requirement => { // If the address is a USDC address, we return it. - return requirement.asset === getUsdcChainConfigForChain(getNetworkId(requirement.network))?.usdcAddress; + return ( + requirement.asset === + getUsdcChainConfigForChain(getNetworkId(requirement.network))?.usdcAddress + ); }); // Prioritize USDC requirements if available @@ -60,5 +67,4 @@ export function selectPaymentRequirements(paymentRequirements: PaymentRequiremen * @param scheme - The scheme to check against. If not provided, the scheme will not be checked. * @returns The payment requirement that is the most appropriate for the user. */ -export type PaymentRequirementsSelector = (paymentRequirements: PaymentRequirements[], network?: Network | Network[], scheme?: "exact") => PaymentRequirements; - +export type PaymentRequirementsSelector = (paymentRequirements: Array, network?: Network | Network[], scheme?: X402_SCHEMES) => PaymentRequirements; diff --git a/typescript/packages/x402/src/client/signPaymentHeader.ts b/typescript/packages/x402/src/client/signPaymentHeader.ts index b28ae1de85..546e237c57 100644 --- a/typescript/packages/x402/src/client/signPaymentHeader.ts +++ b/typescript/packages/x402/src/client/signPaymentHeader.ts @@ -1,7 +1,13 @@ import { signPaymentHeader as signPaymentHeaderExactEVM } from "../schemes/exact/evm/client"; -import { encodePayment } from "../schemes/exact/evm/utils/paymentUtils"; import { isEvmSignerWallet, isMultiNetworkSigner, MultiNetworkSigner, Signer, SupportedEVMNetworks } from "../types/shared"; +import { signPaymentHeader as signPaymentHeaderDeferredEVM } from "../schemes/deferred/evm/client"; +import { encodePayment as encodePaymentExactEVM } from "../schemes/exact/evm/utils/paymentUtils"; +import { encodePayment as encodePaymentDeferredEVM } from "../schemes/deferred/evm/utils/paymentUtils"; import { PaymentRequirements, UnsignedPaymentPayload } from "../types/verify"; +import { UnsignedDeferredPaymentPayloadSchema } from "../types/verify/schemes/deferred"; +import { UnsignedExactPaymentPayloadSchema } from "../types/verify/schemes/exact"; +import { DEFERRRED_SCHEME } from "../types/verify/schemes/deferred"; +import { EXACT_SCHEME } from "../types/verify/schemes/exact"; /** * Signs a payment header using the provided client and payment requirements. @@ -17,7 +23,7 @@ export async function signPaymentHeader( unsignedPaymentHeader: UnsignedPaymentPayload, ): Promise { if ( - paymentRequirements.scheme === "exact" && + paymentRequirements.scheme === EXACT_SCHEME && SupportedEVMNetworks.includes(paymentRequirements.network) ) { const evmClient = isMultiNetworkSigner(client) ? client.evm : client; @@ -25,8 +31,24 @@ export async function signPaymentHeader( if (!isEvmSignerWallet(evmClient)) { throw new Error("Invalid evm wallet client provided"); } + unsignedPaymentHeader = UnsignedExactPaymentPayloadSchema.parse(unsignedPaymentHeader); const signedPaymentHeader = await signPaymentHeaderExactEVM(evmClient, paymentRequirements, unsignedPaymentHeader); - return encodePayment(signedPaymentHeader); + return encodePaymentExactEVM(signedPaymentHeader); + } + + if ( + paymentRequirements.scheme === DEFERRRED_SCHEME && + SupportedEVMNetworks.includes(paymentRequirements.network) + ) { + const evmClient = isMultiNetworkSigner(client) ? client.evm : client; + + if (!isEvmSignerWallet(evmClient)) { + throw new Error("Invalid evm wallet client provided"); + } + + unsignedPaymentHeader = UnsignedDeferredPaymentPayloadSchema.parse(unsignedPaymentHeader); + const signedPaymentHeader = await signPaymentHeaderDeferredEVM(evmClient, unsignedPaymentHeader); + return encodePaymentDeferredEVM(signedPaymentHeader); } throw new Error("Unsupported scheme"); diff --git a/typescript/packages/x402/src/facilitator/facilitator.ts b/typescript/packages/x402/src/facilitator/facilitator.ts index 2e13f6166f..e25817018b 100644 --- a/typescript/packages/x402/src/facilitator/facilitator.ts +++ b/typescript/packages/x402/src/facilitator/facilitator.ts @@ -1,5 +1,6 @@ import { verify as verifyExactEvm, settle as settleExactEvm } from "../schemes/exact/evm"; import { verify as verifyExactSvm, settle as settleExactSvm } from "../schemes/exact/svm"; +import { verify as verifyDeferred, settle as settleDeferred } from "../schemes/deferred/evm"; import { SupportedEVMNetworks, SupportedSVMNetworks } from "../types/shared"; import { X402Config } from "../types/config"; import { @@ -16,6 +17,10 @@ import { } from "../types/verify"; import { Chain, Transport, Account } from "viem"; import { KeyPairSigner } from "@solana/kit"; +import { ExactPaymentPayloadSchema } from "../types/verify/schemes/exact"; +import { DeferredPaymentPayloadSchema } from "../types/verify/schemes/deferred"; +import { DEFERRRED_SCHEME } from "../types/verify/schemes/deferred"; +import { EXACT_SCHEME } from "../types/verify/schemes/exact"; /** * Verifies a payment payload against the required payment details regardless of the scheme @@ -37,8 +42,8 @@ export async function verify< paymentRequirements: PaymentRequirements, config?: X402Config, ): Promise { - // exact scheme - if (paymentRequirements.scheme === "exact") { + if (paymentRequirements.scheme == EXACT_SCHEME) { + payload = ExactPaymentPayloadSchema.parse(payload); // evm if (SupportedEVMNetworks.includes(paymentRequirements.network)) { return verifyExactEvm( @@ -54,6 +59,32 @@ export async function verify< } } + if (paymentRequirements.scheme == DEFERRRED_SCHEME) { + payload = DeferredPaymentPayloadSchema.parse(payload); + if (SupportedEVMNetworks.includes(paymentRequirements.network)) { + if (!config?.schemeContext) { + return { + isValid: false, + invalidReason: "missing_scheme_context", + payer: payload.payload.voucher.buyer, + }; + } + const valid = await verifyDeferred( + client as EvmConnectedClient, + payload, + paymentRequirements, + config?.schemeContext, + ); + return valid; + } else { + return { + isValid: false, + invalidReason: "invalid_network", + payer: payload.payload.voucher.buyer, + }; + } + } + // unsupported scheme return { isValid: false, @@ -80,8 +111,8 @@ export async function settle( paymentRequirements: PaymentRequirements, config?: X402Config, ): Promise { - // exact scheme - if (paymentRequirements.scheme === "exact") { + if (paymentRequirements.scheme == EXACT_SCHEME) { + payload = ExactPaymentPayloadSchema.parse(payload); // evm if (SupportedEVMNetworks.includes(paymentRequirements.network)) { return await settleExactEvm( @@ -97,6 +128,35 @@ export async function settle( } } + if (paymentRequirements.scheme == DEFERRRED_SCHEME) { + payload = DeferredPaymentPayloadSchema.parse(payload); + if (SupportedEVMNetworks.includes(paymentRequirements.network)) { + if (!config?.schemeContext) { + return { + success: false, + errorReason: "missing_scheme_context", + transaction: "", + network: paymentRequirements.network, + payer: payload.payload.voucher.buyer, + }; + } + return settleDeferred( + client as EvmSignerWallet, + payload, + paymentRequirements, + config?.schemeContext, + ); + } else { + return { + success: false, + errorReason: "invalid_scheme", + transaction: "", + network: paymentRequirements.network, + payer: payload.payload.voucher.buyer, + }; + } + } + return { success: false, errorReason: "invalid_scheme", diff --git a/typescript/packages/x402/src/schemes/deferred/evm/client.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/client.test.ts new file mode 100644 index 0000000000..f4d26baeb1 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/client.test.ts @@ -0,0 +1,965 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { createSigner } from "../../../types/shared/evm"; +import { PaymentRequirements } from "../../../types/verify"; +import { + createPaymentHeader, + preparePaymentHeader, + signPaymentHeader, + createNewVoucher, + createPaymentExtraPayload, +} from "./client"; +import { + DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema, + DeferredPaymentPayloadSchema, + DeferredPaymentRequirementsSchema, + UnsignedDeferredPaymentPayload, +} from "../../../types/verify/schemes/deferred"; +import { encodePayment } from "./utils/paymentUtils"; + +vi.mock("./utils/paymentUtils", () => ({ + encodePayment: vi.fn().mockReturnValue("encoded-payment-header"), +})); + +vi.mock("../../../verify/useDeferred", () => ({ + useDeferredFacilitator: vi.fn(), +})); + +const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", +); +const buyerAddress = buyer.account.address; +const sellerAddress = "0x1234567890123456789012345678901234567890"; +const escrowAddress = "0xffFfFf12345678901234567890123456789fffFF"; +const assetAddress = "0x1111111111111111111111111111111111111111"; +const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; + +describe("preparePaymentHeader: new voucher", () => { + const mockPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-05-20T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid unsigned payment header", async () => { + const paymentHeader = await preparePaymentHeader(buyerAddress, 1, mockPaymentRequirements); + + const parsedPaymentRequirements = + DeferredPaymentRequirementsSchema.parse(mockPaymentRequirements); + + expect(paymentHeader).toEqual({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: undefined, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: parsedPaymentRequirements.maxAmountRequired, + asset: assetAddress, + timestamp: expect.any(Number), + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: expect.any(Number), + }, + }, + }); + }); + + it("should revert if paymentRequirement.extra required fields are missing", async () => { + const requiredFields = ["id", "escrow"]; + for (const field of requiredFields) { + const badPaymentRequirements = structuredClone(mockPaymentRequirements); + // @ts-expect-error - TODO: fix this + delete badPaymentRequirements.extra!.voucher[field]; + await expect(preparePaymentHeader(buyerAddress, 1, badPaymentRequirements)).rejects.toThrow(); + } + }); + + it("should revert if paymentRequirement.extra required fields have invalid values", async () => { + const requiredFields = ["id", "escrow"]; + for (const field of requiredFields) { + const badPaymentRequirements = structuredClone(mockPaymentRequirements); + // @ts-expect-error - TODO: fix this + badPaymentRequirements.extra!.voucher[field] = "0x"; + await expect(preparePaymentHeader(buyerAddress, 1, badPaymentRequirements)).rejects.toThrow(); + } + }); + + it("should handle different x402 versions", async () => { + const result = await preparePaymentHeader(buyerAddress, 2, mockPaymentRequirements); + expect(result.x402Version).toBe(2); + }); + + it("should include depositAuthorization in payload when provided in extraPayload", async () => { + const mockDepositAuthorization = { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c", + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }; + + const paymentHeader = await preparePaymentHeader( + buyerAddress, + 1, + mockPaymentRequirements, + mockDepositAuthorization, + ); + + expect(paymentHeader.payload.depositAuthorization).toEqual(mockDepositAuthorization); + expect(paymentHeader.payload.depositAuthorization?.permit).toBeDefined(); + }); + + it("should include depositAuthorization without permit when permit is not provided", async () => { + const mockDepositAuthorizationNoPermit = { + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }; + + const paymentHeader = await preparePaymentHeader( + buyerAddress, + 1, + mockPaymentRequirements, + mockDepositAuthorizationNoPermit, + ); + + expect(paymentHeader.payload.depositAuthorization).toEqual(mockDepositAuthorizationNoPermit); + expect(paymentHeader.payload.depositAuthorization?.permit).toBeUndefined(); + }); + + it("should not include depositAuthorization when extraPayload is not provided", async () => { + const paymentHeader = await preparePaymentHeader(buyerAddress, 1, mockPaymentRequirements); + + expect(paymentHeader.payload.depositAuthorization).toBeUndefined(); + }); + + it("should throw error when depositAuthorization in extraPayload is invalid", async () => { + const invalidDepositAuthorization = { + permit: { + owner: "invalid-address", // Invalid address format + spender: escrowAddress, + }, + depositAuthorization: { + buyer: buyerAddress, + }, + }; + + await expect( + preparePaymentHeader(buyerAddress, 1, mockPaymentRequirements, invalidDepositAuthorization), + ).rejects.toThrow(); + }); +}); + +describe("createNewVoucher", () => { + const mockPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-05-20T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid new voucher with correct properties", () => { + const voucher = createNewVoucher(buyerAddress, mockPaymentRequirements); + + expect(voucher).toEqual({ + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: Math.floor(Date.now() / 1000), + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, // 30 days + }); + }); + + it("should use buyer address as provided", () => { + const differentBuyerAddress = "0x9876543210987654321098765432109876543210"; + const voucher = createNewVoucher(differentBuyerAddress, mockPaymentRequirements); + + expect(voucher.buyer).toBe(differentBuyerAddress); + }); + + it("should set nonce to 0 for new vouchers", () => { + const voucher = createNewVoucher(buyerAddress, mockPaymentRequirements); + + expect(voucher.nonce).toBe(0); + }); + + it("should calculate expiry time correctly", () => { + const currentTime = Math.floor(Date.now() / 1000); + const voucher = createNewVoucher(buyerAddress, mockPaymentRequirements); + + expect(voucher.expiry).toBe(currentTime + 60 * 60 * 24 * 30); + }); + + it("should throw if payment requirements are invalid", () => { + const invalidRequirements = { + ...mockPaymentRequirements, + extra: { + type: "new", + // Missing voucher property + }, + } as PaymentRequirements; + + expect(() => createNewVoucher(buyerAddress, invalidRequirements)).toThrow(); + }); +}); + +describe("preparePaymentHeader: aggregated voucher", () => { + const mockAggregatedPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "aggregation", + signature: + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c", + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-05-20T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid unsigned payment header", async () => { + const paymentHeader = await preparePaymentHeader( + buyerAddress, + 1, + mockAggregatedPaymentRequirements, + ); + + const parsedExtra = DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema.parse( + mockAggregatedPaymentRequirements.extra, + ); + + expect(paymentHeader).toEqual({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: undefined, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: ( + BigInt(mockAggregatedPaymentRequirements.maxAmountRequired) + + BigInt(parsedExtra.voucher.valueAggregate) + ).toString(), + asset: assetAddress, + timestamp: expect.any(Number), + nonce: 1, + escrow: escrowAddress, + chainId: 84532, + expiry: expect.any(Number), + }, + }, + }); + }); + + it("should revert if voucher signature is invalid", async () => { + // Inject incorrect signature into mockAggregatedPaymentRequirements + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + // @ts-expect-error - TODO: fix this + paymentRequirements.extra!.signature = + "0x79ce97f6d1242aa7b6f4826efb553ed453fd6c7132c665d95bc226d5f3027dd5456d61ed1bd8da5de6cea4d8154070ff458300b6b84e0c9010f434af77ad3d291c"; + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Invalid voucher signature", + ); + }); + + it("should revert if paymentRequirement.extra required fields are missing", async () => { + const requiredFields = [ + "id", + "buyer", + "seller", + "valueAggregate", + "asset", + "timestamp", + "nonce", + "escrow", + "chainId", + "expiry", + ]; + for (const field of requiredFields) { + const badPaymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + // @ts-expect-error - TODO: fix this + delete badPaymentRequirements.extra!.voucher[field]; + await expect(preparePaymentHeader(buyerAddress, 1, badPaymentRequirements)).rejects.toThrow(); + } + }); + + it("should revert if paymentRequirement.extra required fields have invalid values", async () => { + const requiredFields = [ + "id", + "buyer", + "seller", + "valueAggregate", + "asset", + "timestamp", + "nonce", + "escrow", + "chainId", + "expiry", + ]; + for (const field of requiredFields) { + const badPaymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + // @ts-expect-error - TODO: fix this + badPaymentRequirements.extra!.voucher[field] = "0x"; + await expect(preparePaymentHeader(buyerAddress, 1, badPaymentRequirements)).rejects.toThrow(); + } + }); + + it("should handle different x402 versions", async () => { + const result = await preparePaymentHeader(buyerAddress, 2, mockAggregatedPaymentRequirements); + expect(result.x402Version).toBe(2); + }); + + it("should revert if voucher seller doesn't match payment requirements", async () => { + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + paymentRequirements.payTo = "0x9999999999999999999999999999999999999999"; + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Invalid voucher seller", + ); + }); + + it("should revert if voucher asset doesn't match payment requirements", async () => { + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + paymentRequirements.asset = "0x2222222222222222222222222222222222222222"; + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Invalid voucher asset", + ); + }); + + it("should revert if voucher chainId doesn't match payment requirements", async () => { + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + paymentRequirements.network = "base"; + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Invalid voucher chainId", + ); + }); + + it("should revert if voucher is expired", async () => { + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + // Set voucher expiry to a past date + // @ts-expect-error - TODO: fix this + paymentRequirements.extra!.voucher.expiry = 1715769600 - 1; // 1 second before timestamp + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Voucher expired", + ); + }); + + it("should revert if voucher timestamp is in the future", async () => { + const paymentRequirements = structuredClone(mockAggregatedPaymentRequirements); + // Set voucher timestamp to future + // @ts-expect-error - TODO: fix this + paymentRequirements.extra!.voucher.timestamp = Math.floor(Date.now() / 1000) + 3600; // 1 hour in future + + await expect(preparePaymentHeader(buyerAddress, 1, paymentRequirements)).rejects.toThrow( + "Voucher timestamp is in the future", + ); + }); +}); + +describe("signPaymentHeader", () => { + const mockUnsignedHeader: UnsignedDeferredPaymentPayload = { + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: undefined, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + const mockVoucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + + it("should sign the payment header and return a complete payload", async () => { + const signedPaymentPayload = await signPaymentHeader(buyer, mockUnsignedHeader); + + expect(signedPaymentPayload).toEqual({ + ...mockUnsignedHeader, + payload: { + ...mockUnsignedHeader.payload, + signature: mockVoucherSignature, + }, + }); + }); + + it("should preserve all original fields in the signed payload", async () => { + let signedPaymentPayload = await signPaymentHeader(buyer, mockUnsignedHeader); + signedPaymentPayload = DeferredPaymentPayloadSchema.parse(signedPaymentPayload); + + // Check that all original fields are preserved + expect(signedPaymentPayload.x402Version).toBe(mockUnsignedHeader.x402Version); + expect(signedPaymentPayload.scheme).toBe(mockUnsignedHeader.scheme); + expect(signedPaymentPayload.network).toBe(mockUnsignedHeader.network); + expect(signedPaymentPayload.payload.voucher).toEqual(mockUnsignedHeader.payload.voucher); + }); + + it("should throw an error if signing fails", async () => { + const badUnsignedHeader = {} as UnsignedDeferredPaymentPayload; + await expect(signPaymentHeader(buyer, badUnsignedHeader)).rejects.toThrow(); + }); +}); + +describe("createPaymentHeader", () => { + const mockPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "aggregation", + signature: + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c", + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + + const mockSignedPayment = { + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: + "0x583c4822217a0a8d9f079800a4abf48ea4f366438181cf24a53a95567e1430442d3c8974edbd0b9d3d9c0d1231c6bbf837848986a7157f7f6056e2f6d4d7433a1b", + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "2000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 1, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-05-20T00:00:00Z")); + vi.clearAllMocks(); + }); + + it("should create and encode a payment header", async () => { + const result = await createPaymentHeader(buyer, 1, mockPaymentRequirements); + expect(result).toBe("encoded-payment-header"); + expect(vi.mocked(encodePayment)).toHaveBeenCalledWith( + expect.objectContaining({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: expect.objectContaining({ + signature: expect.any(String), + voucher: expect.objectContaining({ + id: mockSignedPayment.payload.voucher.id, + buyer: mockSignedPayment.payload.voucher.buyer, + seller: mockSignedPayment.payload.voucher.seller, + valueAggregate: mockSignedPayment.payload.voucher.valueAggregate, + asset: mockSignedPayment.payload.voucher.asset, + timestamp: expect.any(Number), + nonce: mockSignedPayment.payload.voucher.nonce, + escrow: mockSignedPayment.payload.voucher.escrow, + chainId: mockSignedPayment.payload.voucher.chainId, + expiry: expect.any(Number), + }), + }), + }), + ); + }); + + it("should throw an error if signing fails", async () => { + await expect(createPaymentHeader(buyer, 1, {} as PaymentRequirements)).rejects.toThrow(); + }); + + it("should throw an error if encoding fails", async () => { + const error = new Error("Encoding failed"); + vi.mocked(encodePayment).mockImplementation(() => { + throw error; + }); + + await expect(createPaymentHeader(buyer, 1, mockPaymentRequirements)).rejects.toThrow( + "Encoding failed", + ); + }); +}); + +describe("createPaymentExtraPayload", () => { + const mockPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + account: { + balance: "500000", // Below threshold + assetAllowance: "0", + assetPermitNonce: "0", + facilitator: "https://facilitator.example.com", + }, + }, + }; + + const mockDepositConfig = { + asset: assetAddress, + assetDomain: { + name: "USD Coin", + version: "2", + }, + threshold: "10000", + amount: "1000000", + }; + + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2024-05-20T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should return undefined when extra.account is undefined", async () => { + const paymentReqs = { + ...mockPaymentRequirements, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + } as PaymentRequirements; + + const result = await createPaymentExtraPayload(buyer, paymentReqs, [mockDepositConfig]); + expect(result).toBeUndefined(); + }); + + it("should return undefined when balance is sufficient", async () => { + const paymentReqs = { + ...mockPaymentRequirements, + extra: { + ...mockPaymentRequirements.extra, + account: { + balance: "10000000", // High balance + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.example.com", + }, + }, + } as PaymentRequirements; + + const result = await createPaymentExtraPayload(buyer, paymentReqs, [mockDepositConfig]); + expect(result).toBeUndefined(); + }); + + it("should return undefined when facilitator check shows sufficient balance", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "10000000", // High balance from facilitator + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(mockGetEscrowAccountDetails).toHaveBeenCalledWith( + buyerAddress, + sellerAddress, + assetAddress, + escrowAddress, + 84532, + ); + expect(result).toBeUndefined(); + }); + + it("should return undefined when facilitator returns error", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + error: "facilitator_error", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeUndefined(); + }); + + it("should create deposit authorization with permit when allowance is insufficient", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", // Low balance + assetAllowance: "0", // No allowance + assetPermitNonce: "5", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.permit).toBeDefined(); + expect(result?.permit?.owner).toBe(buyerAddress); + expect(result?.permit?.spender).toBe(escrowAddress); + expect(result?.permit?.value).toBe("1000000"); + expect(result?.permit?.nonce).toBe("5"); + expect(result?.permit?.deadline).toBe(Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30); + expect(result?.permit?.domain).toEqual({ + name: "USD Coin", + version: "2", + }); + expect(result?.permit?.signature).toBeDefined(); + + expect(result?.depositAuthorization).toBeDefined(); + expect(result?.depositAuthorization.buyer).toBe(buyerAddress); + expect(result?.depositAuthorization.seller).toBe(sellerAddress); + expect(result?.depositAuthorization.asset).toBe(assetAddress); + expect(result?.depositAuthorization.amount).toBe("1000000"); + expect(result?.depositAuthorization.nonce).toMatch(/^0x[0-9a-f]{64}$/); + expect(result?.depositAuthorization.expiry).toBe( + Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30, + ); + expect(result?.depositAuthorization.signature).toBeDefined(); + }); + + it("should create deposit authorization without permit when allowance is sufficient", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", // Low balance + assetAllowance: "2000000", // Sufficient allowance + assetPermitNonce: "5", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.permit).toBeUndefined(); // No permit needed + expect(result?.depositAuthorization).toBeDefined(); + expect(result?.depositAuthorization.buyer).toBe(buyerAddress); + expect(result?.depositAuthorization.seller).toBe(sellerAddress); + expect(result?.depositAuthorization.asset).toBe(assetAddress); + expect(result?.depositAuthorization.amount).toBe("1000000"); + expect(result?.depositAuthorization.signature).toBeDefined(); + }); + + it("should use default USDC config when no matching deposit config is provided", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", + assetAllowance: "2000000", + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const usdcPaymentReqs = { + ...mockPaymentRequirements, + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", // Base Sepolia USDC + } as PaymentRequirements; + + const result = await createPaymentExtraPayload(buyer, usdcPaymentReqs, []); + + expect(result).toBeDefined(); + expect(result?.depositAuthorization.amount).toBe("1000000"); // Default 1 USDC + }); + + it("should handle balance exactly at threshold plus maxAmountRequired", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "1010000", // Exactly threshold (10000) + maxAmountRequired (1000000) + assetAllowance: "2000000", + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeUndefined(); // Should not need deposit + }); + + it("should create deposit when balance is one unit below threshold plus maxAmountRequired", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "1009999", // One below threshold (10000) + maxAmountRequired (1000000) + assetAllowance: "2000000", + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.depositAuthorization).toBeDefined(); + }); + + it("should use custom deposit config when provided", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", + assetAllowance: "0", + assetPermitNonce: "10", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const customDepositConfig = { + asset: assetAddress, + assetDomain: { + name: "Custom Token", + version: "1", + }, + threshold: "50000", + amount: "5000000", + }; + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + customDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.depositAuthorization.amount).toBe("5000000"); + expect(result?.permit?.value).toBe("5000000"); + expect(result?.permit?.domain).toEqual({ + name: "Custom Token", + version: "1", + }); + }); + + it("should handle allowance exactly at deposit amount", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", + assetAllowance: "1000000", // Exactly the deposit amount + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.permit).toBeUndefined(); // Allowance is sufficient + }); + + it("should create permit when allowance is one unit below deposit amount", async () => { + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetEscrowAccountDetails = vi.fn().mockResolvedValue({ + balance: "500000", + assetAllowance: "999999", // One below deposit amount + assetPermitNonce: "0", + }); + + vi.mocked(useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetEscrowAccountDetails, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any); + + const result = await createPaymentExtraPayload(buyer, mockPaymentRequirements, [ + mockDepositConfig, + ]); + + expect(result).toBeDefined(); + expect(result?.permit).toBeDefined(); // Permit needed + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/client.ts b/typescript/packages/x402/src/schemes/deferred/evm/client.ts new file mode 100644 index 0000000000..017505ece1 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/client.ts @@ -0,0 +1,352 @@ +import { Address, Chain, Client, getAddress, Hex, LocalAccount, toHex, Transport } from "viem"; +import { getNetworkId } from "../../../shared/network"; +import { isSignerWallet, SignerWallet } from "../../../types/shared/evm"; +import { PaymentPayload, PaymentRequirements, UnsignedPaymentPayload } from "../../../types/verify"; +import { + DeferredEscrowDepositAuthorization, + DeferredEscrowDepositAuthorizationConfig, + DeferredEscrowDepositAuthorizationSchema, + DeferredEscrowDepositAuthorizationSignedPermit, + DeferredEvmPayloadVoucher, + DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema, + DeferredEvmPaymentRequirementsExtraNewVoucherSchema, + DeferredPaymentRequirementsSchema, + DEFERRRED_SCHEME, + UnsignedDeferredPaymentPayload, + UnsignedDeferredPaymentPayloadSchema, +} from "../../../types/verify/schemes/deferred"; +import { + signPermit, + signDepositAuthorizationInner, + signVoucher, + verifyVoucherSignature, +} from "./sign"; +import { encodePayment } from "./utils/paymentUtils"; +import { getUsdcChainConfigForChain } from "../../../shared/evm"; +import { randomBytes } from "node:crypto"; +import { useDeferredFacilitator } from "../../../verify/useDeferred"; + +const EXPIRY_TIME = 60 * 60 * 24 * 30; // 30 days + +/** + * Prepares an unsigned payment header with the given sender address and payment requirements. + * + * @param buyer - The sender's address from which the payment will be made + * @param x402Version - The version of the X402 protocol to use + * @param paymentRequirements - The payment requirements containing scheme and network information + * @param extraPayload - Extra payload to be included in the payment header creation + * @returns An unsigned payment payload containing authorization details + */ +export async function preparePaymentHeader( + buyer: Address, + x402Version: number, + paymentRequirements: PaymentRequirements, + extraPayload?: Record, +): Promise { + const deferredPaymentRequirements = DeferredPaymentRequirementsSchema.parse(paymentRequirements); + + const voucher = + deferredPaymentRequirements.extra.type === "new" + ? createNewVoucher(buyer, deferredPaymentRequirements) + : await aggregateVoucher(buyer, deferredPaymentRequirements); + + const depositAuthorization = extraPayload + ? DeferredEscrowDepositAuthorizationSchema.parse(extraPayload) + : undefined; + + return { + x402Version, + scheme: DEFERRRED_SCHEME, + network: deferredPaymentRequirements.network, + payload: { + signature: undefined, + voucher: voucher, + ...(depositAuthorization && { depositAuthorization }), + }, + } as const satisfies UnsignedDeferredPaymentPayload; +} + +/** + * Creates a new voucher with the given payment requirements + * + * @param buyer - The sender's address from which the payment will be made + * @param paymentRequirements - The payment requirements containing scheme and network information + * @returns The new voucher + */ +export function createNewVoucher( + buyer: Address, + paymentRequirements: PaymentRequirements, +): DeferredEvmPayloadVoucher { + const extra = DeferredEvmPaymentRequirementsExtraNewVoucherSchema.parse( + paymentRequirements.extra, + ); + + return { + id: extra.voucher.id.toLowerCase(), + buyer: getAddress(buyer), + seller: getAddress(paymentRequirements.payTo), + valueAggregate: paymentRequirements.maxAmountRequired, + asset: getAddress(paymentRequirements.asset), + timestamp: Math.floor(Date.now() / 1000), + nonce: 0, + escrow: getAddress(extra.voucher.escrow), + chainId: getNetworkId(paymentRequirements.network), + expiry: Math.floor(Date.now() / 1000) + EXPIRY_TIME, + }; +} + +/** + * Aggregates a voucher with new payment requirements + * + * @param buyer - The sender's address from which the payment will be made + * @param paymentRequirements - The payment requirements containing scheme and network information + * @returns The aggregated voucher + */ +export async function aggregateVoucher( + buyer: Address, + paymentRequirements: PaymentRequirements, +): Promise { + const extra = DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema.parse( + paymentRequirements.extra, + ); + const { id, escrow, seller, valueAggregate, asset, nonce, chainId, expiry, timestamp } = + extra.voucher; + const now = Math.floor(Date.now() / 1000); + + // verify previous voucher matches payment requirements + if (getAddress(paymentRequirements.payTo) !== getAddress(seller)) { + throw new Error("Invalid voucher seller"); + } + if (getAddress(paymentRequirements.asset) !== getAddress(asset)) { + throw new Error("Invalid voucher asset"); + } + if (getNetworkId(paymentRequirements.network) !== chainId) { + throw new Error("Invalid voucher chainId"); + } + if (now > expiry) { + throw new Error("Voucher expired"); + } + if (now < timestamp) { + throw new Error("Voucher timestamp is in the future"); + } + + // verify signature is valid and the voucher's buyer is the client + const isValid = await verifyVoucherSignature(extra.voucher, extra.signature as Hex, buyer); + if (!isValid) { + throw new Error("Invalid voucher signature"); + } + + return { + id, + buyer, + seller, + valueAggregate: ( + BigInt(paymentRequirements.maxAmountRequired) + BigInt(valueAggregate) + ).toString(), + asset, + timestamp: now, + nonce: nonce + 1, + escrow, + chainId, + expiry: now + EXPIRY_TIME, + }; +} + +/** + * Signs a payment header using the provided client and payment requirements. + * + * @param client - The signer wallet instance used to sign the payment header + * @param unsignedPaymentPayload - The unsigned payment payload to be signed + * @returns A promise that resolves to the signed payment payload + */ +export async function signPaymentHeader( + client: SignerWallet | LocalAccount, + unsignedPaymentPayload: UnsignedPaymentPayload, +): Promise { + const unsignedDeferredPaymentPayload = + UnsignedDeferredPaymentPayloadSchema.parse(unsignedPaymentPayload); + const { signature } = await signVoucher(client, unsignedDeferredPaymentPayload.payload.voucher); + + return { + ...unsignedDeferredPaymentPayload, + payload: { + ...unsignedDeferredPaymentPayload.payload, + signature, + }, + }; +} + +/** + * Creates a complete payment payload by preparing and signing a payment header. + * + * @param client - The signer wallet instance used to create and sign the payment + * @param x402Version - The version of the X402 protocol to use + * @param paymentRequirements - The payment requirements containing scheme and network information + * @param extraPayload - Extra payload to be included in the payment header creation + * @returns A promise that resolves to the complete signed payment payload + */ +export async function createPayment( + client: SignerWallet | LocalAccount, + x402Version: number, + paymentRequirements: PaymentRequirements, + extraPayload?: Record, +): Promise { + const from = isSignerWallet(client) ? client.account!.address : client.address; + const unsignedPaymentHeader = await preparePaymentHeader( + from, + x402Version, + paymentRequirements, + extraPayload, + ); + return signPaymentHeader(client, unsignedPaymentHeader); +} + +/** + * Creates and encodes a payment header for the given client and payment requirements. + * + * @param client - The signer wallet instance used to create the payment header + * @param x402Version - The version of the X402 protocol to use + * @param paymentRequirements - The payment requirements containing scheme and network information + * @param extraPayload - Extra payload to be included in the payment header creation + * @returns A promise that resolves to the encoded payment header string + */ +export async function createPaymentHeader( + client: SignerWallet | LocalAccount, + x402Version: number, + paymentRequirements: PaymentRequirements, + extraPayload?: Record, +): Promise { + const payment = await createPayment(client, x402Version, paymentRequirements, extraPayload); + return encodePayment(payment); +} + +/** + * Creates the payment extra payload for deferred scheme with deposit with authorization flow. + * + * __Note__: This implementation requires the buyer to trust the seller provided balance to decide if they deposit additional + * funds to the escrow. A malicious seller could manipulate the value and force additional deposits from the buyer, those funds + * would not be at risk as they could be withdrawn, however it would be a form of abuse. + * TODO: We could improve this by having this client-side function verify the balance themselves, that requires however the client + * to make a direct call to the facilitator. + * + * @param client - The signer wallet instance used to create the payment extra payload + * @param paymentRequirements - The payment requirements containing scheme and network information + * @param depositConfigs - The auto deposit configurations to use for the deposit with authorization flow + * @returns The extra payload or undefined + */ +export async function createPaymentExtraPayload( + client: SignerWallet | LocalAccount, + paymentRequirements: PaymentRequirements, + depositConfigs: DeferredEscrowDepositAuthorizationConfig[], +): Promise { + const { network, asset, extra, maxAmountRequired } = + DeferredPaymentRequirementsSchema.parse(paymentRequirements); + const buyer = (client as LocalAccount).address || (client as Client).account?.address; + + // No account info, no deposit + if (extra.account === undefined) { + return; + } + + let depositConfig = depositConfigs.find(config => getAddress(config.asset) === getAddress(asset)); + + if (depositConfig === undefined) { + const chainId = getNetworkId(network); + const usdc = getUsdcChainConfigForChain(chainId); + + // No matching asset, no deposit + if (usdc === undefined) { + return; + } + + depositConfig = { + asset: usdc.usdcAddress, + assetDomain: { + name: usdc.usdcName, + version: "2", // TODO: use getVersion + }, + threshold: "10000", // 0.01 USDC + amount: "1000000", // 1 USDC + }; + } + + // Enough balance, no deposit + if ( + BigInt(extra.account.balance) >= + BigInt(depositConfig.threshold) + BigInt(maxAmountRequired) + ) { + return; + } + + // Ensure the deposit is actually needed + // This creates a client/buyer <> facilitator interaction but it's necessary to avoid having to trust the seller + const { getBuyerData } = useDeferredFacilitator({ + url: extra.account.facilitator as `${string}://${string}`, + }); + const buyerData = await getBuyerData( + buyer, + paymentRequirements.payTo, + asset, + extra.voucher.escrow, + getNetworkId(network), + ); + if ("error" in buyerData) { + return; + } + + // Re-check balance using the data obtained from the facilitator + if (BigInt(buyerData.balance) >= BigInt(depositConfig.threshold) + BigInt(maxAmountRequired)) { + return; + } + + // Build ERC20 permit if needed + let signedErc20Permit: DeferredEscrowDepositAuthorizationSignedPermit | undefined; + if (BigInt(buyerData.assetAllowance) < BigInt(depositConfig.amount)) { + const erc20Permit = { + nonce: buyerData.assetPermitNonce, + value: depositConfig.amount, + domain: { + name: depositConfig.assetDomain.name, + version: depositConfig.assetDomain.version, + }, + owner: getAddress(buyer), + spender: getAddress(extra.voucher.escrow), + deadline: Math.floor(Date.now() / 1000) + EXPIRY_TIME, + }; + const erc20PermitSignature = await signPermit( + client, + erc20Permit, + getNetworkId(network), + getAddress(asset), + ); + signedErc20Permit = { + ...erc20Permit, + signature: erc20PermitSignature.signature, + }; + } + + // Build deposit authorization + const depositAuthorization = { + buyer: getAddress(buyer), + seller: getAddress(paymentRequirements.payTo), + asset: getAddress(asset), + amount: depositConfig.amount, + nonce: toHex(randomBytes(32)), + expiry: Math.floor(Date.now() / 1000) + EXPIRY_TIME, + }; + + const depositAuthorizationSignature = await signDepositAuthorizationInner( + client, + depositAuthorization, + getNetworkId(network), + getAddress(extra.voucher.escrow), + ); + + return { + ...(signedErc20Permit && { permit: signedErc20Permit }), + depositAuthorization: { + ...depositAuthorization, + signature: depositAuthorizationSignature.signature, + }, + }; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/facilitator.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/facilitator.test.ts new file mode 100644 index 0000000000..12ee0e37f4 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/facilitator.test.ts @@ -0,0 +1,1530 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { Address, Chain, Log, TransactionReceipt, Transport } from "viem"; +import { createSigner, ConnectedClient, SignerWallet } from "../../../types/shared/evm"; +import { PaymentRequirements, SchemeContext } from "../../../types/verify"; +import { DeferredPaymentPayload, DEFERRRED_SCHEME } from "../../../types/verify/schemes/deferred"; +import { + verify, + settle, + settleVoucher, + depositWithAuthorization, + flushWithAuthorization, + getAccountData, +} from "./facilitator"; +import { VoucherStore } from "./store"; +import * as verifyModule from "./verify"; + +// Mock the verify module +vi.mock("./verify", () => ({ + verifyPaymentRequirements: vi.fn(), + verifyVoucherContinuity: vi.fn(), + verifyVoucherSignatureWrapper: vi.fn(), + verifyVoucherAvailability: vi.fn(), + verifyVoucherOnchainState: vi.fn(), + verifyDepositAuthorizationSignatureAndContinuity: vi.fn(), + verifyDepositAuthorizationOnchainState: vi.fn(), + verifyFlushAuthorization: vi.fn(), + getOnchainVerificationData: vi.fn(), +})); + +const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", +); +const buyerAddress = buyer.account.address; +const sellerAddress = "0x1234567890123456789012345678901234567890"; +const escrowAddress = "0xffffff12345678901234567890123456789fffff"; +const assetAddress = "0x1111111111111111111111111111111111111111"; +const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; +const voucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + +/** + * Mock VoucherStore class for testing + */ +class MockVoucherStore extends VoucherStore { + getVoucher = vi.fn(); + getVoucherSeries = vi.fn(); + getVouchers = vi.fn(); + getAvailableVoucher = vi.fn(); + settleVoucher = vi.fn(); + storeVoucher = vi.fn(); + getVoucherCollections = vi.fn(); +} + +const mockVoucherStore = new MockVoucherStore(); + +describe("facilitator - verify", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockPaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: mockVoucher, + }, + }; + + const mockPaymentRequirements: PaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + let mockClient: ConnectedClient; + let mockSchemeContext: SchemeContext; + + beforeEach(() => { + vi.clearAllMocks(); + + mockClient = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as ConnectedClient; + + mockSchemeContext = { + deferred: { + voucherStore: mockVoucherStore, + }, + }; + + // Mock all verification functions to return success by default + vi.mocked(verifyModule.verifyPaymentRequirements).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherContinuity).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherSignatureWrapper).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherAvailability).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherOnchainState).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.getOnchainVerificationData).mockResolvedValue({ + isValid: true, + data: { + voucherOutstanding: BigInt(1000000), + voucherCollectable: BigInt(1000000), + availableBalance: BigInt(1000000), + allowance: BigInt(1000000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }, + }); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should return valid response when all verifications pass", async () => { + const result = await verify( + mockClient, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + isValid: true, + invalidReason: undefined, + payer: buyerAddress, + }); + }); + + it("should return invalid response when payment requirements verification fails", async () => { + vi.mocked(verifyModule.verifyPaymentRequirements).mockReturnValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_scheme", + }); + + const result = await verify( + mockClient, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_scheme", + }); + }); + + it("should return invalid response when voucher continuity verification fails", async () => { + vi.mocked(verifyModule.verifyVoucherContinuity).mockReturnValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expired", + payer: buyerAddress, + }); + + const result = await verify( + mockClient, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expired", + payer: buyerAddress, + }); + }); + + it("should return invalid response when voucher signature verification fails", async () => { + vi.mocked(verifyModule.verifyVoucherSignatureWrapper).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + payer: buyerAddress, + }); + + const result = await verify( + mockClient, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + payer: buyerAddress, + }); + }); + + it("should verify previous voucher availability for aggregation type", async () => { + const aggregationRequirements: PaymentRequirements = { + ...mockPaymentRequirements, + extra: { + type: "aggregation", + signature: voucherSignature, + voucher: mockVoucher, + }, + }; + + await verify(mockClient, mockPaymentPayload, aggregationRequirements, mockSchemeContext); + + expect(verifyModule.verifyVoucherAvailability).toHaveBeenCalledWith( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, // mockVoucher is the previous voucher + mockVoucherStore, + ); + }); + + it("should skip previous voucher verification for new voucher type", async () => { + await verify(mockClient, mockPaymentPayload, mockPaymentRequirements, mockSchemeContext); + + expect(verifyModule.verifyVoucherAvailability).not.toHaveBeenCalled(); + }); + + it("should return invalid response when onchain state verification fails", async () => { + vi.mocked(verifyModule.verifyVoucherOnchainState).mockResolvedValue({ + isValid: false, + invalidReason: "insufficient_funds", + payer: buyerAddress, + }); + + const result = await verify( + mockClient, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "insufficient_funds", + payer: buyerAddress, + }); + }); +}); + +describe("facilitator - settle", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }; + + const mockPaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: mockVoucher, + }, + }; + + const mockPaymentRequirements: PaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + let mockSchemeContext: SchemeContext; + let mockWallet: SignerWallet; + + beforeEach(() => { + vi.clearAllMocks(); + + mockSchemeContext = { + deferred: { + voucherStore: mockVoucherStore, + }, + }; + + // Mock successful verification by default + vi.mocked(verifyModule.verifyPaymentRequirements).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherContinuity).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherSignatureWrapper).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherOnchainState).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherAvailability).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.getOnchainVerificationData).mockResolvedValue({ + isValid: true, + data: { + voucherOutstanding: BigInt(1000000), + voucherCollectable: BigInt(1000000), + availableBalance: BigInt(1000000), + allowance: BigInt(1000000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }, + }); + + // Mock successful voucher store settlement + vi.mocked(mockVoucherStore.settleVoucher).mockResolvedValue({ success: true }); + vi.mocked(mockVoucherStore.getVoucher).mockResolvedValue(mockVoucher); + + // Create a proper mock wallet with all required properties + mockWallet = { + account: { + address: buyerAddress, + }, + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn().mockResolvedValue("0x1234567890abcdef"), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ + status: "success", + logs: [ + { + data: "0x000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000000000000000000000000000000000000050", + topics: [ + "0xfe7c1ad1ce8265245e3420fdcd8d27904701eb6c1d348c3e1704aebfaa8a50e0", + "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + "0x00000000000000000000000080cdf1957ebb7a2df22dd8913753a4423ff4272e", + "0x000000000000000000000000c93d37ad45c907ee1b27a02b2e1bd823ba9d379c", + ], + } as unknown as Log, + ], + }), + } as unknown as SignerWallet; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should settle voucher successfully when all verifications pass", async () => { + const result = await settle( + mockWallet, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + success: true, + network: "base-sepolia", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + }); + + it("should return error when re-verification fails", async () => { + vi.mocked(verifyModule.verifyPaymentRequirements).mockReturnValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_scheme", + }); + + const result = await settle( + buyer, + mockPaymentPayload, + mockPaymentRequirements, + mockSchemeContext, + ); + + expect(result).toEqual({ + success: false, + network: "base-sepolia", + transaction: "", + errorReason: "invalid_deferred_evm_payload_scheme", + payer: buyerAddress, + }); + }); +}); + +describe("facilitator - settleVoucher", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }; + + let mockVoucherStore: VoucherStore; + let mockWallet: SignerWallet; + + beforeEach(() => { + vi.clearAllMocks(); + + mockVoucherStore = { + settleVoucher: vi.fn().mockResolvedValue({ success: true }), + getVoucher: vi.fn().mockResolvedValue(mockVoucher), + } as unknown as VoucherStore; + + // Mock successful verification by default + vi.mocked(verifyModule.verifyVoucherSignatureWrapper).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherOnchainState).mockReturnValue({ isValid: true }); + vi.mocked(verifyModule.verifyVoucherAvailability).mockResolvedValue({ isValid: true }); + vi.mocked(verifyModule.getOnchainVerificationData).mockResolvedValue({ + isValid: true, + data: { + voucherOutstanding: BigInt(1000000), + voucherCollectable: BigInt(1000000), + availableBalance: BigInt(1000000), + allowance: BigInt(1000000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }, + }); + + // Mock successful voucher store settlement + vi.mocked(mockVoucherStore.settleVoucher).mockResolvedValue({ success: true }); + + // Create a proper mock wallet with all required properties + mockWallet = { + account: { + address: buyerAddress, + }, + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn().mockResolvedValue("0x1234567890abcdef"), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ + status: "success", + logs: [ + { + data: "0x000000000000000000000000036cbd53842c5426634e7929541ec2318f3dcf7e0000000000000000000000000000000000000000000000000000000000000050", + topics: [ + "0xfe7c1ad1ce8265245e3420fdcd8d27904701eb6c1d348c3e1704aebfaa8a50e0", + "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + "0x00000000000000000000000080cdf1957ebb7a2df22dd8913753a4423ff4272e", + "0x000000000000000000000000c93d37ad45c907ee1b27a02b2e1bd823ba9d379c", + ], + } as unknown as Log, + ], + }), + } as unknown as SignerWallet; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should settle voucher successfully", async () => { + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + expect(mockVoucherStore.settleVoucher).toHaveBeenCalledWith( + mockVoucher, + "0x1234567890abcdef", + 0n, // mocked amount in log + ); + }); + + it("should return error when voucher not found in store", async () => { + vi.mocked(verifyModule.verifyVoucherAvailability).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_found", + }); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(verifyModule.verifyVoucherAvailability).toHaveBeenCalledWith( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, + mockVoucherStore, + ); + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_voucher_not_found", + transaction: "", + payer: buyerAddress, + }); + }); + + it("should return error when voucher signature verification fails", async () => { + vi.mocked(verifyModule.verifyVoucherSignatureWrapper).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + }); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_signature", + transaction: "", + payer: buyerAddress, + }); + }); + + it("should return error when onchain state verification fails", async () => { + vi.mocked(verifyModule.verifyVoucherOnchainState).mockReturnValue({ + isValid: false, + invalidReason: "insufficient_funds", + }); + + const result = await settleVoucher(buyer, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "insufficient_funds", + transaction: "", + payer: buyerAddress, + }); + }); + + it("should return error when contract transaction fails", async () => { + mockWallet.writeContract = vi.fn().mockRejectedValue(new Error("Transaction failed")); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + }); + + it("should return error when transaction receipt shows failure", async () => { + mockWallet.waitForTransactionReceipt = vi.fn().mockResolvedValue({ + status: "reverted", + logs: [], + }); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_state", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + }); + + it("should return error when voucher store settlement fails", async () => { + vi.mocked(mockVoucherStore.settleVoucher).mockResolvedValue({ success: false }); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_voucher_could_not_settle_store", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + }); + + it("should return error when voucher store throws exception", async () => { + vi.mocked(mockVoucherStore.settleVoucher).mockRejectedValue(new Error("Store error")); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_voucher_error_settling_store", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + }); + + it("should handle empty logs when parsing voucher collected event", async () => { + mockWallet.waitForTransactionReceipt = vi.fn().mockResolvedValue({ + status: "success", + logs: [], + }); + + const result = await settleVoucher(mockWallet, mockVoucher, voucherSignature, mockVoucherStore); + + expect(result.success).toBe(true); + expect(mockVoucherStore.settleVoucher).toHaveBeenCalledWith( + mockVoucher, + "0x1234567890abcdef", + 0n, // Default amount when no logs + ); + }); +}); + +describe("facilitator - depositWithAuthorization", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }; + + const mockDepositAuthorizationWithPermit = { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c", + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }; + + const mockDepositAuthorizationWithoutPermit = { + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }; + + let mockWallet: SignerWallet; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock successful verification by default + vi.mocked(verifyModule.verifyDepositAuthorizationSignatureAndContinuity).mockResolvedValue({ + isValid: true, + }); + + // Mock successful onchain state verification by default + vi.mocked(verifyModule.verifyDepositAuthorizationOnchainState).mockReturnValue({ + isValid: true, + }); + + vi.mocked(verifyModule.getOnchainVerificationData).mockResolvedValue({ + isValid: true, + data: { + voucherOutstanding: BigInt(1000000), + voucherCollectable: BigInt(1000000), + availableBalance: BigInt(1000000), + allowance: BigInt(1000000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }, + }); + + // Create a proper mock wallet with all required properties + mockWallet = { + account: { + address: buyerAddress, + }, + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn().mockResolvedValue("0x1234567890abcdef"), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ + status: "success", + logs: [], + }), + } as unknown as SignerWallet; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should deposit with authorization successfully with permit", async () => { + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have called writeContract twice (permit + depositWithAuthorization) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(2); + + // Verify permit call - args: [owner, spender, value, deadline, v, r, s] + const permitCall = vi.mocked(mockWallet.writeContract).mock.calls[0][0]; + expect(permitCall).toMatchObject({ + address: assetAddress, + functionName: "permit", + chain: mockWallet.chain, + }); + expect(permitCall.args).toHaveLength(7); + expect(permitCall.args?.[0]).toBe(buyerAddress); + expect((permitCall.args?.[1] as Address).toLowerCase()).toBe(escrowAddress.toLowerCase()); + expect(permitCall.args?.[2]).toBe(BigInt("1000000")); + expect(permitCall.args?.[3]).toBe(BigInt(mockDepositAuthorizationWithPermit.permit.deadline)); + + // Verify depositWithAuthorization call + const depositCall = vi.mocked(mockWallet.writeContract).mock.calls[1][0]; + expect(depositCall).toMatchObject({ + address: escrowAddress, + functionName: "depositWithAuthorization", + chain: mockWallet.chain, + }); + expect(depositCall.args).toHaveLength(2); + expect(depositCall.args?.[0]).toMatchObject({ + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: BigInt("1000000"), + }); + + // Should have waited for both receipts + expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledTimes(2); + }); + + it("should deposit with authorization successfully without permit", async () => { + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithoutPermit, + ); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have called writeContract only once (depositWithAuthorization, no permit) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + + // Verify depositWithAuthorization call + const depositCall = vi.mocked(mockWallet.writeContract).mock.calls[0][0]; + expect(depositCall).toMatchObject({ + address: escrowAddress, + functionName: "depositWithAuthorization", + chain: mockWallet.chain, + }); + expect(depositCall.args).toHaveLength(2); + expect(depositCall.args?.[0]).toMatchObject({ + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: BigInt("1000000"), + }); + + // Should have waited for only one receipt + expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledTimes(1); + }); + + it("should return error when deposit authorization verification fails", async () => { + vi.mocked(verifyModule.verifyDepositAuthorizationSignatureAndContinuity).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_signature", + }); + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_permit_signature", + transaction: "", + payer: buyerAddress, + }); + + // Should not have called any contract writes + expect(mockWallet.writeContract).not.toHaveBeenCalled(); + }); + + it("should return error when permit transaction fails", async () => { + mockWallet.writeContract = vi.fn().mockRejectedValueOnce(new Error("Permit failed")); + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + + // Should have only called writeContract once (permit failed) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + }); + + it("should return error when permit transaction receipt shows failure", async () => { + mockWallet.waitForTransactionReceipt = vi + .fn() + .mockResolvedValueOnce({ status: "reverted", logs: [] }); + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_state", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have only called writeContract once (permit succeeded but reverted) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledTimes(1); + }); + + it("should return error when depositWithAuthorization transaction fails with permit", async () => { + mockWallet.writeContract = vi + .fn() + .mockResolvedValueOnce("0x1234567890abcdef") // permit succeeds + .mockRejectedValueOnce(new Error("Deposit failed")); // depositWithAuthorization fails + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + + // Should have called writeContract twice + expect(mockWallet.writeContract).toHaveBeenCalledTimes(2); + }); + + it("should return error when depositWithAuthorization transaction fails without permit", async () => { + mockWallet.writeContract = vi.fn().mockRejectedValueOnce(new Error("Deposit failed")); + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithoutPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + + // Should have called writeContract once + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + }); + + it("should return error when depositWithAuthorization receipt shows failure", async () => { + mockWallet.waitForTransactionReceipt = vi + .fn() + .mockResolvedValueOnce({ status: "success", logs: [] }) // permit succeeds + .mockResolvedValueOnce({ status: "reverted", logs: [] }); // depositWithAuthorization reverts + + const result = await depositWithAuthorization( + mockWallet, + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_state", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have completed both transactions + expect(mockWallet.writeContract).toHaveBeenCalledTimes(2); + expect(mockWallet.waitForTransactionReceipt).toHaveBeenCalledTimes(2); + }); +}); + +describe("facilitator - getAccountData", () => { + let mockClient: ConnectedClient; + let mockVoucherStore: VoucherStore; + + beforeEach(() => { + vi.clearAllMocks(); + + mockClient = { + chain: { id: 84532 }, + readContract: vi.fn(), + } as unknown as ConnectedClient; + + mockVoucherStore = { + getVouchers: vi.fn(), + } as unknown as VoucherStore; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should return account details successfully with no outstanding vouchers", async () => { + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue([]); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt(5000000), // balance + BigInt(2000000), // allowance + BigInt(10), // nonce + ]); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(mockVoucherStore.getVouchers).toHaveBeenCalledWith( + { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + escrow: escrowAddress, + chainId: 84532, + latest: true, + }, + { + limit: 1_000, + }, + ); + + expect(mockClient.readContract).toHaveBeenCalledWith({ + address: escrowAddress, + abi: expect.any(Array), + functionName: "getAccountData", + args: [buyerAddress, sellerAddress, assetAddress, [], []], + }); + + expect(result).toEqual({ + balance: "5000000", + assetAllowance: "2000000", + assetPermitNonce: "10", + }); + }); + + it("should return account details successfully with outstanding vouchers", async () => { + const mockVouchers = [ + { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c", + }, + { + id: "0x8b4f0c21f9c7af0c5f96d32c8e6f4e79bc2c8e5735c6ef49c2gg9e581bc8g5g2", + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "500000", + asset: assetAddress, + timestamp: 1715769700, + nonce: 1, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769700 + 1000 * 60 * 60 * 24 * 30, + signature: + "0x79ce97f6d1242aa7b6f4826efb553ed453fd6c7132c665d95bc226d5f3027dd5456d61ed1bd8da5de6cea4d8154070ff458300b6b84e0c9010f434af77ad3d291c", + }, + ]; + + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue(mockVouchers); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt(3500000), // balance (adjusted for outstanding vouchers) + BigInt(2000000), // allowance + BigInt(5), // nonce + ]); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(mockClient.readContract).toHaveBeenCalledWith({ + address: escrowAddress, + abi: expect.any(Array), + functionName: "getAccountData", + args: [ + buyerAddress, + sellerAddress, + assetAddress, + [voucherId, "0x8b4f0c21f9c7af0c5f96d32c8e6f4e79bc2c8e5735c6ef49c2gg9e581bc8g5g2"], + [BigInt(1000000), BigInt(500000)], + ], + }); + + expect(result).toEqual({ + balance: "3500000", + assetAllowance: "2000000", + assetPermitNonce: "5", + }); + }); + + it("should return error when contract call fails", async () => { + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue([]); + vi.mocked(mockClient.readContract).mockRejectedValue(new Error("Contract call failed")); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(result).toEqual({ + error: "invalid_deferred_evm_contract_call_failed_account_details", + }); + }); + + it("should handle voucher store errors gracefully", async () => { + vi.mocked(mockVoucherStore.getVouchers).mockRejectedValue(new Error("Store error")); + + await expect( + getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ), + ).rejects.toThrow("Store error"); + }); + + it("should handle large number of outstanding vouchers", async () => { + const manyVouchers = Array.from({ length: 100 }, (_, i) => ({ + id: `0x${i.toString(16).padStart(64, "0")}`, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "10000", + asset: assetAddress, + timestamp: 1715769600 + i, + nonce: i, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c", + })); + + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue(manyVouchers); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt(4000000), // balance + BigInt(1000000), // allowance + BigInt(3), // nonce + ]); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(result).toEqual({ + balance: "4000000", + assetAllowance: "1000000", + assetPermitNonce: "3", + }); + + const contractCallArgs = vi.mocked(mockClient.readContract).mock.calls[0][0]; + expect(contractCallArgs.args?.[3]).toHaveLength(100); // All voucher IDs included + expect(contractCallArgs.args?.[4]).toHaveLength(100); // All voucher values included + }); + + it("should handle zero balance", async () => { + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue([]); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt(0), // zero balance + BigInt(0), // zero allowance + BigInt(0), // zero nonce + ]); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(result).toEqual({ + balance: "0", + assetAllowance: "0", + assetPermitNonce: "0", + }); + }); + + it("should handle very large balances", async () => { + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue([]); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt("1000000000000000000"), // 1 ETH in wei + BigInt("500000000000000000"), // 0.5 ETH in wei + BigInt(999), // large nonce + ]); + + const result = await getAccountData( + mockClient, + buyerAddress as Address, + sellerAddress as Address, + assetAddress as Address, + escrowAddress as Address, + 84532, + mockVoucherStore, + ); + + expect(result).toEqual({ + balance: "1000000000000000000", + assetAllowance: "500000000000000000", + assetPermitNonce: "999", + }); + }); + + it("should call voucher store with correct filters", async () => { + const differentBuyer = "0x9876543210987654321098765432109876543210"; + const differentSeller = "0x8765432109876543210987654321098765432109"; + const differentAsset = "0x7654321098765432109876543210987654321098"; + const differentEscrow = "0x6543210987654321098765432109876543210987"; + + vi.mocked(mockVoucherStore.getVouchers).mockResolvedValue([]); + vi.mocked(mockClient.readContract).mockResolvedValue([ + BigInt(1000000), + BigInt(500000), + BigInt(1), + ]); + + await getAccountData( + mockClient, + differentBuyer as Address, + differentSeller as Address, + differentAsset as Address, + differentEscrow as Address, + 1, + mockVoucherStore, + ); + + expect(mockVoucherStore.getVouchers).toHaveBeenCalledWith( + { + buyer: differentBuyer, + seller: differentSeller, + asset: differentAsset, + escrow: differentEscrow, + chainId: 1, + latest: true, + }, + { + limit: 1_000, + }, + ); + }); +}); + +describe("facilitator - flushWithAuthorization", () => { + const mockFlushAuthorization = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }; + + let mockWallet: SignerWallet; + + beforeEach(() => { + vi.clearAllMocks(); + + // Mock successful verification by default + vi.mocked(verifyModule.verifyFlushAuthorization).mockResolvedValue({ isValid: true }); + + // Create a proper mock wallet with all required properties + mockWallet = { + account: { + address: buyerAddress, + }, + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn().mockResolvedValue("0x1234567890abcdef"), + waitForTransactionReceipt: vi.fn().mockResolvedValue({ + status: "success", + logs: [], + }), + } as unknown as SignerWallet; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should flush with authorization successfully", async () => { + const result = await flushWithAuthorization( + mockWallet, + mockFlushAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have called writeContract once (flushWithAuthorization) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + + // Verify flushWithAuthorization call + const flushCall = vi.mocked(mockWallet.writeContract).mock.calls[0][0]; + expect(flushCall).toMatchObject({ + address: escrowAddress, + functionName: "flushWithAuthorization", + }); + + // Verify the args structure + expect(flushCall.args?.[0]).toMatchObject({ + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: mockFlushAuthorization.nonce, + }); + expect((flushCall.args?.[0] as { expiry: bigint }).expiry).toBe( + BigInt(mockFlushAuthorization.expiry), + ); + expect(flushCall.args?.[1]).toBe(mockFlushAuthorization.signature); + }); + + it("should return error if flush authorization verification fails", async () => { + vi.mocked(verifyModule.verifyFlushAuthorization).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_signature", + }); + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_flush_authorization_signature", + transaction: "", + payer: buyerAddress, + }); + + expect(mockWallet.writeContract).not.toHaveBeenCalled(); + }); + + it("should return error when flushWithAuthorization transaction reverts", async () => { + vi.mocked(mockWallet.writeContract).mockRejectedValueOnce(new Error("Flush failed")); + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + }); + + it("should return error when flushWithAuthorization transaction has failed status", async () => { + vi.mocked(mockWallet.waitForTransactionReceipt).mockResolvedValueOnce({ + status: "reverted", + logs: [], + } as unknown as TransactionReceipt); + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_state", + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + }); + + it("should call flushAllWithAuthorization when seller is undefined", async () => { + const mockFlushAllAuthorization = { + buyer: buyerAddress, + seller: undefined, + asset: undefined, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }; + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAllAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have called writeContract once (flushAllWithAuthorization) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + + // Verify flushAllWithAuthorization call + const flushCall = vi.mocked(mockWallet.writeContract).mock.calls[0][0]; + expect(flushCall).toMatchObject({ + address: escrowAddress, + functionName: "flushAllWithAuthorization", + }); + + // Verify the args structure (no seller/asset in args) + expect(flushCall.args?.[0]).toMatchObject({ + buyer: buyerAddress, + nonce: mockFlushAllAuthorization.nonce, + }); + expect((flushCall.args?.[0] as { expiry: bigint }).expiry).toBe( + BigInt(mockFlushAllAuthorization.expiry), + ); + expect(flushCall.args?.[1]).toBe(mockFlushAllAuthorization.signature); + // Ensure seller and asset are NOT in the args + expect(flushCall.args?.[0]).not.toHaveProperty("seller"); + expect(flushCall.args?.[0]).not.toHaveProperty("asset"); + }); + + it("should call flushAllWithAuthorization when asset is undefined", async () => { + const mockFlushAllAuthorization = { + buyer: buyerAddress, + seller: sellerAddress, + asset: undefined, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }; + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAllAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }); + + // Should have called writeContract once (flushAllWithAuthorization) + expect(mockWallet.writeContract).toHaveBeenCalledTimes(1); + + // Verify flushAllWithAuthorization call + const flushCall = vi.mocked(mockWallet.writeContract).mock.calls[0][0]; + expect(flushCall).toMatchObject({ + address: escrowAddress, + functionName: "flushAllWithAuthorization", + }); + + // Verify the args structure (no seller/asset in args) + expect(flushCall.args?.[0]).toMatchObject({ + buyer: buyerAddress, + nonce: mockFlushAllAuthorization.nonce, + }); + expect((flushCall.args?.[0] as { expiry: bigint }).expiry).toBe( + BigInt(mockFlushAllAuthorization.expiry), + ); + expect(flushCall.args?.[1]).toBe(mockFlushAllAuthorization.signature); + }); + + it("should handle flushAllWithAuthorization verification failure", async () => { + const mockFlushAllAuthorization = { + buyer: buyerAddress, + seller: undefined, + asset: undefined, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }; + + vi.mocked(verifyModule.verifyFlushAuthorization).mockResolvedValue({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_signature", + }); + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAllAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_deferred_evm_payload_flush_authorization_signature", + transaction: "", + payer: buyerAddress, + }); + + expect(mockWallet.writeContract).not.toHaveBeenCalled(); + }); + + it("should handle flushAllWithAuthorization transaction revert", async () => { + const mockFlushAllAuthorization = { + buyer: buyerAddress, + seller: undefined, + asset: undefined, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }; + + vi.mocked(mockWallet.writeContract).mockRejectedValueOnce(new Error("Flush all failed")); + + const result = await flushWithAuthorization( + mockWallet, + mockFlushAllAuthorization, + escrowAddress as `0x${string}`, + ); + + expect(result).toEqual({ + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: buyerAddress, + }); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/facilitator.ts b/typescript/packages/x402/src/schemes/deferred/evm/facilitator.ts new file mode 100644 index 0000000000..37c83d3ff3 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/facilitator.ts @@ -0,0 +1,702 @@ +import { + Account, + parseSignature, + Address, + Chain, + Transport, + Hex, + parseEventLogs, + getAddress, +} from "viem"; +import { ConnectedClient, SignerWallet } from "../../../types/shared/evm"; +import { + PaymentPayload, + PaymentRequirements, + SchemeContext, + SettleResponse, + VerifyResponse, +} from "../../../types/verify"; +import { ErrorReasons } from "../../../types/verify/x402Specs"; +import { + DeferredBuyerDataResponse, + DeferredDepositWithAuthorizationResponse, + DeferredErrorResponse, + DeferredEscrowDepositAuthorization, + DeferredEscrowFlushAuthorizationSigned, + DeferredEvmPayloadVoucher, + DeferredFlushWithAuthorizationResponse, + DeferredPaymentPayloadSchema, + DeferredPaymentRequirementsSchema, + DeferredSchemeContextSchema, +} from "../../../types/verify/schemes/deferred"; +import { deferredEscrowABI } from "../../../types/shared/evm/deferredEscrowABI"; +import { usdcABI } from "../../../types/shared/evm/erc20PermitABI"; +import { + verifyPaymentRequirements, + verifyVoucherSignatureWrapper, + verifyVoucherOnchainState, + verifyVoucherContinuity, + verifyVoucherAvailability, + verifyDepositAuthorizationSignatureAndContinuity, + verifyFlushAuthorization, + verifyDepositAuthorizationOnchainState, + getOnchainVerificationData, +} from "./verify"; +import { VoucherStore } from "./store"; + +/** + * Verifies a payment payload against the required payment details + * + * This function performs several verification steps: + * - ✅ Validates the payment payload satisfies the payment requirements + * - ✅ Validates the voucher structure is valid and continuity is maintained + * - ✅ Validates the voucher signature is valid + * - ✅ Validates the previous voucher is available for aggregation vouchers + * - ✅ Validates the onchain state allows the payment to be settled + * + * @param client - The public client used for blockchain interactions + * @param paymentPayload - The signed payment payload containing transfer parameters and signature + * @param paymentRequirements - The payment requirements that the payload must satisfy + * @param schemeContext - Scheme specific context for verification + * @returns A ValidPaymentRequest indicating if the payment is valid and any invalidation reason + */ +export async function verify< + transport extends Transport, + chain extends Chain, + account extends Account | undefined, +>( + client: ConnectedClient, + paymentPayload: PaymentPayload, + paymentRequirements: PaymentRequirements, + schemeContext: SchemeContext, +): Promise { + paymentPayload = DeferredPaymentPayloadSchema.parse(paymentPayload); + paymentRequirements = DeferredPaymentRequirementsSchema.parse(paymentRequirements); + const { voucherStore } = DeferredSchemeContextSchema.parse(schemeContext.deferred); + + // Verify the payment payload matches the payment requirements + const requirementsResult = verifyPaymentRequirements(paymentPayload, paymentRequirements); + if (!requirementsResult.isValid) { + return requirementsResult; + } + + // Verify voucher structure and continuity + const continuityResult = verifyVoucherContinuity(paymentPayload, paymentRequirements); + if (!continuityResult.isValid) { + return continuityResult; + } + + // Verify voucher signature is valid + const signatureResult = await verifyVoucherSignatureWrapper( + paymentPayload.payload.voucher, + paymentPayload.payload.signature, + ); + if (!signatureResult.isValid) { + return signatureResult; + } + + // Verify previous voucher availability + if (paymentRequirements.extra.type === "aggregation") { + const previousVoucherResult = await verifyVoucherAvailability( + paymentRequirements.extra.voucher, + paymentRequirements.extra.signature, + paymentRequirements.extra.voucher.id, + paymentRequirements.extra.voucher.nonce, + voucherStore, + ); + if (!previousVoucherResult.isValid) { + return previousVoucherResult; + } + } + + // Verify deposit authorization signature and continuity + if (paymentPayload.payload.depositAuthorization) { + const depositAuthorizationResult = await verifyDepositAuthorizationSignatureAndContinuity( + paymentPayload.payload.voucher, + paymentPayload.payload.depositAuthorization, + ); + if (!depositAuthorizationResult.isValid) { + return depositAuthorizationResult; + } + } + + // Fetch all on-chain data in a single contract call + const onchainDataResult = await getOnchainVerificationData( + client, + paymentPayload.payload.voucher, + paymentPayload.payload.depositAuthorization?.depositAuthorization.nonce, + ); + + if (!onchainDataResult.isValid) { + return { + isValid: false, + invalidReason: + onchainDataResult.invalidReason ?? + "invalid_deferred_evm_contract_call_failed_verification_data", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + const onchainData = onchainDataResult.data!; + + // Verify the onchain state allows the deposit authorization to be executed + if (paymentPayload.payload.depositAuthorization) { + const depositAuthorizationOnchainResult = verifyDepositAuthorizationOnchainState( + paymentPayload.payload.voucher, + paymentPayload.payload.depositAuthorization, + onchainData, + ); + if (!depositAuthorizationOnchainResult.isValid) { + return depositAuthorizationOnchainResult; + } + } + + // Verify the onchain state allows the payment to be settled + const onchainResult = verifyVoucherOnchainState( + paymentPayload.payload.voucher, + paymentPayload.payload.depositAuthorization, + onchainData, + ); + if (!onchainResult.isValid) { + return onchainResult; + } + + return { + isValid: true, + invalidReason: undefined, + payer: paymentPayload.payload.voucher.buyer, + }; +} + +/** + * Settles a payment by executing a collect transaction on the deferred escrow contract + * + * This function executes the collect transaction using a signed voucher. + * The facilitator wallet submits the transaction but does not need to hold or transfer any tokens itself. + * + * @param wallet - The facilitator wallet that will submit the transaction + * @param paymentPayload - The signed payment payload containing the transfer parameters and signature + * @param paymentRequirements - The original payment details that were used to create the payload + * @param schemeContext - Scheme specific context for verification + * @returns A PaymentExecutionResponse containing the transaction status and hash + */ +export async function settle( + wallet: SignerWallet, + paymentPayload: PaymentPayload, + paymentRequirements: PaymentRequirements, + schemeContext: SchemeContext, +): Promise { + // Verify payload is a deferred payment payload - plus type assert to DeferredPaymentPayload + paymentPayload = DeferredPaymentPayloadSchema.parse(paymentPayload); + const { voucherStore } = DeferredSchemeContextSchema.parse(schemeContext.deferred); + + // re-verify to ensure the payment is still valid + const valid = await verify(wallet, paymentPayload, paymentRequirements, schemeContext); + if (!valid.isValid) { + return { + success: false, + network: paymentPayload.network, + transaction: "", + errorReason: valid.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + if (paymentPayload.payload.depositAuthorization) { + const depositAuthorizationResponse = await depositWithAuthorization( + wallet, + paymentPayload.payload.voucher, + paymentPayload.payload.depositAuthorization, + false, // Skip reverification - already verified in verify() call above + ); + if (!depositAuthorizationResponse.success) { + return { + success: false, + network: paymentPayload.network, + transaction: "", + errorReason: + (depositAuthorizationResponse.errorReason as (typeof ErrorReasons)[number]) ?? + "invalid_deferred_evm_payload_deposit_authorization_failed", + payer: paymentPayload.payload.voucher.buyer, + }; + } + } + + const { voucher, signature } = paymentPayload.payload; + const response = await settleVoucher( + wallet, + voucher, + signature, + voucherStore, + false, // Skip reverification - already verified in verify() call above + ); + + return { + ...response, + network: paymentPayload.network, + }; +} + +/** + * Executes the voucher settlement transaction. The facilitator can invoke this function directly to settle a + * voucher in a deferred manner, outside of the x402 handshake. + * + * @param wallet - The facilitator wallet that will submit the transaction + * @param voucher - The voucher to settle + * @param signature - The signature of the voucher + * @param voucherStore - The voucher store to use for verification + * @param reverify - Rerun the verification steps for the voucher + * @returns A PaymentExecutionResponse containing the transaction status and hash + */ +export async function settleVoucher( + wallet: SignerWallet, + voucher: DeferredEvmPayloadVoucher, + signature: string, + voucherStore: VoucherStore, + reverify: boolean = true, +): Promise { + if (reverify) { + // Verify the voucher signature + const signatureResult = await verifyVoucherSignatureWrapper(voucher, signature); + if (!signatureResult.isValid) { + return { + success: false, + errorReason: + signatureResult.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: voucher.buyer, + }; + } + + // Verify the voucher exists in the store + const storeResult = await verifyVoucherAvailability( + voucher, + signature, + voucher.id, + voucher.nonce, + voucherStore, + ); + if (!storeResult.isValid) { + return { + success: false, + errorReason: storeResult.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: voucher.buyer, + }; + } + + // Verify the onchain state allows the payment to be settled + const onchainDataResult = await getOnchainVerificationData(wallet, voucher); + + // Check if contract call failed + if (!onchainDataResult.isValid) { + return { + success: false, + errorReason: + onchainDataResult.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: voucher.buyer, + }; + } + + const onchainData = onchainDataResult.data!; + const voucherOnchainResult = verifyVoucherOnchainState(voucher, undefined, onchainData, true); + if (!voucherOnchainResult.isValid) { + return { + success: false, + errorReason: + voucherOnchainResult.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: voucher.buyer, + }; + } + } + + let tx = ""; + try { + tx = await wallet.writeContract({ + address: voucher.escrow as Address, + abi: deferredEscrowABI, + functionName: "collect" as const, + args: [ + { + id: voucher.id as Hex, + buyer: voucher.buyer as Address, + seller: voucher.seller as Address, + valueAggregate: BigInt(voucher.valueAggregate), + asset: voucher.asset as Address, + timestamp: BigInt(voucher.timestamp), + nonce: BigInt(voucher.nonce), + escrow: voucher.escrow as Address, + chainId: BigInt(voucher.chainId), + expiry: BigInt(voucher.expiry), + }, + signature as Hex, + ], + chain: wallet.chain as Chain, + }); + } catch (error) { + console.error(error); + return { + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: voucher.buyer, + }; + } + + const receipt = await wallet.waitForTransactionReceipt({ hash: tx as `0x${string}` }); + if (receipt.status !== "success") { + return { + success: false, + errorReason: "invalid_transaction_state", + transaction: tx, + payer: voucher.buyer, + }; + } + + const logs = parseEventLogs({ + abi: deferredEscrowABI, + eventName: "VoucherCollected", + logs: receipt.logs, + }); + const collectedAmount = logs.length > 0 ? BigInt(logs[0].args.amount) : BigInt(0); + + try { + const actionResult = await voucherStore.settleVoucher(voucher, tx, collectedAmount); + if (!actionResult.success) { + return { + success: false, + errorReason: "invalid_deferred_evm_payload_voucher_could_not_settle_store", + transaction: tx, + payer: voucher.buyer, + }; + } + } catch { + return { + success: false, + errorReason: "invalid_deferred_evm_payload_voucher_error_settling_store", + transaction: tx, + payer: voucher.buyer, + }; + } + + return { + success: true, + transaction: tx, + payer: voucher.buyer, + }; +} + +/** + * Deposits funds to the escrow using a deposit authorization + * + * @param wallet - The facilitator wallet that will submit the transaction + * @param voucher - The voucher that the deposit authorization is escrowing for + * @param depositAuthorization - The deposit authorization + * @param reverify - Rerun the verification steps for the deposit authorization + * @returns A PaymentExecutionResponse containing the transaction status and hash + */ +export async function depositWithAuthorization( + wallet: SignerWallet, + voucher: DeferredEvmPayloadVoucher, + depositAuthorization: DeferredEscrowDepositAuthorization, + reverify: boolean = true, +): Promise { + if (reverify) { + // Verify the deposit authorization + const valid = await verifyDepositAuthorizationSignatureAndContinuity( + voucher, + depositAuthorization, + ); + if (!valid.isValid) { + return { + success: false, + errorReason: valid.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: depositAuthorization.depositAuthorization.buyer, + }; + } + + // Verify the onchain state allows the deposit authorization to be executed + const onchainDataResult = await getOnchainVerificationData( + wallet, + voucher, + depositAuthorization.depositAuthorization.nonce, + ); + + // Check if contract call failed + if (!onchainDataResult.isValid) { + return { + success: false, + errorReason: + onchainDataResult.invalidReason ?? "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: depositAuthorization.depositAuthorization.buyer, + }; + } + + const onchainData = onchainDataResult.data!; + const depositAuthorizationOnchainResult = verifyDepositAuthorizationOnchainState( + voucher, + depositAuthorization, + onchainData, + ); + if (!depositAuthorizationOnchainResult.isValid) { + return { + success: false, + errorReason: + depositAuthorizationOnchainResult.invalidReason ?? + "invalid_deferred_evm_payload_no_longer_valid", + transaction: "", + payer: depositAuthorization.depositAuthorization.buyer, + }; + } + } + + const { permit, depositAuthorization: depositAuthorizationInnerWithSignature } = + depositAuthorization; + + // Send permit() transaction + if (permit) { + const { v, r, s, yParity } = parseSignature(permit.signature as `0x${string}`); + let permitTx = ""; + try { + permitTx = await wallet.writeContract({ + address: voucher.asset as Address, + abi: usdcABI, + functionName: "permit" as const, + args: [ + getAddress(permit.owner), + getAddress(permit.spender), + BigInt(permit.value), + BigInt(permit.deadline), + Number(v ?? (yParity === 0 ? 27n : 28n)), + r, + s, + ], + chain: wallet.chain as Chain, + }); + } catch (error) { + console.error(error); + return { + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: permit.owner, + }; + } + + const permitReceipt = await wallet.waitForTransactionReceipt({ + hash: permitTx as `0x${string}`, + }); + if (permitReceipt.status !== "success") { + return { + success: false, + errorReason: "invalid_transaction_state", + transaction: permitTx, + payer: permit.owner, + }; + } + } + + // Send depositWithAuthorization() transaction + const { signature: depositAuthorizationSignature, ...depositAuthorizationInner } = + depositAuthorizationInnerWithSignature; + let tx = ""; + try { + tx = await wallet.writeContract({ + address: voucher.escrow as Address, + abi: deferredEscrowABI, + functionName: "depositWithAuthorization" as const, + args: [ + { + buyer: getAddress(depositAuthorizationInner.buyer), + seller: getAddress(depositAuthorizationInner.seller), + asset: getAddress(depositAuthorizationInner.asset), + amount: BigInt(depositAuthorizationInner.amount), + nonce: depositAuthorizationInner.nonce as `0x${string}`, + expiry: BigInt(depositAuthorizationInner.expiry), + }, + depositAuthorizationSignature as `0x${string}`, + ], + chain: wallet.chain as Chain, + }); + } catch (error) { + console.error(error); + return { + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: voucher.buyer, + }; + } + + const receipt = await wallet.waitForTransactionReceipt({ hash: tx as `0x${string}` }); + if (receipt.status !== "success") { + return { + success: false, + errorReason: "invalid_transaction_state", + transaction: tx, + payer: depositAuthorizationInner.buyer, + }; + } + + return { + success: true, + transaction: tx, + payer: depositAuthorization.depositAuthorization.buyer, + }; +} + +/** + * Gets the details of an escrow account defined by a buyer, seller, and asset on a target escrow contract + * Note that it will consider offchain outstanding vouchers when calculating the balance + * + * @param client - The client to use for retrieving the onchain balance + * @param buyer - The buyer address + * @param seller - The seller address + * @param asset - The asset address + * @param escrow - The escrow address + * @param chainId - The chain ID + * @param voucherStore - The voucher store to use to get outstanding vouchers + * @returns The balance of the buyer for the given asset + */ +export async function getAccountData< + transport extends Transport, + chain extends Chain, + account extends Account | undefined, +>( + client: ConnectedClient, + buyer: Address, + seller: Address, + asset: Address, + escrow: Address, + chainId: number, + voucherStore: VoucherStore, +): Promise { + const outstandingVouchers = await voucherStore.getVouchers( + { + buyer, + seller, + asset, + escrow, + chainId, + latest: true, + }, + { + limit: 1_000, // TODO: pagination? + }, + ); + + let balance: bigint; + let allowance: bigint; + let nonce: bigint; + try { + [balance, allowance, nonce] = await client.readContract({ + address: escrow as Address, + abi: deferredEscrowABI, + functionName: "getAccountData", + args: [ + buyer as Address, + seller as Address, + asset as Address, + outstandingVouchers.map(voucher => voucher.id as `0x${string}`), + outstandingVouchers.map(voucher => BigInt(voucher.valueAggregate)), + ], + }); + } catch (error) { + console.log(error); + return { + error: "invalid_deferred_evm_contract_call_failed_account_details", + }; + } + + return { + balance: balance.toString(), + assetAllowance: allowance.toString(), + assetPermitNonce: nonce.toString(), + }; +} + +/** + * Flushes an escrow account using a signed flush authorization + * + * This function performs a flush operation which: + * 1. Withdraws any funds that have completed their thawing period (ready to withdraw) + * 2. Initiates thawing for any remaining balance that isn't already thawing + * + * @param wallet - The facilitator wallet that will submit the transaction + * @param flushAuthorization - The signed flush authorization from the buyer + * @param escrow - The address of the escrow contract + * @returns A response containing the transaction status and hash + */ +export async function flushWithAuthorization( + wallet: SignerWallet, + flushAuthorization: DeferredEscrowFlushAuthorizationSigned, + escrow: Address, +): Promise { + // Verify the flush authorization + const valid = await verifyFlushAuthorization(flushAuthorization, escrow, wallet.chain.id); + if (!valid.isValid) { + return { + success: false, + errorReason: valid.invalidReason ?? "invalid_deferred_evm_payload_flush_authorization_failed", + transaction: "", + payer: flushAuthorization.buyer, + }; + } + + const { seller, asset } = flushAuthorization; + const flushAll = seller == undefined || asset == undefined; + + let tx = ""; + try { + tx = await wallet.writeContract({ + address: escrow as Address, + abi: deferredEscrowABI, + functionName: flushAll + ? ("flushAllWithAuthorization" as const) + : ("flushWithAuthorization" as const), + args: [ + { + buyer: getAddress(flushAuthorization.buyer), + ...(flushAll + ? {} + : { + seller: getAddress(seller), + asset: getAddress(asset), + }), + nonce: flushAuthorization.nonce as `0x${string}`, + expiry: BigInt(flushAuthorization.expiry), + }, + flushAuthorization.signature as `0x${string}`, + ], + chain: wallet.chain as Chain, + }); + } catch (error) { + console.error(error); + return { + success: false, + errorReason: "invalid_transaction_reverted", + transaction: "", + payer: flushAuthorization.buyer, + }; + } + + const receipt = await wallet.waitForTransactionReceipt({ hash: tx as `0x${string}` }); + if (receipt.status !== "success") { + return { + success: false, + errorReason: "invalid_transaction_state", + transaction: tx, + payer: flushAuthorization.buyer, + }; + } + + return { + success: true, + transaction: tx, + payer: flushAuthorization.buyer, + }; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/id.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/id.test.ts new file mode 100644 index 0000000000..8500e71182 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/id.test.ts @@ -0,0 +1,34 @@ +import { describe, expect, it } from "vitest"; +import { generateVoucherId } from "./id"; + +describe("generateVoucherId", () => { + it("should generate a valid voucher ID with 0x prefix", () => { + const voucherId = generateVoucherId(); + + expect(voucherId).toMatch(/^0x[a-f0-9]{64}$/i); + expect(voucherId).toHaveLength(66); // 0x + 64 hex characters + }); + + it("should generate unique IDs on multiple calls", () => { + const ids = new Set(); + const count = 1000; + + for (let i = 0; i < count; i++) { + ids.add(generateVoucherId()); + } + + expect(ids.size).toBe(count); + }); + + it("should generate IDs with proper hex format", () => { + const voucherId = generateVoucherId(); + + // Should start with 0x + expect(voucherId.startsWith("0x")).toBe(true); + + // Should contain only valid hex characters after 0x + const hexPart = voucherId.slice(2); + expect(hexPart).toMatch(/^[a-f0-9]+$/i); + expect(hexPart).toHaveLength(64); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/id.ts b/typescript/packages/x402/src/schemes/deferred/evm/id.ts new file mode 100644 index 0000000000..c13ff8a6d6 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/id.ts @@ -0,0 +1,12 @@ +import crypto from "crypto"; + +/** + * Generate a 64-character hexadecimal voucher ID + * + * @returns A new voucher id (0x-prefixed) + */ +export function generateVoucherId(): string { + // 32 bytes = 64 hex characters + const bytes = crypto.randomBytes(32); + return `0x${bytes.toString("hex")}`; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/index.ts b/typescript/packages/x402/src/schemes/deferred/evm/index.ts new file mode 100644 index 0000000000..fa845ee266 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/index.ts @@ -0,0 +1,9 @@ +export * from "./client"; +export * from "./facilitator"; +export * from "./id"; +export * from "./server"; +export * from "./sign"; +export * from "./store"; +export * from "./store.mock"; +export * from "./utils/paymentUtils"; +export * from "./verify"; diff --git a/typescript/packages/x402/src/schemes/deferred/evm/integration.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/integration.test.ts new file mode 100644 index 0000000000..b151a7a120 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/integration.test.ts @@ -0,0 +1,1048 @@ +import { describe, expect, it, vi, beforeAll, afterAll } from "vitest"; +import { createSigner, SignerWallet } from "../../../types/shared/evm"; +import { + DeferredPaymentPayload, + DeferredPaymentRequirements, + DEFERRRED_SCHEME, + DeferredEscrowDepositAuthorization, +} from "../../../types/verify/schemes/deferred"; +import { createPayment, createPaymentExtraPayload } from "./client"; +import { decodePayment } from "./utils/paymentUtils"; +import { settle, verify } from "./facilitator"; +import { InMemoryVoucherStore } from "./store.mock"; +import { getPaymentRequirementsExtra } from "./server"; +import { Chain, Log, TransactionReceipt, Transport } from "viem"; +import { signPermit, signDepositAuthorizationInner } from "./sign"; +import { createPaymentHeader } from "../../../client"; + +vi.mock("../../../verify/useDeferred", () => ({ + useDeferredFacilitator: vi.fn().mockReturnValue({ + getBuyerData: vi.fn().mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }), + }), +})); + +describe("Deferred Payment Integration Tests", () => { + const sellerAddress = "0x1234567890123456789012345678901234567890"; + const escrowAddress = "0xffffff12345678901234567890123456789fffff"; + const assetAddress = "0x1111111111111111111111111111111111111111"; + + const baseVoucher = { + id: "0x9dce748efdc0ac6ce5875ae50b7cb8aff28d14e4f335b4f6393c2ed3866bc361", + buyer: "0x05159b6100E8c7A3BbaE174A94c32E1E2e37059b", + seller: "0x1234567890123456789012345678901234567890", + valueAggregate: "1017", + asset: "0x1111111111111111111111111111111111111111", + timestamp: 1756313313, + nonce: 0, + escrow: "0xffFfFf12345678901234567890123456789fffFF", + chainId: 84532, + expiry: 1758905313, + signature: + "0x3921aa078a4d02b2c7f65614a87636a7e62f9d3990842204cf778c92a7721dba54831c2269df2ef84231d79680f844a81b7aba33a8482dfa7e97a71cf613c66f1b", + }; + const basePaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1017", + resource: "https://example.com/resource", + description: "payment", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + }; + + beforeAll(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date(baseVoucher.timestamp * 1000 + 1000)); + }); + + afterAll(() => { + vi.useRealTimers(); + }); + + describe("End-to-end x402 payment flow: /verify, /settle", () => { + it("should handle complete payment lifecycle with client initiating with buyer address and no previous history", async () => { + // Initialize facilitator -- we use in memory voucher store but blockchain interactions are mocked + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // * Step 1: Payment requirements generation + // Buyer client requests access to a resource specifying their address via X-BUYER header + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + // Seller middleware decodes X-BUYER header and generates payment requirements + const buyerAddress = buyer.account.address; + const paymentRequirements = { + ...basePaymentRequirements, + extra: await getPaymentRequirementsExtra( + undefined, // no X-PAYMENT header -- client initiates with X-BUYER header + buyerAddress, + sellerAddress, + escrowAddress, + assetAddress, + 84532, + { url: "https://facilitator.x402.io" }, + (buyer, seller) => voucherStore.getAvailableVoucher(buyer, seller), + ), + } as DeferredPaymentRequirements; + + expect(paymentRequirements.extra.type).toBe("new"); + + // * Step 2: Payment payload generation + // Buyer creates a new signed voucher, encodes it as X-PAYMENT header and sends it to the seller + const paymentPayload = await createPaymentHeader(buyer, 1, paymentRequirements); + + // * Step 3: Seller decodes and verifies the payment payload, then stores the voucher in the store - mock blockchain interactions + const decodedPaymentPayload = decodePayment(paymentPayload) as DeferredPaymentPayload; + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { + deferred: { voucherStore }, + }, + ); + expect(verifyResponse.isValid).toBe(true); + + const signedVoucher = { + ...decodedPaymentPayload.payload.voucher, + signature: decodedPaymentPayload.payload.signature, + }; + voucherStore.storeVoucher(signedVoucher); + const storedVoucher = await voucherStore.getVoucher( + decodedPaymentPayload.payload.voucher.id, + decodedPaymentPayload.payload.voucher.nonce, + ); + expect(voucherStore.vouchers.length).toBe(1); + expect(signedVoucher).toEqual(storedVoucher); + + // * Step 4: Seller does work + // -- Nothing to do here -- + + // * Step 5: Seller settles the voucher -- mock blockchain interactions + mockBlockchainInteractionsSettle(facilitatorWallet); + const settleResponse = await settle( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { deferred: { voucherStore } }, + ); + expect(settleResponse.success).toBe(true); + + const voucherCollections = await voucherStore.getVoucherCollections( + { + id: decodedPaymentPayload.payload.voucher.id, + nonce: decodedPaymentPayload.payload.voucher.nonce, + }, + {}, + ); + expect(voucherCollections.length).toBe(1); + expect(voucherCollections[0]).toEqual({ + voucherId: decodedPaymentPayload.payload.voucher.id, + voucherNonce: decodedPaymentPayload.payload.voucher.nonce, + chainId: decodedPaymentPayload.payload.voucher.chainId, + transactionHash: settleResponse.transaction, + collectedAmount: decodedPaymentPayload.payload.voucher.valueAggregate, + asset: decodedPaymentPayload.payload.voucher.asset, + collectedAt: expect.any(Number), + }); + }); + + it("should handle complete payment lifecycle with client initiating with buyer address and previous history", async () => { + // Initialize facilitator -- we use in memory voucher store but blockchain interactions are mocked + const voucherStore = new InMemoryVoucherStore(); + voucherStore.storeVoucher(baseVoucher); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // * Step 1: Payment requirements generation + // Buyer client requests access to a resource specifying their address via X-BUYER header + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + // Seller middleware decodes X-BUYER header and generates payment requirements + const buyerAddress = buyer.account.address; + const paymentRequirements = { + ...basePaymentRequirements, + extra: await getPaymentRequirementsExtra( + undefined, // no X-PAYMENT header -- client initiates with X-BUYER header + buyerAddress, + sellerAddress, + escrowAddress, + assetAddress, + 84532, + { url: "https://facilitator.x402.io" }, + (buyer, seller) => voucherStore.getAvailableVoucher(buyer, seller), + ), + } as DeferredPaymentRequirements; + + expect(paymentRequirements.extra.type).toBe("aggregation"); + + // * Step 2: Payment payload generation + // Buyer creates a new signed voucher, encodes it as X-PAYMENT header and sends it to the seller + const paymentPayload = await createPaymentHeader(buyer, 1, paymentRequirements); + + // * Step 3: Seller decodes and verifies the payment payload, then stores the voucher in the store - mock blockchain interactions + const decodedPaymentPayload = decodePayment(paymentPayload) as DeferredPaymentPayload; + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { + deferred: { voucherStore }, + }, + ); + expect(verifyResponse.isValid).toBe(true); + + voucherStore.storeVoucher({ + ...decodedPaymentPayload.payload.voucher, + signature: decodedPaymentPayload.payload.signature, + }); + expect(voucherStore.vouchers.length).toBe(2); + expect( + await voucherStore.getVoucher( + decodedPaymentPayload.payload.voucher.id, + decodedPaymentPayload.payload.voucher.nonce, + ), + ).toEqual({ + ...decodedPaymentPayload.payload.voucher, + signature: decodedPaymentPayload.payload.signature, + }); + + // * Step 4: Seller does work + // -- Nothing to do here -- + + // * Step 5: Seller settles the voucher -- mock blockchain interactions + mockBlockchainInteractionsSettle(facilitatorWallet); + const settleResponse = await settle( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { deferred: { voucherStore } }, + ); + expect(settleResponse.success).toBe(true); + + const voucherCollections = await voucherStore.getVoucherCollections( + { + id: decodedPaymentPayload.payload.voucher.id, + nonce: decodedPaymentPayload.payload.voucher.nonce, + }, + {}, + ); + expect(voucherCollections.length).toBe(1); + expect(voucherCollections[0]).toEqual({ + voucherId: decodedPaymentPayload.payload.voucher.id, + voucherNonce: decodedPaymentPayload.payload.voucher.nonce, + chainId: decodedPaymentPayload.payload.voucher.chainId, + transactionHash: settleResponse.transaction, + collectedAmount: baseVoucher.valueAggregate, // mocked transaction data uses the base voucher valueAggregate + asset: decodedPaymentPayload.payload.voucher.asset, + collectedAt: expect.any(Number), + }); + }); + + it("should handle complete payment lifecycle with client initiating with payment payload and previous history", async () => { + // Initialize facilitator -- we use in memory voucher store but blockchain interactions are mocked + const voucherStore = new InMemoryVoucherStore(); + voucherStore.storeVoucher(baseVoucher); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // * Step 0: Payment payload generation + // Buyer has the previous payment requirements so they can generate the new ones directly, skipping the X-BUYER header + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const buyerAddress = buyer.account.address; + const originalPaymentRequirements = { + ...basePaymentRequirements, + extra: await getPaymentRequirementsExtra( + undefined, // no X-PAYMENT header -- client initiates with X-BUYER header + buyerAddress, + sellerAddress, + escrowAddress, + assetAddress, + 84532, + { url: "https://facilitator.x402.io" }, + (buyer, seller) => voucherStore.getAvailableVoucher(buyer, seller), + ), + } as DeferredPaymentRequirements; + const originalPaymentPayload = await createPaymentHeader( + buyer, + 1, + originalPaymentRequirements, + ); + + // * Step 1: Payment requirements generation + // Seller middleware decodes X-PAYMENT header and generates payment requirements + const paymentRequirements = { + ...basePaymentRequirements, + extra: await getPaymentRequirementsExtra( + originalPaymentPayload, // X-PAYMENT present! -- client initiates with X-PAYMENT header + undefined, // no X-BUYER header + sellerAddress, + escrowAddress, + assetAddress, + 84532, + { url: "https://facilitator.x402.io" }, + (buyer, seller) => voucherStore.getAvailableVoucher(buyer, seller), + ), + } as DeferredPaymentRequirements; + + expect(paymentRequirements.extra.type).toBe("aggregation"); + + // * Step 2: Payment payload generation + // Buyer creates a new signed voucher, encodes it as X-PAYMENT header and sends it to the seller + const paymentPayload = await createPaymentHeader(buyer, 1, paymentRequirements); + + // * Step 3: Seller decodes and verifies the payment payload, then stores the voucher in the store - mock blockchain interactions + const decodedPaymentPayload = decodePayment(paymentPayload) as DeferredPaymentPayload; + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { + deferred: { voucherStore }, + }, + ); + expect(verifyResponse.isValid).toBe(true); + + voucherStore.storeVoucher({ + ...decodedPaymentPayload.payload.voucher, + signature: decodedPaymentPayload.payload.signature, + }); + expect(voucherStore.vouchers.length).toBe(2); + expect( + await voucherStore.getVoucher( + decodedPaymentPayload.payload.voucher.id, + decodedPaymentPayload.payload.voucher.nonce, + ), + ).toEqual({ + ...decodedPaymentPayload.payload.voucher, + signature: decodedPaymentPayload.payload.signature, + }); + + // * Step 4: Seller does work + // -- Nothing to do here -- + + // * Step 5: Seller settles the voucher -- mock blockchain interactions + mockBlockchainInteractionsSettle(facilitatorWallet); + const settleResponse = await settle( + facilitatorWallet, + decodedPaymentPayload, + paymentRequirements, + { deferred: { voucherStore } }, + ); + expect(settleResponse.success).toBe(true); + + const voucherCollections = await voucherStore.getVoucherCollections( + { + id: decodedPaymentPayload.payload.voucher.id, + nonce: decodedPaymentPayload.payload.voucher.nonce, + }, + {}, + ); + expect(voucherCollections.length).toBe(1); + expect(voucherCollections[0]).toEqual({ + voucherId: decodedPaymentPayload.payload.voucher.id, + voucherNonce: decodedPaymentPayload.payload.voucher.nonce, + chainId: decodedPaymentPayload.payload.voucher.chainId, + transactionHash: settleResponse.transaction, + collectedAmount: baseVoucher.valueAggregate, // mocked transaction data uses the base voucher valueAggregate + asset: decodedPaymentPayload.payload.voucher.asset, + collectedAt: expect.any(Number), + }); + }); + + it("should handle payment lifecycle with depositAuthorization", async () => { + // Initialize facilitator + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // Create buyer + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const buyerAddress = buyer.account.address; + + // Create deposit authorization (permit + depositAuth) + const now = Math.floor(Date.now() / 1000); + const oneWeek = 604800; + const depositAmount = "5000"; + + const permit = { + owner: buyerAddress, + spender: escrowAddress, + value: depositAmount, + nonce: "0", + deadline: now + oneWeek * 2, + domain: { + name: "USD Coin", + version: "2", + }, + }; + + const permitSignature = await signPermit(buyer, permit, 84532, assetAddress); + + const depositAuthInner = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: depositAmount, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000001", + expiry: now + oneWeek * 2, + }; + + const depositAuthSignature = await signDepositAuthorizationInner( + buyer, + depositAuthInner, + 84532, + escrowAddress, + ); + + const depositAuthorization: DeferredEscrowDepositAuthorization = { + permit: { + ...permit, + signature: permitSignature.signature, + }, + depositAuthorization: { + ...depositAuthInner, + signature: depositAuthSignature.signature, + }, + }; + + // * Step 1: Payment requirements generation + const paymentRequirements = { + ...basePaymentRequirements, + extra: await getPaymentRequirementsExtra( + undefined, + buyerAddress, + sellerAddress, + escrowAddress, + assetAddress, + 84532, + { url: "https://facilitator.x402.io" }, + (buyer, seller) => voucherStore.getAvailableVoucher(buyer, seller), + ), + } as DeferredPaymentRequirements; + + expect(paymentRequirements.extra.type).toBe("new"); + + // * Step 2: Create payment with deposit authorization + const paymentPayload = (await createPayment( + buyer, + 1, + paymentRequirements, + )) as DeferredPaymentPayload; + + // Manually add depositAuthorization to the payload + const payloadWithAuth: DeferredPaymentPayload = { + ...paymentPayload, + payload: { + ...paymentPayload.payload, + depositAuthorization, + }, + }; + + // * Step 3: Verify payment with deposit authorization + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify(facilitatorWallet, payloadWithAuth, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(verifyResponse.isValid).toBe(true); + + // * Step 4: Store voucher + voucherStore.storeVoucher({ + ...payloadWithAuth.payload.voucher, + signature: payloadWithAuth.payload.signature, + }); + + // * Step 5: Settle with deposit authorization + mockBlockchainInteractionsSettle(facilitatorWallet); + const settleResponse = await settle(facilitatorWallet, payloadWithAuth, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(settleResponse.success).toBe(true); + + const voucherCollections = await voucherStore.getVoucherCollections( + { + id: payloadWithAuth.payload.voucher.id, + nonce: payloadWithAuth.payload.voucher.nonce, + }, + {}, + ); + expect(voucherCollections.length).toBe(1); + expect(voucherCollections[0]).toEqual({ + voucherId: payloadWithAuth.payload.voucher.id, + voucherNonce: payloadWithAuth.payload.voucher.nonce, + chainId: payloadWithAuth.payload.voucher.chainId, + transactionHash: settleResponse.transaction, + collectedAmount: payloadWithAuth.payload.voucher.valueAggregate, + asset: payloadWithAuth.payload.voucher.asset, + collectedAt: expect.any(Number), + }); + }); + + it("should handle complete payment lifecycle with createPaymentExtraPayload generating depositAuthorization", async () => { + // Initialize facilitator + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // Create buyer + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const buyerAddress = buyer.account.address; + + // * Step 1: Payment requirements generation with account details indicating low balance + const paymentRequirements = { + ...basePaymentRequirements, + extra: { + type: "new", + voucher: { + id: "0x9dce748efdc0ac6ce5875ae50b7cb8aff28d14e4f335b4f6393c2ed3866bc361", + escrow: escrowAddress, + }, + account: { + balance: "500", // Low balance - below threshold + assetAllowance: "0", // No allowance + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + }, + } as DeferredPaymentRequirements; + + // Mock facilitator getBuyerData call + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetBuyerData = vi.fn().mockResolvedValue({ + balance: "500", // Confirm low balance + assetAllowance: "0", + assetPermitNonce: "0", + }); + (useDeferredFacilitator as ReturnType).mockReturnValue({ + getBuyerData: mockGetBuyerData, + }); + + // * Step 2: Client automatically generates deposit authorization using createPaymentExtraPayload + const depositConfig = { + asset: assetAddress, + assetDomain: { + name: "USD Coin", + version: "2", + }, + threshold: "10000", + amount: "1000000", + }; + + const extraPayload = await createPaymentExtraPayload(buyer, paymentRequirements, [ + depositConfig, + ]); + + expect(extraPayload).toBeDefined(); + expect(extraPayload?.permit).toBeDefined(); + expect(extraPayload?.depositAuthorization).toBeDefined(); + + // Verify facilitator was called to check balance + expect(mockGetBuyerData).toHaveBeenCalledWith( + buyerAddress, + sellerAddress, + assetAddress, + escrowAddress, + 84532, + ); + + // * Step 3: Create payment with the deposit authorization + const paymentPayload = (await createPayment( + buyer, + 1, + paymentRequirements, + extraPayload, + )) as DeferredPaymentPayload; + + expect(paymentPayload.payload.depositAuthorization).toBeDefined(); + expect(paymentPayload.payload.depositAuthorization?.permit).toBeDefined(); + expect(paymentPayload.payload.depositAuthorization?.depositAuthorization).toBeDefined(); + + // * Step 4: Verify payment with deposit authorization (mock blockchain) + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(verifyResponse.isValid).toBe(true); + + // * Step 5: Store voucher + voucherStore.storeVoucher({ + ...paymentPayload.payload.voucher, + signature: paymentPayload.payload.signature, + }); + + // * Step 6: Settle with deposit authorization (mock blockchain) + mockBlockchainInteractionsSettleWithDepositAuth(facilitatorWallet); + const settleResponse = await settle(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(settleResponse.success).toBe(true); + + // Verify writeContract was called for permit, depositWithAuthorization, and collect + expect(facilitatorWallet.writeContract).toHaveBeenCalledTimes(3); + + // First call should be permit + const permitCall = vi.mocked(facilitatorWallet.writeContract).mock.calls[0][0]; + expect(permitCall).toMatchObject({ + functionName: "permit", + address: assetAddress, + }); + + // Second call should be depositWithAuthorization + const depositCall = vi.mocked(facilitatorWallet.writeContract).mock.calls[1][0]; + expect(depositCall.functionName).toBe("depositWithAuthorization"); + expect(depositCall.address.toLowerCase()).toBe(escrowAddress.toLowerCase()); + + // Third call should be collect + const collectCall = vi.mocked(facilitatorWallet.writeContract).mock.calls[2][0]; + expect(collectCall.functionName).toBe("collect"); + expect(collectCall.address.toLowerCase()).toBe(escrowAddress.toLowerCase()); + + // * Step 7: Verify voucher collection + const voucherCollections = await voucherStore.getVoucherCollections( + { + id: paymentPayload.payload.voucher.id, + nonce: paymentPayload.payload.voucher.nonce, + }, + {}, + ); + expect(voucherCollections.length).toBe(1); + expect(voucherCollections[0]).toEqual({ + voucherId: paymentPayload.payload.voucher.id, + voucherNonce: paymentPayload.payload.voucher.nonce, + chainId: paymentPayload.payload.voucher.chainId, + transactionHash: settleResponse.transaction, + collectedAmount: paymentPayload.payload.voucher.valueAggregate, + asset: paymentPayload.payload.voucher.asset, + collectedAt: expect.any(Number), + }); + }); + + it("should handle payment lifecycle without depositAuthorization when balance is sufficient", async () => { + // Initialize facilitator + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // Create buyer + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + // * Step 1: Payment requirements generation with account details indicating sufficient balance + const paymentRequirements = { + ...basePaymentRequirements, + extra: { + type: "new", + voucher: { + id: "0x9dce748efdc0ac6ce5875ae50b7cb8aff28d14e4f335b4f6393c2ed3866bc361", + escrow: escrowAddress, + }, + account: { + balance: "10000000", // High balance - above threshold + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + }, + } as DeferredPaymentRequirements; + + // * Step 2: Client checks balance and decides no deposit needed + const depositConfig = { + asset: assetAddress, + assetDomain: { + name: "USD Coin", + version: "2", + }, + threshold: "10000", + amount: "1000000", + }; + + const extraPayload = await createPaymentExtraPayload(buyer, paymentRequirements, [ + depositConfig, + ]); + + // Should return undefined when balance is sufficient + expect(extraPayload).toBeUndefined(); + + // * Step 3: Create payment without deposit authorization + const paymentPayload = (await createPayment( + buyer, + 1, + paymentRequirements, + )) as DeferredPaymentPayload; + + expect(paymentPayload.payload.depositAuthorization).toBeUndefined(); + + // * Step 4: Verify and settle normally (mock blockchain) + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(verifyResponse.isValid).toBe(true); + + voucherStore.storeVoucher({ + ...paymentPayload.payload.voucher, + signature: paymentPayload.payload.signature, + }); + + mockBlockchainInteractionsSettle(facilitatorWallet); + const settleResponse = await settle(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(settleResponse.success).toBe(true); + + // Should only call writeContract once (for voucher collection, not for deposit) + expect(facilitatorWallet.writeContract).toHaveBeenCalledTimes(1); + }); + + it("should handle payment lifecycle with depositAuthorization but no permit when allowance is sufficient", async () => { + // Initialize facilitator + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + // Create buyer + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + // * Step 1: Payment requirements with low balance but sufficient allowance + const paymentRequirements = { + ...basePaymentRequirements, + extra: { + type: "new", + voucher: { + id: "0x9dce748efdc0ac6ce5875ae50b7cb8aff28d14e4f335b4f6393c2ed3866bc361", + escrow: escrowAddress, + }, + account: { + balance: "500", // Low balance + assetAllowance: "2000000", // Sufficient allowance - no permit needed + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + }, + } as DeferredPaymentRequirements; + + // Mock facilitator call + const { useDeferredFacilitator } = await import("../../../verify/useDeferred"); + const mockGetBuyerData = vi.fn().mockResolvedValue({ + balance: "500", + assetAllowance: "2000000", + assetPermitNonce: "0", + }); + (useDeferredFacilitator as ReturnType).mockReturnValue({ + getBuyerData: mockGetBuyerData, + }); + + // * Step 2: Generate deposit authorization without permit + const depositConfig = { + asset: assetAddress, + assetDomain: { + name: "USD Coin", + version: "2", + }, + threshold: "10000", + amount: "1000000", + }; + + const extraPayload = await createPaymentExtraPayload(buyer, paymentRequirements, [ + depositConfig, + ]); + + expect(extraPayload).toBeDefined(); + expect(extraPayload?.permit).toBeUndefined(); // No permit needed + expect(extraPayload?.depositAuthorization).toBeDefined(); + + // * Step 3: Create and verify payment + const paymentPayload = (await createPayment( + buyer, + 1, + paymentRequirements, + extraPayload, + )) as DeferredPaymentPayload; + + mockBlockchainInteractionsVerify(facilitatorWallet); + const verifyResponse = await verify(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(verifyResponse.isValid).toBe(true); + + voucherStore.storeVoucher({ + ...paymentPayload.payload.voucher, + signature: paymentPayload.payload.signature, + }); + + // * Step 4: Settle without permit transaction + mockBlockchainInteractionsSettleWithDepositAuthNoPermit(facilitatorWallet); + const settleResponse = await settle(facilitatorWallet, paymentPayload, paymentRequirements, { + deferred: { voucherStore }, + }); + expect(settleResponse.success).toBe(true); + + // Should call writeContract twice (depositWithAuthorization + collect, no permit) + expect(facilitatorWallet.writeContract).toHaveBeenCalledTimes(2); + const depositCall = vi.mocked(facilitatorWallet.writeContract).mock.calls[0][0]; + expect(depositCall.functionName).toBe("depositWithAuthorization"); + expect(depositCall.address.toLowerCase()).toBe(escrowAddress.toLowerCase()); + const collectCall = vi.mocked(facilitatorWallet.writeContract).mock.calls[1][0]; + expect(collectCall.functionName).toBe("collect"); + expect(collectCall.address.toLowerCase()).toBe(escrowAddress.toLowerCase()); + }); + }); + + describe("Multi-round voucher aggregation", () => { + it("should support multiple rounds of aggregation", async () => { + const voucherStore = new InMemoryVoucherStore(); + const facilitatorWallet = { + chain: { id: 84532 }, + readContract: vi.fn(), + writeContract: vi.fn(), + waitForTransactionReceipt: vi.fn(), + } as unknown as SignerWallet; + + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const paymentRequirements = { + ...basePaymentRequirements, + maxAmountRequired: "100000", + extra: { + type: "new", + voucher: baseVoucher, + }, + } as DeferredPaymentRequirements; + const firstPaymentPayload = (await createPayment( + buyer, + 1, + paymentRequirements, + )) as DeferredPaymentPayload; + voucherStore.storeVoucher({ + ...firstPaymentPayload.payload.voucher, + signature: firstPaymentPayload.payload.signature, + }); + + mockBlockchainInteractionsVerify(facilitatorWallet); + const valid = await verify(facilitatorWallet, firstPaymentPayload, paymentRequirements, { + deferred: { + voucherStore, + }, + }); + expect(valid.isValid).toBe(true); + + // Aggregate multiple times + let currentPayment = firstPaymentPayload; + for (let i = 1; i <= 10; i++) { + const requirements = { + ...paymentRequirements, + maxAmountRequired: "50000", + description: `payment round ${i}`, + extra: { + type: "aggregation", + signature: currentPayment.payload.signature, + voucher: currentPayment.payload.voucher, + }, + } as DeferredPaymentRequirements; + + currentPayment = (await createPayment(buyer, 1, requirements)) as DeferredPaymentPayload; + expect(currentPayment.payload.voucher.nonce).toBe(i); + expect(currentPayment.payload.voucher.valueAggregate).toBe((100000 + i * 50000).toString()); + } + + // Final voucher should have correct accumulated value + expect(currentPayment.payload.voucher.valueAggregate).toBe("600000"); // 100k + 10*50k + expect(currentPayment.payload.voucher.nonce).toBe(10); + }); + }); +}); + +/** + * Mock blockchain interactions for settle facilitator calls + * + * @param wallet - The wallet to mock blockchain interactions for + */ +function mockBlockchainInteractionsSettle(wallet: SignerWallet) { + vi.mocked(wallet.readContract).mockImplementation(async (args: { functionName: string }) => { + if (args.functionName === "getVerificationData") { + return [ + BigInt(1_000_000), // voucherOutstanding + BigInt(1_000_000), // voucherCollectable + BigInt(10_000_000), // balance + BigInt(10_000_000), // availableBalance + BigInt(10_000_000), // allowance + BigInt(0), // nonce + false, // isDepositNonceUsed + ]; + } + throw new Error(`Unmocked contract read: ${args.functionName}`); + }); + vi.mocked(wallet.writeContract).mockResolvedValue("0x1234567890abcdef"); + vi.mocked(wallet.waitForTransactionReceipt).mockResolvedValue({ + status: "success", + logs: [ + { + data: "0x000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003f9", + topics: [ + "0x9cc196634f792f4e61cf0cd71e2fbbd459e54c5e57a9bad3e6f7b6e79503cc70", + "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + "0x00000000000000000000000080cdf1957ebb7a2df22dd8913753a4423ff4272e", + "0x000000000000000000000000c93d37ad45c907ee1b27a02b2e1bd823ba9d379c", + ], + } as unknown as Log, + ], + } as TransactionReceipt); +} + +/** + * Mock blockchain interactions for verify facilitator calls + * + * @param wallet - The wallet to mock blockchain interactions for + */ +function mockBlockchainInteractionsVerify(wallet: SignerWallet) { + vi.mocked(wallet.readContract).mockImplementation(async (args: { functionName: string }) => { + if (args.functionName === "getVerificationData") { + return [ + BigInt(1_000_000), // voucherOutstanding + BigInt(1_000_000), // voucherCollectable + BigInt(10_000_000), // balance + BigInt(10_000_000), // availableBalance + BigInt(10_000_000), // allowance + BigInt(0), // nonce + false, // isDepositNonceUsed + ]; + } + throw new Error(`Unmocked contract read: ${args.functionName}`); + }); +} + +/** + * Mock blockchain interactions for settle with deposit authorization (with permit) + * + * @param wallet - The wallet to mock blockchain interactions for + */ +function mockBlockchainInteractionsSettleWithDepositAuth(wallet: SignerWallet) { + vi.mocked(wallet.readContract).mockImplementation(async (args: { functionName: string }) => { + if (args.functionName === "getVerificationData") { + return [ + BigInt(1_000_000), // voucherOutstanding + BigInt(1_000_000), // voucherCollectable + BigInt(10_000_000), // balance + BigInt(10_000_000), // availableBalance + BigInt(10_000_000), // allowance + BigInt(0), // nonce + false, // isDepositNonceUsed + ]; + } + throw new Error(`Unmocked contract read: ${args.functionName}`); + }); + vi.mocked(wallet.writeContract).mockResolvedValue("0x1234567890abcdef"); + vi.mocked(wallet.waitForTransactionReceipt).mockResolvedValue({ + status: "success", + logs: [ + { + data: "0x000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003f9", + topics: [ + "0x9cc196634f792f4e61cf0cd71e2fbbd459e54c5e57a9bad3e6f7b6e79503cc70", + "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + "0x00000000000000000000000080cdf1957ebb7a2df22dd8913753a4423ff4272e", + "0x000000000000000000000000c93d37ad45c907ee1b27a02b2e1bd823ba9d379c", + ], + } as unknown as Log, + ], + } as TransactionReceipt); +} + +/** + * Mock blockchain interactions for settle with deposit authorization (no permit) + * + * @param wallet - The wallet to mock blockchain interactions for + */ +function mockBlockchainInteractionsSettleWithDepositAuthNoPermit( + wallet: SignerWallet, +) { + vi.mocked(wallet.readContract).mockImplementation(async (args: { functionName: string }) => { + if (args.functionName === "getVerificationData") { + return [ + BigInt(1_000_000), // voucherOutstanding + BigInt(1_000_000), // voucherCollectable + BigInt(10_000_000), // balance + BigInt(10_000_000), // availableBalance + BigInt(10_000_000), // allowance + BigInt(0), // nonce + false, // isDepositNonceUsed + ]; + } + throw new Error(`Unmocked contract read: ${args.functionName}`); + }); + vi.mocked(wallet.writeContract).mockResolvedValue("0x1234567890abcdef"); + vi.mocked(wallet.waitForTransactionReceipt).mockResolvedValue({ + status: "success", + logs: [ + { + data: "0x000000000000000000000000111111111111111111111111111111111111111100000000000000000000000000000000000000000000000000000000000003f900000000000000000000000000000000000000000000000000000000000003f9", + topics: [ + "0x9cc196634f792f4e61cf0cd71e2fbbd459e54c5e57a9bad3e6f7b6e79503cc70", + "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + "0x00000000000000000000000080cdf1957ebb7a2df22dd8913753a4423ff4272e", + "0x000000000000000000000000c93d37ad45c907ee1b27a02b2e1bd823ba9d379c", + ], + } as unknown as Log, + ], + } as TransactionReceipt); +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/server.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/server.test.ts new file mode 100644 index 0000000000..86330e7770 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/server.test.ts @@ -0,0 +1,431 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { Address, getAddress } from "viem"; +import { + DeferredEvmPayloadSignedVoucher, + DeferredPaymentRequirements, + PaymentPayload, +} from "../../../types"; +import { getPaymentRequirementsExtra } from "./server"; +import * as idModule from "./id"; +import * as paymentUtilsModule from "./utils/paymentUtils"; +import * as useDeferredModule from "../../../verify/useDeferred"; + +// Mock dependencies +vi.mock("./id", () => ({ + generateVoucherId: vi.fn(), +})); + +vi.mock("./utils/paymentUtils", () => ({ + decodePayment: vi.fn(), +})); + +vi.mock("../../../verify/useDeferred", () => ({ + useDeferredFacilitator: vi.fn(), +})); + +describe("getPaymentRequirementsExtra", () => { + const mockSeller: Address = "0x1234567890123456789012345678901234567890"; + const mockEscrow: Address = "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd"; + const mockBuyer: Address = "0x9876543210987654321098765432109876543210"; + const mockAsset: Address = "0x1111111111111111111111111111111111111111"; + const mockChainId = 84532; + const mockVoucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; + const mockSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + + const mockVoucher = { + id: mockVoucherId, + buyer: mockBuyer, + seller: mockSeller, + valueAggregate: "1000000", + asset: mockAsset, + timestamp: 1715769600, + nonce: 5, + escrow: mockEscrow, + chainId: mockChainId, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: mockSignature, + }; + + const mockGetAvailableVoucher = vi.fn(); + const mockGetBuyerData = vi.fn(); + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(idModule.generateVoucherId).mockReturnValue(mockVoucherId); + vi.mocked(useDeferredModule.useDeferredFacilitator).mockReturnValue({ + getBuyerData: mockGetBuyerData, + } as unknown as ReturnType); + // Mock getBuyerData to return an error by default (so it doesn't add account info) + mockGetBuyerData.mockResolvedValue({ error: "not available" }); + }); + + describe("when no headers are provided", () => { + it("should return a new voucher extra", async () => { + const result = await getPaymentRequirementsExtra( + undefined, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "new", + voucher: { + id: mockVoucherId, + escrow: mockEscrow, + }, + }); + + expect(mockGetAvailableVoucher).not.toHaveBeenCalled(); + expect(idModule.generateVoucherId).toHaveBeenCalledOnce(); + }); + }); + + describe("when only X-BUYER header is provided", () => { + it("should return aggregation extra when previous voucher exists", async () => { + mockGetBuyerData.mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + mockGetAvailableVoucher.mockResolvedValue(mockVoucher); + + const result = await getPaymentRequirementsExtra( + undefined, + mockBuyer, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "aggregation", + account: { + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + signature: mockSignature, + voucher: mockVoucher, + }); + + expect(mockGetAvailableVoucher).toHaveBeenCalledWith(mockBuyer, mockSeller); + expect(mockGetAvailableVoucher).toHaveBeenCalledOnce(); + }); + + it("should return new voucher extra when no previous voucher exists", async () => { + mockGetBuyerData.mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + mockGetAvailableVoucher.mockResolvedValue(null); + + const result = await getPaymentRequirementsExtra( + undefined, + mockBuyer, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "new", + account: { + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + voucher: { + id: mockVoucherId, + escrow: mockEscrow, + }, + }); + + expect(mockGetAvailableVoucher).toHaveBeenCalledWith(mockBuyer, mockSeller); + }); + }); + + describe("when only X-PAYMENT header is provided", () => { + const mockPaymentHeader = "base64encodedheader"; + + it("should return aggregation extra when payment header is valid and previous voucher exists", async () => { + vi.mocked(paymentUtilsModule.decodePayment).mockReturnValue({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: mockSignature, + voucher: mockVoucher, + }, + }); + + mockGetBuyerData.mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + mockGetAvailableVoucher.mockResolvedValue(mockVoucher); + + const result = await getPaymentRequirementsExtra( + mockPaymentHeader, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "aggregation", + account: { + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + signature: mockSignature, + voucher: mockVoucher, + }); + + expect(paymentUtilsModule.decodePayment).toHaveBeenCalledWith(mockPaymentHeader); + expect(mockGetAvailableVoucher).toHaveBeenCalledWith(mockBuyer, mockSeller); + }); + + it("should return new voucher extra when payment header is invalid", async () => { + vi.mocked(paymentUtilsModule.decodePayment).mockReturnValue({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + invalidField: "invalid", + }, + } as unknown as PaymentPayload); + + const result = await getPaymentRequirementsExtra( + mockPaymentHeader, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "new", + voucher: { + id: mockVoucherId, + escrow: mockEscrow, + }, + }); + + expect(paymentUtilsModule.decodePayment).toHaveBeenCalledWith(mockPaymentHeader); + expect(mockGetAvailableVoucher).not.toHaveBeenCalled(); + }); + + it("should return new voucher extra when payment header is valid but no previous voucher exists", async () => { + vi.mocked(paymentUtilsModule.decodePayment).mockReturnValue({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: mockSignature, + voucher: mockVoucher, + }, + }); + + mockGetBuyerData.mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + mockGetAvailableVoucher.mockResolvedValue(null); + + const result = await getPaymentRequirementsExtra( + mockPaymentHeader, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "new", + account: { + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + voucher: { + id: mockVoucherId, + escrow: mockEscrow, + }, + }); + + expect(mockGetAvailableVoucher).toHaveBeenCalledWith(mockBuyer, mockSeller); + }); + }); + + describe("when both headers are provided", () => { + it("should prioritize X-PAYMENT header over X-BUYER header", async () => { + const differentBuyer: Address = "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"; + + vi.mocked(paymentUtilsModule.decodePayment).mockReturnValue({ + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: mockSignature, + voucher: { + ...mockVoucher, + buyer: differentBuyer, + }, + }, + }); + + mockGetBuyerData.mockResolvedValue({ + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + }); + + const voucherResponse: DeferredEvmPayloadSignedVoucher = { + ...mockVoucher, + buyer: differentBuyer, + }; + mockGetAvailableVoucher.mockResolvedValue(voucherResponse); + + const result = await getPaymentRequirementsExtra( + "paymentHeader", + mockBuyer, // This should be ignored + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "aggregation", + account: { + balance: "10000000", + assetAllowance: "1000000", + assetPermitNonce: "0", + facilitator: "https://facilitator.x402.io", + }, + signature: mockSignature, + voucher: { + ...mockVoucher, + buyer: differentBuyer, + }, + }); + + // Should use buyer from payment header, not from X-BUYER header + // getAddress normalizes the address to checksum format + expect(mockGetAvailableVoucher).toHaveBeenCalledWith(getAddress(differentBuyer), mockSeller); + expect(mockGetAvailableVoucher).not.toHaveBeenCalledWith(mockBuyer, mockSeller); + }); + }); + + describe("edge cases", () => { + it("should handle getAvailableVoucher throwing an error", async () => { + mockGetAvailableVoucher.mockRejectedValue(new Error("Database error")); + + // When getAvailableVoucher fails, the function catches the error and returns a new voucher + const result = await getPaymentRequirementsExtra( + undefined, + mockBuyer, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ); + + expect(result).toEqual({ + type: "new", + voucher: { + id: mockVoucherId, + escrow: mockEscrow, + }, + }); + }); + + it("should handle decodePayment throwing an error", async () => { + vi.mocked(paymentUtilsModule.decodePayment).mockImplementation(() => { + throw new Error("Invalid base64"); + }); + + await expect( + getPaymentRequirementsExtra( + "invalid-header", + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + ), + ).rejects.toThrow("Invalid base64"); + }); + + it("should generate unique voucher IDs for each new voucher request", async () => { + const voucherId1 = "0x1111111111111111111111111111111111111111111111111111111111111111"; + const voucherId2 = "0x2222222222222222222222222222222222222222222222222222222222222222"; + + vi.mocked(idModule.generateVoucherId) + .mockReturnValueOnce(voucherId1) + .mockReturnValueOnce(voucherId2); + + const result1 = (await getPaymentRequirementsExtra( + undefined, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + )) as DeferredPaymentRequirements["extra"]; + + const result2 = (await getPaymentRequirementsExtra( + undefined, + undefined, + mockSeller, + mockEscrow, + mockAsset, + mockChainId, + { url: "https://facilitator.x402.io" }, + mockGetAvailableVoucher, + )) as DeferredPaymentRequirements["extra"]; + + expect(result1.voucher.id).toBe(voucherId1); + expect(result2.voucher.id).toBe(voucherId2); + expect(result1.voucher.id).not.toBe(result2.voucher.id); + }); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/server.ts b/typescript/packages/x402/src/schemes/deferred/evm/server.ts new file mode 100644 index 0000000000..35c52200f3 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/server.ts @@ -0,0 +1,108 @@ +import { Address, getAddress } from "viem"; +import { + DeferredEvmPayloadSchema, + DeferredEvmPayloadSignedVoucher, + FacilitatorConfig, + PaymentRequirementsExtra, +} from "../../../types"; +import { generateVoucherId } from "./id"; +import { decodePayment } from "./utils/paymentUtils"; +import { useDeferredFacilitator } from "../../../verify/useDeferred"; + +/** + * Compute the extra data for the payment requirements + * + * @param xPaymentHeader - The x-payment header + * @param xBuyerHeader - The x-buyer header + * @param seller - The seller address + * @param escrow - The escrow address + * @param asset - The asset address + * @param chainId - The chain ID + * @param facilitator - The facilitator URL to get escrow balance from + * @param getAvailableVoucherLocal - A function to get the latest voucher for a given buyer and seller. If not provided, the facilitator will be used. + * @returns The extra data for the payment requirements + */ +export async function getPaymentRequirementsExtra( + xPaymentHeader: string | undefined, + xBuyerHeader: Address | undefined, + seller: Address, + escrow: Address, + asset: Address, + chainId: number, + facilitator: FacilitatorConfig, + getAvailableVoucherLocal?: ( + buyer: string, + seller: string, + ) => Promise, +): Promise { + const { getBuyerData } = useDeferredFacilitator(facilitator); + + let buyer: Address; + const newVoucherExtra: PaymentRequirementsExtra = { + type: "new" as const, + voucher: { + id: generateVoucherId(), + escrow, + }, + }; + + // No headers -> new voucher + if (!xPaymentHeader && !xBuyerHeader) { + return newVoucherExtra; + } + + // Extract buyer from X-PAYMENT or X-BUYER header + if (xPaymentHeader) { + const paymentHeader = decodePayment(xPaymentHeader); + const parsedPaymentPayload = DeferredEvmPayloadSchema.safeParse(paymentHeader.payload); + if (!parsedPaymentPayload.success) { + return newVoucherExtra; + } + buyer = getAddress(parsedPaymentPayload.data.voucher.buyer); + } else { + buyer = xBuyerHeader!; // This is safe due to the previous early return + } + + // Retrieve buyer data from facilitator and/or local voucher store + let balance = ""; + let assetAllowance = ""; + let assetPermitNonce = ""; + let success = false; + let previousVoucher: DeferredEvmPayloadSignedVoucher | null = null; + try { + const response = await getBuyerData(buyer, seller, asset, escrow, chainId); + if (!("error" in response)) { + success = true; + ({ balance, assetAllowance, assetPermitNonce } = response); + + if (getAvailableVoucherLocal == undefined) { + previousVoucher = response.voucher ?? null; + } else { + previousVoucher = await getAvailableVoucherLocal(buyer, seller); + } + } + } catch (error) { + console.error(error); + } + + const account = { + balance, + assetAllowance, + assetPermitNonce, + facilitator: facilitator.url, + }; + + if (previousVoucher) { + return { + type: "aggregation" as const, + ...(success ? { account } : {}), + signature: previousVoucher.signature, + voucher: { + ...previousVoucher, + }, + }; + } + + if (success) newVoucherExtra.account = account; + return newVoucherExtra; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/sign.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/sign.test.ts new file mode 100644 index 0000000000..cbad507308 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/sign.test.ts @@ -0,0 +1,483 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { createSigner } from "../../../types/shared/evm"; +import { + signVoucher, + verifyVoucherSignature, + signPermit, + verifyPermitSignature, + signDepositAuthorizationInner, + verifyDepositAuthorizationInnerSignature, + signFlushAuthorization, + verifyFlushAuthorizationSignature, +} from "./sign"; +import { privateKeyToAccount } from "viem/accounts"; + +const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", +); +const buyerAddress = buyer.account.address; +const anotherBuyerAddress = "0x9234567890123456789012345678901234567890"; +const sellerAddress = "0x1234567890123456789012345678901234567890"; +const escrowAddress = "0xffffff12345678901234567890123456789fffff"; +const assetAddress = "0x1111111111111111111111111111111111111111"; +const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; + +describe("voucher signature", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockVoucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-01-01T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid voucher signature", async () => { + const signature = await signVoucher(buyer, mockVoucher); + expect(signature.signature).toBe(mockVoucherSignature); + }); + + it("should verify a valid voucher signature", async () => { + const isValid = await verifyVoucherSignature(mockVoucher, mockVoucherSignature, buyerAddress); + expect(isValid).toBe(true); + }); + + it("should return false if voucher signature is valid but for a different buyer", async () => { + const isValid = await verifyVoucherSignature( + mockVoucher, + mockVoucherSignature, + anotherBuyerAddress, + ); + expect(isValid).toBe(false); + }); + + it("should sign a voucher using a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signVoucher(localAccount, mockVoucher); + + expect(signature.signature).toBe(mockVoucherSignature); + }); + + it("should verify a voucher signed by a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signVoucher(localAccount, mockVoucher); + + const isValid = await verifyVoucherSignature( + mockVoucher, + signature.signature, + localAccount.address, + ); + expect(isValid).toBe(true); + }); + + it("should throw error if wallet client does not support signTypedData", async () => { + const invalidWallet = { + account: { address: buyerAddress }, + // Missing signTypedData method + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect(signVoucher(invalidWallet, mockVoucher)).rejects.toThrow( + "Invalid wallet client provided does not support signTypedData", + ); + }); + + it("should throw error if LocalAccount does not support signTypedData", async () => { + const invalidAccount = { + address: buyerAddress, + type: "local", + // Missing signTypedData method + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect(signVoucher(invalidAccount, mockVoucher)).rejects.toThrow( + "Invalid wallet client provided does not support signTypedData", + ); + }); +}); + +describe("permit signature", () => { + const mockPermit = { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + domain: { + name: "USD Coin", + version: "2", + }, + }; + + const mockPermitSignature = + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c"; + + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2024-01-01T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid permit signature", async () => { + const signature = await signPermit(buyer, mockPermit, 84532, assetAddress as `0x${string}`); + expect(signature.signature).toBe(mockPermitSignature); + }); + + it("should verify a valid permit signature", async () => { + const isValid = await verifyPermitSignature( + mockPermit, + mockPermitSignature as `0x${string}`, + buyerAddress as `0x${string}`, + 84532, + assetAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should return false if permit signature is valid but for a different signer", async () => { + const isValid = await verifyPermitSignature( + mockPermit, + mockPermitSignature as `0x${string}`, + anotherBuyerAddress as `0x${string}`, + 84532, + assetAddress as `0x${string}`, + ); + expect(isValid).toBe(false); + }); + + it("should sign a permit using a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signPermit( + localAccount, + mockPermit, + 84532, + assetAddress as `0x${string}`, + ); + + expect(signature.signature).toBe(mockPermitSignature); + }); + + it("should verify a permit signed by a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signPermit( + localAccount, + mockPermit, + 84532, + assetAddress as `0x${string}`, + ); + + const isValid = await verifyPermitSignature( + mockPermit, + signature.signature, + localAccount.address, + 84532, + assetAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should throw error if wallet client does not support signTypedData", async () => { + const invalidWallet = { + account: { address: buyerAddress }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signPermit(invalidWallet, mockPermit, 84532, assetAddress as `0x${string}`), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); + + it("should throw error if LocalAccount does not support signTypedData", async () => { + const invalidAccount = { + address: buyerAddress, + type: "local", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signPermit(invalidAccount, mockPermit, 84532, assetAddress as `0x${string}`), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); +}); + +describe("deposit authorization signature", () => { + const mockDepositAuth = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockDepositAuthSignature = + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b"; + + beforeEach(() => { + vi.useFakeTimers(); + vi.setSystemTime(new Date("2024-01-01T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid deposit authorization signature", async () => { + const signature = await signDepositAuthorizationInner( + buyer, + mockDepositAuth, + 84532, + escrowAddress as `0x${string}`, + ); + expect(signature.signature).toBe(mockDepositAuthSignature); + }); + + it("should verify a valid deposit authorization signature", async () => { + const isValid = await verifyDepositAuthorizationInnerSignature( + mockDepositAuth, + mockDepositAuthSignature as `0x${string}`, + buyerAddress as `0x${string}`, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should return false if deposit authorization signature is valid but for a different signer", async () => { + const isValid = await verifyDepositAuthorizationInnerSignature( + mockDepositAuth, + mockDepositAuthSignature as `0x${string}`, + anotherBuyerAddress as `0x${string}`, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(false); + }); + + it("should sign a deposit authorization using a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signDepositAuthorizationInner( + localAccount, + mockDepositAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + expect(signature.signature).toBe(mockDepositAuthSignature); + }); + + it("should verify a deposit authorization signed by a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signDepositAuthorizationInner( + localAccount, + mockDepositAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + const isValid = await verifyDepositAuthorizationInnerSignature( + mockDepositAuth, + signature.signature, + localAccount.address, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should throw error if wallet client does not support signTypedData", async () => { + const invalidWallet = { + account: { address: buyerAddress }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signDepositAuthorizationInner( + invalidWallet, + mockDepositAuth, + 84532, + escrowAddress as `0x${string}`, + ), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); + + it("should throw error if LocalAccount does not support signTypedData", async () => { + const invalidAccount = { + address: buyerAddress, + type: "local", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signDepositAuthorizationInner( + invalidAccount, + mockDepositAuth, + 84532, + escrowAddress as `0x${string}`, + ), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); +}); + +describe("flush authorization signature", () => { + const mockFlushAuth = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + beforeEach(() => { + vi.useFakeTimers(); + // Set a fixed time for consistent testing + vi.setSystemTime(new Date("2024-01-01T00:00:00Z")); + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.useRealTimers(); + }); + + it("should create a valid flush authorization signature", async () => { + const signature = await signFlushAuthorization( + buyer, + mockFlushAuth, + 84532, + escrowAddress as `0x${string}`, + ); + expect(signature.signature).toBeDefined(); + expect(signature.signature).toMatch(/^0x[0-9a-fA-F]{130}$/); + }); + + it("should verify a valid flush authorization signature", async () => { + const signature = await signFlushAuthorization( + buyer, + mockFlushAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + const isValid = await verifyFlushAuthorizationSignature( + mockFlushAuth, + signature.signature, + buyerAddress as `0x${string}`, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should return false if flush authorization signature is valid but for a different signer", async () => { + const signature = await signFlushAuthorization( + buyer, + mockFlushAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + const isValid = await verifyFlushAuthorizationSignature( + mockFlushAuth, + signature.signature, + anotherBuyerAddress as `0x${string}`, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(false); + }); + + it("should sign a flush authorization using a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signFlushAuthorization( + localAccount, + mockFlushAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + expect(signature.signature).toBeDefined(); + expect(signature.signature).toMatch(/^0x[0-9a-fA-F]{130}$/); + }); + + it("should verify a flush authorization signed by a LocalAccount", async () => { + const localAccount = privateKeyToAccount( + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + const signature = await signFlushAuthorization( + localAccount, + mockFlushAuth, + 84532, + escrowAddress as `0x${string}`, + ); + + const isValid = await verifyFlushAuthorizationSignature( + mockFlushAuth, + signature.signature, + localAccount.address, + 84532, + escrowAddress as `0x${string}`, + ); + expect(isValid).toBe(true); + }); + + it("should throw error if wallet client does not support signTypedData", async () => { + const invalidWallet = { + account: { address: buyerAddress }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signFlushAuthorization(invalidWallet, mockFlushAuth, 84532, escrowAddress as `0x${string}`), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); + + it("should throw error if LocalAccount does not support signTypedData", async () => { + const invalidAccount = { + address: buyerAddress, + type: "local", + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + await expect( + signFlushAuthorization(invalidAccount, mockFlushAuth, 84532, escrowAddress as `0x${string}`), + ).rejects.toThrow("Invalid wallet client provided does not support signTypedData"); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/sign.ts b/typescript/packages/x402/src/schemes/deferred/evm/sign.ts new file mode 100644 index 0000000000..21614278f7 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/sign.ts @@ -0,0 +1,401 @@ +import { Address, Chain, getAddress, Hex, LocalAccount, Transport } from "viem"; +import { + typedDataTypes, + isAccount, + isSignerWallet, + SignerWallet, + deferredVoucherPrimaryType, + createConnectedClient, + permitPrimaryType, + depositAuthorizationPrimaryType, + flushAuthorizationPrimaryType, + flushAllAuthorizationPrimaryType, +} from "../../../types/shared/evm"; +import { + DeferredEscrowDepositAuthorizationInner, + DeferredEscrowDepositAuthorizationPermit, + DeferredEscrowFlushAuthorization, + DeferredEvmPayloadVoucher, +} from "../../../types/verify/schemes/deferred"; +import { getNetworkName } from "../../../shared"; + +/** + * Signs a voucher + * + * @param walletClient - The wallet client that will sign the authorization + * @param voucher - The voucher to sign + * @returns The signature for the authorization + */ +export async function signVoucher( + walletClient: SignerWallet | LocalAccount, + voucher: DeferredEvmPayloadVoucher, +): Promise<{ signature: Hex }> { + const { id, buyer, seller, valueAggregate, asset, timestamp, nonce, escrow, chainId, expiry } = + voucher; + const data = { + types: typedDataTypes, + primaryType: deferredVoucherPrimaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId, + verifyingContract: getAddress(escrow), + }, + message: { + id: id.toLowerCase(), + buyer: getAddress(buyer), + seller: getAddress(seller), + valueAggregate, + asset: getAddress(asset), + timestamp, + nonce, + escrow: getAddress(escrow), + chainId, + expiry, + }, + }; + + if (isSignerWallet(walletClient)) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else if (isAccount(walletClient) && walletClient.signTypedData) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else { + throw new Error("Invalid wallet client provided does not support signTypedData"); + } +} + +/** + * Verifies a voucher signature + * + * @param voucher - The voucher to verify + * @param signature - The signature to verify + * @param signer - The address of the signer to verify + * @returns The address that signed the voucher + */ +export async function verifyVoucherSignature( + voucher: DeferredEvmPayloadVoucher, + signature: Hex, + signer: Address, +) { + const voucherTypedData = { + types: typedDataTypes, + primaryType: deferredVoucherPrimaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId: voucher.chainId, + verifyingContract: getAddress(voucher.escrow), + }, + message: voucher, + }; + + const client = createConnectedClient(getNetworkName(voucher.chainId)); + return await client.verifyTypedData({ + address: signer, + ...voucherTypedData, + signature: signature as Hex, + }); +} + +/** + * Signs an EIP-2612 permit + * + * Note that the permit input object is not the actual EIP-712 signed message. It contains additional fields. + * + * @param walletClient - The wallet client that will sign the authorization + * @param permit - The permit to sign + * @param chainId - The chain ID + * @param asset - The address of the asset + * @returns The signature for the permit + */ +export async function signPermit( + walletClient: SignerWallet | LocalAccount, + permit: DeferredEscrowDepositAuthorizationPermit, + chainId: number, + asset: Address, +): Promise<{ signature: Hex }> { + const { domain, owner, spender, value, nonce, deadline } = permit; + const data = { + types: typedDataTypes, + primaryType: permitPrimaryType, + domain: { + name: domain.name, + version: domain.version, + chainId: chainId, + verifyingContract: getAddress(asset), + }, + message: { + owner: getAddress(owner), + spender: getAddress(spender), + value, + nonce, + deadline, + }, + }; + + if (isSignerWallet(walletClient)) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else if (isAccount(walletClient) && walletClient.signTypedData) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else { + throw new Error("Invalid wallet client provided does not support signTypedData"); + } +} + +/** + * Verifies a permit signature + * + * Note that the permit input object is not the actual EIP-712 signed message. It contains additional fields. + * + * @param permit - The permit to verify + * @param signature - The signature to verify + * @param signer - The address of the signer to verify + * @param chainId - The chain ID + * @param asset - The address of the asset + * @returns The address that signed the voucher + */ +export async function verifyPermitSignature( + permit: DeferredEscrowDepositAuthorizationPermit, + signature: Hex, + signer: Address, + chainId: number, + asset: Address, +) { + const { domain, ...eip712Permit } = permit; + const permitTypedData = { + types: typedDataTypes, + primaryType: permitPrimaryType, + domain: { + name: domain.name, + version: domain.version, + chainId: chainId, + verifyingContract: getAddress(asset), + }, + message: eip712Permit, + }; + + const client = createConnectedClient(getNetworkName(chainId)); + return await client.verifyTypedData({ + address: signer, + ...permitTypedData, + signature: signature as Hex, + }); +} + +/** + * Signs a deferred escrow deposit authorization + * + * @param walletClient - The wallet client that will sign the authorization + * @param depositAuthorization - The deposit authorization to sign + * @param chainId - The chain ID + * @param escrow - The address of the escrow contract + * @returns The signature for the permit + */ +export async function signDepositAuthorizationInner< + transport extends Transport, + chain extends Chain, +>( + walletClient: SignerWallet | LocalAccount, + depositAuthorization: DeferredEscrowDepositAuthorizationInner, + chainId: number, + escrow: Address, +): Promise<{ signature: Hex }> { + const { buyer, seller, asset, amount, nonce, expiry } = depositAuthorization; + const data = { + types: typedDataTypes, + primaryType: depositAuthorizationPrimaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId: chainId, + verifyingContract: getAddress(escrow), + }, + message: { + buyer: getAddress(buyer), + seller: getAddress(seller), + asset: getAddress(asset), + amount, + nonce, + expiry, + }, + }; + + if (isSignerWallet(walletClient)) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else if (isAccount(walletClient) && walletClient.signTypedData) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else { + throw new Error("Invalid wallet client provided does not support signTypedData"); + } +} + +/** + * Verifies a deposit authorization signature + * + * @param depositAuthorization - The deposit authorization to verify + * @param signature - The signature to verify + * @param signer - The address of the signer to verify + * @param chainId - The chain ID + * @param escrow - The address of the escrow contract + * @returns The address that signed the voucher + */ +export async function verifyDepositAuthorizationInnerSignature( + depositAuthorization: DeferredEscrowDepositAuthorizationInner, + signature: Hex, + signer: Address, + chainId: number, + escrow: Address, +) { + const depositAuthorizationTypedData = { + types: typedDataTypes, + primaryType: depositAuthorizationPrimaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId: chainId, + verifyingContract: getAddress(escrow), + }, + message: { + buyer: getAddress(depositAuthorization.buyer), + seller: getAddress(depositAuthorization.seller), + asset: getAddress(depositAuthorization.asset), + amount: depositAuthorization.amount, + nonce: depositAuthorization.nonce, + expiry: depositAuthorization.expiry, + }, + }; + + const client = createConnectedClient(getNetworkName(chainId)); + return await client.verifyTypedData({ + address: signer, + ...depositAuthorizationTypedData, + signature: signature as Hex, + }); +} + +/** + * Signs a flush authorization + * + * The function will sign a FlushAuthorization or FlushAllAuthorization depending on the presence of a seller and asset in the message. + * + * @param walletClient - The wallet client that will sign the authorization + * @param flushAuthorization - The flush authorization to sign + * @param chainId - The chain ID + * @param escrow - The address of the escrow contract + * @returns The signature for the authorization + */ +export async function signFlushAuthorization( + walletClient: SignerWallet | LocalAccount, + flushAuthorization: DeferredEscrowFlushAuthorization, + chainId: number, + escrow: Address, +): Promise<{ signature: Hex }> { + const { buyer, seller, asset, nonce, expiry } = flushAuthorization; + const flushAll = seller == undefined || asset == undefined; + const primaryType = flushAll ? flushAllAuthorizationPrimaryType : flushAuthorizationPrimaryType; + const data = { + types: typedDataTypes, + primaryType: primaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId: chainId, + verifyingContract: getAddress(escrow), + }, + message: { + buyer: getAddress(buyer), + ...(flushAll + ? {} + : { + seller: getAddress(seller), + asset: getAddress(asset), + }), + nonce, + expiry, + }, + }; + + if (isSignerWallet(walletClient)) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else if (isAccount(walletClient) && walletClient.signTypedData) { + const signature = await walletClient.signTypedData(data); + return { + signature, + }; + } else { + throw new Error("Invalid wallet client provided does not support signTypedData"); + } +} + +/** + * Verifies a flush authorization signature + * + * The function will verify a FlushAuthorization or FlushAllAuthorization depending on the presence of a seller and asset in the message. + * + * @param flushAuthorization - The flush authorization to verify + * @param signature - The signature to verify + * @param signer - The address of the signer to verify + * @param chainId - The chain ID + * @param escrow - The address of the escrow contract + * @returns The address that signed the authorization + */ +export async function verifyFlushAuthorizationSignature( + flushAuthorization: DeferredEscrowFlushAuthorization, + signature: Hex, + signer: Address, + chainId: number, + escrow: Address, +) { + const { seller, asset } = flushAuthorization; + const flushAll = seller == undefined || asset == undefined; + const primaryType = flushAll ? flushAllAuthorizationPrimaryType : flushAuthorizationPrimaryType; + const flushAuthorizationTypedData = { + types: typedDataTypes, + primaryType: primaryType, + domain: { + name: "DeferredPaymentEscrow", + version: "1", + chainId: chainId, + verifyingContract: getAddress(escrow), + }, + message: { + buyer: getAddress(flushAuthorization.buyer), + ...(flushAll + ? {} + : { + seller: getAddress(seller), + asset: getAddress(asset), + }), + nonce: flushAuthorization.nonce, + expiry: flushAuthorization.expiry, + }, + }; + + const client = createConnectedClient(getNetworkName(chainId)); + return await client.verifyTypedData({ + address: signer, + ...flushAuthorizationTypedData, + signature: signature as Hex, + }); +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/store.mock.ts b/typescript/packages/x402/src/schemes/deferred/evm/store.mock.ts new file mode 100644 index 0000000000..7f41bb0733 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/store.mock.ts @@ -0,0 +1,240 @@ +import { + DeferredEvmPayloadSignedVoucher, + DeferredEvmPayloadVoucher, + DeferredVoucherCollection, +} from "../../../types"; +import { VoucherStore, VoucherStoreActionResult } from "./store"; + +/** + * In-memory implementation of the VoucherStore interface for testing and development + * + * This class provides a non-persistent storage solution for X402 deferred EVM vouchers. + * It maintains vouchers and their collection records in memory arrays, making it suitable + * for unit tests, integration tests, and local development environments. + * + * WARNING: do not use in production + * This voucher store implementation does not persist data between application restarts + * and might not be feature complete. + */ +export class InMemoryVoucherStore extends VoucherStore { + public vouchers: Array = []; + public collections: Array = []; + + /** + * Get a voucher by its id and nonce + * + * @param id - The id of the voucher + * @param nonce - The nonce of the voucher + * @returns The voucher or null if not found + */ + async getVoucher(id: string, nonce?: number): Promise { + if (nonce !== undefined) { + return this.vouchers.find(voucher => voucher.id === id && voucher.nonce === nonce) ?? null; + } + + return ( + this.vouchers + .filter(voucher => voucher.id === id) + .sort((a, b) => b.nonce - a.nonce) + .at(0) ?? null + ); + } + + /** + * Get a series of vouchers by their id + * + * @param id - The id of the voucher series + * @param pagination - The pagination options + * @param pagination.limit - The maximum number of vouchers to return + * @param pagination.offset - The offset of the first voucher to return + * @returns The vouchers in the series + */ + async getVoucherSeries( + id: string, + pagination: { + limit?: number | undefined; + offset?: number | undefined; + }, + ): Promise> { + const { limit = 100, offset = 0 } = pagination; + return this.vouchers + .filter(voucher => voucher.id === id) + .sort((a, b) => b.nonce - a.nonce) + .slice(offset, offset + limit); + } + + /** + * Get all vouchers matching the query + * + * @param query - The query options + * @param query.buyer - The buyer's address + * @param query.seller - The seller's address + * @param query.asset - The asset's address + * @param query.escrow - The escrow's address + * @param query.chainId - The chain ID + * @param query.latest - Whether to return only the latest voucher per series + * @param pagination - The pagination options + * @param pagination.limit - The maximum number of vouchers to return + * @param pagination.offset - The offset of the first voucher to return + * @returns The vouchers matching the query + */ + async getVouchers( + query: { + buyer?: string | undefined; + seller?: string | undefined; + asset?: string | undefined; + escrow?: string | undefined; + chainId?: number | undefined; + latest?: boolean | undefined; + }, + pagination: { + limit?: number | undefined; + offset?: number | undefined; + }, + ): Promise> { + const { limit = 100, offset = 0 } = pagination; + const { buyer, latest, seller, asset, escrow, chainId } = query; + + // Filter vouchers by buyer and/or seller + let filteredVouchers = this.vouchers.filter(voucher => { + if (buyer && voucher.buyer !== buyer) return false; + if (seller && voucher.seller !== seller) return false; + if (asset && voucher.asset !== asset) return false; + if (escrow && voucher.escrow !== escrow) return false; + if (chainId && voucher.chainId !== chainId) return false; + return true; + }); + + // If latest=true, return only the latest voucher per series ID + if (latest) { + const voucherMap = new Map(); + + filteredVouchers.forEach(voucher => { + const existing = voucherMap.get(voucher.id); + if (!existing || voucher.nonce > existing.nonce) { + voucherMap.set(voucher.id, voucher); + } + }); + + filteredVouchers = Array.from(voucherMap.values()); + } + + // Sort by nonce descending, then by timestamp descending + return filteredVouchers + .sort((a, b) => { + if (b.nonce !== a.nonce) return b.nonce - a.nonce; + return b.timestamp - a.timestamp; + }) + .slice(offset, offset + limit); + } + + /** + * Get the "latest available" voucher for a given buyer and seller + * + * @param buyer - The buyer's address + * @param seller - The seller's address + * @returns The available voucher or null if none available + */ + async getAvailableVoucher( + buyer: string, + seller: string, + ): Promise { + // Get all vouchers for this buyer/seller pair + const vouchers = this.vouchers.filter( + voucher => voucher.buyer === buyer && voucher.seller === seller, + ); + + if (vouchers.length === 0) return null; + + // Group by series ID and find the highest nonce per series + const voucherMap = new Map(); + vouchers.forEach(voucher => { + const existing = voucherMap.get(voucher.id); + if (!existing || voucher.nonce > existing.nonce) { + voucherMap.set(voucher.id, voucher); + } + }); + + // Sort by timestamp descending to get the most recent + const latestVouchers = Array.from(voucherMap.values()).sort( + (a, b) => b.timestamp - a.timestamp, + ); + + return latestVouchers.at(0) ?? null; + } + + /** + * Store a voucher + * + * @param voucher - The voucher to store + * @returns The result of the operation + */ + async storeVoucher(voucher: DeferredEvmPayloadSignedVoucher): Promise { + if (this.vouchers.some(v => v.id === voucher.id && v.nonce === voucher.nonce)) { + return { success: false, error: "Voucher already exists" }; + } + + this.vouchers.push(voucher); + return { success: true }; + } + + /** + * Settle a voucher + * + * @param voucher - The voucher to settle + * @param txHash - The transaction hash of the settlement + * @param amount - The amount of the settlement + * @returns The result of the operation + */ + async settleVoucher( + voucher: DeferredEvmPayloadVoucher, + txHash: string, + amount: bigint, + ): Promise { + this.collections.push({ + voucherId: voucher.id, + voucherNonce: voucher.nonce, + transactionHash: txHash, + collectedAmount: amount.toString(), + asset: voucher.asset, + chainId: voucher.chainId, + collectedAt: Date.now(), + }); + return { success: true }; + } + + /** + * Get the voucher collections for a given voucher id and nonce + * + * @param query - The query options + * @param query.id - The id of the voucher + * @param query.nonce - The nonce of the voucher + * @param pagination - The pagination options + * @param pagination.limit - The maximum number of collections to return + * @param pagination.offset - The offset of the first collection to return + * @returns The voucher collections + */ + async getVoucherCollections( + query: { + id?: string | undefined; + nonce?: number | undefined; + }, + pagination: { + limit?: number | undefined; + offset?: number | undefined; + }, + ): Promise> { + const { limit = 100, offset = 0 } = pagination; + const { id, nonce } = query; + + return this.collections + .filter(collection => { + if (id && collection.voucherId !== id) return false; + if (nonce !== undefined && collection.voucherNonce !== nonce) return false; + return true; + }) + .slice(offset, offset + limit); + } +} + +export const voucherStore = new InMemoryVoucherStore(); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/store.ts b/typescript/packages/x402/src/schemes/deferred/evm/store.ts new file mode 100644 index 0000000000..295146cda8 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/store.ts @@ -0,0 +1,211 @@ +import { + DeferredEvmPayloadSignedVoucher, + DeferredEvmPayloadVoucher, + DeferredVoucherCollection, +} from "../../../types/verify/schemes/deferred"; + +export type VoucherStoreActionResult = { + success: boolean; + error?: string; +}; + +/** + * Voucher store interface for X402 deferred EVM schemes + * + * This abstract class defines the interface for storing, retrieving, and managing vouchers + * in the deferred payment system. Implementations should provide persistent storage + * with proper transaction safety. + * + * Key concepts: + * - A `voucher` is uniquely identified by the pair (id, nonce). + * - A `voucher series` is a set of vouchers that share the same id but with different nonces. + * - A `voucher collection` is a record of a voucher being settled on-chain. Multiple collections can be + * associated with a single voucher. + * + */ +export abstract class VoucherStore { + /** + * Retrieve a voucher by id and nonce + * + * @param id - The voucher series identifier (64-character hex string) + * @param nonce - Optional. The specific voucher nonce to retrieve, defaults to the latest voucher in the series. + * @returns The voucher if found, null otherwise + * + * @example + * ```typescript + * // Get specific voucher + * const voucher = await store.getVoucher("0x123...", 5); + * + * // Get latest voucher in series + * const latest = await store.getVoucher("0x123..."); + * ``` + */ + abstract getVoucher(id: string, nonce?: number): Promise; + + /** + * Retrieve all vouchers in a series by id + * + * @param id - The voucher series identifier + * @param pagination - Pagination options + * @returns Array of vouchers sorted by nonce (descending) + * + * Implementation requirements: + * - Results must be sorted by nonce in descending order (newest first) + * - Must support pagination with limit/offset + * - Should return empty array if series doesn't exist + * - Default limit should be reasonable (e.g., 100) if not specified + * + * @example + * ```typescript + * // Get first 10 vouchers in series + * const vouchers = await store.getVoucherSeries("0x123...", { limit: 10, offset: 0 }); + * ``` + */ + abstract getVoucherSeries( + id: string, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise>; + + /** + * Query vouchers with filtering and pagination + * + * @param query - Filter criteria + * @param pagination - Pagination options + * @returns Array of vouchers matching the criteria + * + * Query Options: + * - buyer: Filter by buyer's address + * - seller: Filter by seller's address + * - asset: Filter by asset's address + * - escrow: Filter by escrow's address + * - chainId: Filter by chain ID + * - latest: If true, return only the highest nonce voucher per series + * + * Behavior: + * - When latest=true: returns one voucher per series (the one with highest nonce) + * - When latest=false: returns all vouchers matching buyer/seller criteria + * - Results should be sorted by nonce descending, then by timestamp descending + * + * @example + * ```typescript + * // Get all latest vouchers for a buyer + * const latest = await store.getVouchers( + * { buyer: "0x123...", latest: true }, + * { limit: 50 } + * ); + * + * // Get all historical vouchers for a pair + * const history = await store.getVouchers( + * { buyer: "0x123...", seller: "0x456...", latest: false }, + * { limit: 100, offset: 0 } + * ); + * ``` + */ + abstract getVouchers( + query: { + buyer?: string; + seller?: string; + asset?: string; + escrow?: string; + chainId?: number; + latest?: boolean; + }, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise>; + + /** + * Get the "latest available" voucher for a given buyer and seller. + * + * @param buyer - The buyer's address + * @param seller - The seller's address + * @returns The available voucher or null if none available + * + * The voucher to return must follow the following selection criteria: + * 1. Voucher matches the provided buyer and seller addresses + * 2. Voucher has the highest nonce for its series + * 3. Among multiple matching series, select the one with the most recent timestamp + * + * @example + * ```typescript + * // Get the latest available voucher for a given buyer and seller + * const voucher = await store.getAvailableVoucher("0xbuyer...", "0xseller..."); + * if (voucher) { + * // Process payment... + * } + * ``` + */ + abstract getAvailableVoucher( + buyer: string, + seller: string, + ): Promise; + + /** + * Store a new voucher in the system + * + * @param voucher - The signed voucher to store + * @returns Result indicating success/failure + * + * @example + * ```typescript + * const result = await store.storeVoucher(signedVoucher, signature); + * if (result.success) { + * console.log("Voucher stored successfully"); + * } else { + * console.error("Failed to store voucher:", result.error); + * } + * ``` + */ + abstract storeVoucher( + voucher: DeferredEvmPayloadSignedVoucher, + ): Promise; + + /** + * Record the settlement of a voucher on-chain + * + * @param voucher - The voucher that was settled + * @param txHash - The transaction hash of the settlement + * @param amount - The actual amount collected (as determined by on-chain logic) + * @returns Result indicating success/failure + * + * @example + * ```typescript + * // Record settlement + * const result = await store.settleVoucher(voucher, "0xabc123...", 1000000n); + * ``` + */ + abstract settleVoucher( + voucher: DeferredEvmPayloadVoucher, + txHash: string, + amount: bigint, + ): Promise; + + /** + * Get the collections for a voucher + * + * @param query - The voucher id and nonce + * @param pagination - Pagination options + * @returns The collections for the voucher + * + * @example + * ```typescript + * // Get the collections for a voucher + * const collections = await store.getVoucherCollections({ id: "0x123...", nonce: 5 }, { limit: 10, offset: 0 }); + * ``` + */ + abstract getVoucherCollections( + query: { + id?: string; + nonce?: number; + }, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise>; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.test.ts new file mode 100644 index 0000000000..8765045d0a --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.test.ts @@ -0,0 +1,356 @@ +import { describe, expect, it } from "vitest"; +import { encodePayment, decodePayment } from "./paymentUtils"; +import { PaymentPayload } from "../../../../types/verify"; +import { + DeferredPaymentPayload, + DEFERRRED_SCHEME, +} from "../../../../types/verify/schemes/deferred"; + +const buyerAddress = "0xf33332f96E5EA32c90a5301b646Bf5e93EA1D892"; +const sellerAddress = "0x1234567890123456789012345678901234567890"; +const escrowAddress = "0xffffff12345678901234567890123456789fffff"; +const assetAddress = "0x1111111111111111111111111111111111111111"; +const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; +const voucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + +describe("paymentUtils", () => { + const mockPaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + + describe("encodePayment", () => { + it("should encode a deferred payment payload to base64 string", () => { + const encoded = encodePayment(mockPaymentPayload); + + expect(typeof encoded).toBe("string"); + expect(encoded.length).toBeGreaterThan(0); + + // Should be valid base64 + expect(() => { + Buffer.from(encoded, "base64"); + }).not.toThrow(); + }); + + it("should handle vouchers with zero values", () => { + const zeroValuePayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + voucher: { + ...mockPaymentPayload.payload.voucher, + valueAggregate: "0", + timestamp: 0, + nonce: 0, + chainId: 0, + expiry: 0, + }, + }, + }; + + const encoded = encodePayment(zeroValuePayload); + expect(typeof encoded).toBe("string"); + expect(encoded.length).toBeGreaterThan(0); + }); + + it("should throw error for invalid payment payload", () => { + const invalidPayload = { + x402Version: 1, + scheme: "invalid-scheme", + network: "base-sepolia", + } as unknown as PaymentPayload; + + expect(() => encodePayment(invalidPayload)).toThrow(); + }); + + it("should produce consistent encoding for identical inputs", () => { + const encoded1 = encodePayment(mockPaymentPayload); + const encoded2 = encodePayment(mockPaymentPayload); + + expect(encoded1).toBe(encoded2); + }); + }); + + describe("decodePayment", () => { + it("should decode a valid encoded payment payload", () => { + const encoded = encodePayment(mockPaymentPayload); + const decoded = decodePayment(encoded); + + expect(decoded).toEqual(mockPaymentPayload); + }); + + it("should correctly decode voucher fields as expected types", () => { + const encoded = encodePayment(mockPaymentPayload); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(typeof decoded.x402Version).toBe("number"); + expect(typeof decoded.scheme).toBe("string"); + expect(typeof decoded.network).toBe("string"); + expect(typeof decoded.payload.signature).toBe("string"); + expect(typeof decoded.payload.voucher.id).toBe("string"); + expect(typeof decoded.payload.voucher.buyer).toBe("string"); + expect(typeof decoded.payload.voucher.seller).toBe("string"); + expect(typeof decoded.payload.voucher.valueAggregate).toBe("string"); + expect(typeof decoded.payload.voucher.asset).toBe("string"); + expect(typeof decoded.payload.voucher.timestamp).toBe("number"); + expect(typeof decoded.payload.voucher.nonce).toBe("number"); + expect(typeof decoded.payload.voucher.escrow).toBe("string"); + expect(typeof decoded.payload.voucher.chainId).toBe("number"); + expect(typeof decoded.payload.voucher.expiry).toBe("number"); + }); + + it("should handle zero values correctly", () => { + const zeroValuePayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + voucher: { + ...mockPaymentPayload.payload.voucher, + valueAggregate: "0", + timestamp: 0, + nonce: 0, + chainId: 0, + expiry: 0, + }, + }, + }; + + const encoded = encodePayment(zeroValuePayload); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(decoded.payload.voucher.valueAggregate).toBe("0"); + expect(decoded.payload.voucher.timestamp).toBe(0); + expect(decoded.payload.voucher.nonce).toBe(0); + expect(decoded.payload.voucher.chainId).toBe(0); + expect(decoded.payload.voucher.expiry).toBe(0); + }); + + it("should throw error for invalid base64 string", () => { + const invalidBase64 = "invalid-base64-string!@#"; + + expect(() => decodePayment(invalidBase64)).toThrow(); + }); + + it("should throw error for invalid JSON in base64", () => { + const invalidJson = Buffer.from("invalid json content").toString("base64"); + + expect(() => decodePayment(invalidJson)).toThrow(); + }); + + it("should throw error for valid JSON but invalid payment payload structure", () => { + const invalidPayload = { + x402Version: 1, + scheme: "invalid-scheme", + network: "base-sepolia", + }; + const encoded = Buffer.from(JSON.stringify(invalidPayload)).toString("base64"); + + expect(() => decodePayment(encoded)).toThrow(); + }); + + it("should throw error for malformed voucher data", () => { + const malformedPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: { + id: voucherId, + // Missing required fields + }, + }, + }; + const encoded = Buffer.from(JSON.stringify(malformedPayload)).toString("base64"); + + expect(() => decodePayment(encoded)).toThrow(); + }); + }); + + describe("round-trip encoding/decoding", () => { + it("should maintain data integrity through multiple encode/decode cycles", () => { + let current = mockPaymentPayload; + + // Perform multiple round trips + for (let i = 0; i < 5; i++) { + const encoded = encodePayment(current); + current = decodePayment(encoded) as DeferredPaymentPayload; + } + + expect(current).toEqual(mockPaymentPayload); + }); + + it("should handle different networks", () => { + const networkTestPayload = { + ...mockPaymentPayload, + network: "iotex" as const, + }; + + const encoded = encodePayment(networkTestPayload); + const decoded = decodePayment(encoded); + + expect(decoded.network).toBe("iotex"); + expect(decoded).toEqual(networkTestPayload); + }); + + it("should handle different signature formats", () => { + const differentSigPayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + signature: "0x" + "0".repeat(130), // Different valid signature format + }, + }; + + const encoded = encodePayment(differentSigPayload); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(decoded.payload.signature).toBe("0x" + "0".repeat(130)); + expect(decoded).toEqual(differentSigPayload); + }); + + it("should handle voucher with mixed case addresses", () => { + const mixedCasePayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + voucher: { + ...mockPaymentPayload.payload.voucher, + buyer: "0xF33332f96E5EA32c90a5301b646Bf5e93EA1D892", + seller: "0x1234567890123456789012345678901234567890", + asset: "0x1111111111111111111111111111111111111111", + escrow: "0xFFFFFF12345678901234567890123456789FFFFF", + }, + }, + }; + + const encoded = encodePayment(mixedCasePayload); + const decoded = decodePayment(encoded); + + expect(decoded).toEqual(mixedCasePayload); + }); + }); + + describe("depositAuthorization handling", () => { + const mockPaymentWithDepositAuth: DeferredPaymentPayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + depositAuthorization: { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c", + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }, + }, + }; + + const mockPaymentWithDepositAuthNoPermit: DeferredPaymentPayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + depositAuthorization: { + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }, + }, + }, + }; + + it("should encode payment with depositAuthorization (with permit)", () => { + const encoded = encodePayment(mockPaymentWithDepositAuth); + + expect(typeof encoded).toBe("string"); + expect(encoded.length).toBeGreaterThan(0); + }); + + it("should decode payment with depositAuthorization (with permit)", () => { + const encoded = encodePayment(mockPaymentWithDepositAuth); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(decoded).toEqual(mockPaymentWithDepositAuth); + expect(decoded.payload.depositAuthorization).toBeDefined(); + expect(decoded.payload.depositAuthorization?.permit).toBeDefined(); + expect(decoded.payload.depositAuthorization?.depositAuthorization).toBeDefined(); + }); + + it("should encode payment with depositAuthorization (without permit)", () => { + const encoded = encodePayment(mockPaymentWithDepositAuthNoPermit); + + expect(typeof encoded).toBe("string"); + expect(encoded.length).toBeGreaterThan(0); + }); + + it("should decode payment with depositAuthorization (without permit)", () => { + const encoded = encodePayment(mockPaymentWithDepositAuthNoPermit); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(decoded).toEqual(mockPaymentWithDepositAuthNoPermit); + expect(decoded.payload.depositAuthorization).toBeDefined(); + expect(decoded.payload.depositAuthorization?.permit).toBeUndefined(); + expect(decoded.payload.depositAuthorization?.depositAuthorization).toBeDefined(); + }); + + it("should round-trip payment with depositAuthorization", () => { + let current = mockPaymentWithDepositAuth; + + for (let i = 0; i < 3; i++) { + const encoded = encodePayment(current); + current = decodePayment(encoded) as DeferredPaymentPayload; + } + + expect(current).toEqual(mockPaymentWithDepositAuth); + }); + + it("should handle payment without depositAuthorization", () => { + const encoded = encodePayment(mockPaymentPayload); + const decoded = decodePayment(encoded) as DeferredPaymentPayload; + + expect(decoded.payload.depositAuthorization).toBeUndefined(); + expect(decoded).toEqual(mockPaymentPayload); + }); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.ts b/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.ts new file mode 100644 index 0000000000..d154e19fe3 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/utils/paymentUtils.ts @@ -0,0 +1,56 @@ +import { safeBase64Encode, safeBase64Decode } from "../../../../shared"; +import { PaymentPayload } from "../../../../types/verify"; +import { DeferredPaymentPayloadSchema } from "../../../../types/verify/schemes/deferred"; + +/** + * Encodes a payment payload into a base64 string, ensuring bigint values are properly stringified + * + * @param payment - The payment payload to encode + * @returns A base64 encoded string representation of the payment payload + */ +export function encodePayment(payment: PaymentPayload): string { + const deferredPayment = DeferredPaymentPayloadSchema.parse(payment); + const safe = { + ...deferredPayment, + payload: { + signature: deferredPayment.payload.signature, + voucher: Object.fromEntries( + Object.entries(deferredPayment.payload.voucher).map(([key, value]) => [ + key, + typeof value === "bigint" ? (value as bigint).toString() : value, + ]), + ), + ...(deferredPayment.payload.depositAuthorization && { + depositAuthorization: deferredPayment.payload.depositAuthorization, + }), + }, + }; + return safeBase64Encode(JSON.stringify(safe)); +} + +/** + * Decodes a base64 encoded payment string back into a PaymentPayload object + * + * @param payment - The base64 encoded payment string to decode + * @returns The decoded and validated PaymentPayload object + */ +export function decodePayment(payment: string): PaymentPayload { + const decoded = safeBase64Decode(payment); + const parsed = JSON.parse(decoded); + + const obj = { + ...parsed, + payload: { + signature: parsed.payload.signature, + voucher: { + ...parsed.payload.voucher, + }, + ...(parsed.payload.depositAuthorization && { + depositAuthorization: parsed.payload.depositAuthorization, + }), + }, + }; + + const validated = DeferredPaymentPayloadSchema.parse(obj); + return validated; +} diff --git a/typescript/packages/x402/src/schemes/deferred/evm/verify.test.ts b/typescript/packages/x402/src/schemes/deferred/evm/verify.test.ts new file mode 100644 index 0000000000..977d55c07c --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/verify.test.ts @@ -0,0 +1,1587 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { DeferredEscrowFlushAuthorizationSigned, PaymentRequirements } from "../../../types"; +import { + DeferredPaymentPayload, + DeferredPaymentRequirements, + DeferredEvmPayloadSignedVoucher, + DEFERRRED_SCHEME, +} from "../../../types/verify/schemes/deferred"; +import { + verifyPaymentRequirements, + verifyVoucherSignatureWrapper, + verifyVoucherOnchainState, + verifyVoucherContinuity, + verifyVoucherAvailability, + verifyVoucherDuplicate, + verifyDepositAuthorizationSignatureAndContinuity, + verifyDepositAuthorizationOnchainState, + verifyFlushAuthorization, +} from "./verify"; +import { createSigner } from "../../../types/shared/evm/wallet"; +import { getNetworkId } from "../../../shared"; +import { VoucherStore } from "./store"; +import { getAddress } from "viem"; +import { signVoucher } from "./sign"; + +vi.mock("../../../shared", async (original: () => Promise>) => { + const actual = await original(); + return { + ...(actual as Record), + getNetworkId: vi.fn(), + }; +}); + +const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", +); +const buyerAddress = "0x05159b6100E8c7A3BbaE174A94c32E1E2e37059b"; +const sellerAddress = "0x1234567890123456789012345678901234567890"; +const escrowAddress = "0xffFfFf12345678901234567890123456789fffFF"; +const assetAddress = "0x1111111111111111111111111111111111111111"; +const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; +const voucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + +describe("verifyPaymentRequirements", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockPaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: mockVoucher, + }, + }; + + const mockPaymentRequirements: PaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(getNetworkId).mockReturnValue(84532); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should return isValid: true for valid payment requirements with new voucher", () => { + const result = verifyPaymentRequirements(mockPaymentPayload, mockPaymentRequirements); + expect(result).toEqual({ isValid: true }); + }); + + it("should return isValid: true for valid payment requirements with aggregation voucher", () => { + const aggregationRequirements: PaymentRequirements = { + ...mockPaymentRequirements, + maxAmountRequired: "500000", + extra: { + type: "aggregation", + signature: voucherSignature, + voucher: { + ...mockVoucher, + valueAggregate: "500000", + }, + }, + }; + const result = verifyPaymentRequirements(mockPaymentPayload, aggregationRequirements); + expect(result).toEqual({ isValid: true }); + }); + + it("should return error if payload scheme is not deferred", () => { + const invalidPayload = { + ...mockPaymentPayload, + scheme: "immediate", + } as unknown as DeferredPaymentPayload; + const result = verifyPaymentRequirements(invalidPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_scheme", + }); + }); + + it("should return error if requirements scheme is not deferred", () => { + const invalidRequirements = { + ...mockPaymentRequirements, + scheme: "immediate", + } as unknown as PaymentRequirements; + const result = verifyPaymentRequirements( + mockPaymentPayload, + invalidRequirements as DeferredPaymentRequirements, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_requirements_scheme", + }); + }); + + it("should return error if payment payload network does not match payment requirements network", () => { + const invalidPayload = { + ...mockPaymentPayload, + network: "base", + } as DeferredPaymentPayload; + const result = verifyPaymentRequirements(invalidPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_network_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if network is not supported", () => { + vi.mocked(getNetworkId).mockImplementation(() => { + throw new Error("Unsupported network"); + }); + const result = verifyPaymentRequirements(mockPaymentPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_network_unsupported", + payer: buyerAddress, + }); + vi.resetAllMocks(); + }); + + it("should return error if network is invalid", () => { + const invalidPayload = { + ...mockPaymentPayload, + network: "iotex", + payload: { + ...mockPaymentPayload.payload, + }, + } as DeferredPaymentPayload; + const result = verifyPaymentRequirements(invalidPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_network_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if voucher value is insufficient for new voucher", () => { + const invalidPayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + voucher: { + ...mockVoucher, + valueAggregate: "999999", + }, + }, + }; + const result = verifyPaymentRequirements(invalidPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_value", + payer: buyerAddress, + }); + }); + + it("should return error if voucher value is insufficient for aggregation voucher", () => { + const aggregationRequirements: PaymentRequirements = { + ...mockPaymentRequirements, + extra: { + type: "aggregation", + signature: voucherSignature, + voucher: { + ...mockVoucher, + valueAggregate: "500000", + }, + }, + }; + const result = verifyPaymentRequirements(mockPaymentPayload, aggregationRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_value", + payer: buyerAddress, + }); + }); + + it("should return error if payTo does not match voucher seller", () => { + const invalidRequirements = { + ...mockPaymentRequirements, + payTo: "0x9999999999999999999999999999999999999999", + }; + const result = verifyPaymentRequirements(mockPaymentPayload, invalidRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_recipient_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if asset mismatch", () => { + const invalidPayload = { + ...mockPaymentPayload, + payload: { + ...mockPaymentPayload.payload, + voucher: { + ...mockVoucher, + asset: "0x2222222222222222222222222222222222222222", + }, + }, + }; + const result = verifyPaymentRequirements(invalidPayload, mockPaymentRequirements); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_asset_mismatch", + payer: buyerAddress, + }); + }); +}); + +describe("verifyVoucherContinuity", () => { + const baseVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const basePaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: baseVoucher, + }, + }; + + const aggregatedVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "12000000", + asset: assetAddress, + timestamp: 1715769600 + 100, + nonce: 1, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 60, // 60 days + }; + + const aggregatedPaymentPayload: DeferredPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + signature: voucherSignature, // does not matter + voucher: aggregatedVoucher, + }, + }; + + const baseNewVoucherPaymentRequirements: PaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + }; + + const baseAggregationVoucherPaymentRequirements: PaymentRequirements = { + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test resource", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "aggregation", + signature: voucherSignature, + voucher: baseVoucher, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + // Mock current time to be within valid range + vi.useFakeTimers(); + vi.setSystemTime(new Date((baseVoucher.timestamp + 100) * 1000)); // 100 seconds after voucher timestamp + }); + + afterEach(() => { + vi.useRealTimers(); + vi.resetAllMocks(); + }); + + it("should return valid if new voucher is valid", () => { + const result = verifyVoucherContinuity(basePaymentPayload, baseNewVoucherPaymentRequirements); + expect(result).toEqual({ + isValid: true, + }); + }); + + it("should return valid if aggregation voucher is valid", () => { + const result = verifyVoucherContinuity( + aggregatedPaymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + expect(result).toEqual({ + isValid: true, + }); + }); + + it("should return error if voucher is expired", () => { + const expiredPaymentPayload = { + ...basePaymentPayload, + payload: { + ...basePaymentPayload.payload, + voucher: { + ...baseVoucher, + expiry: baseVoucher.timestamp - 1000, + }, + }, + }; + + const result = verifyVoucherContinuity( + expiredPaymentPayload, + baseNewVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expired", + payer: buyerAddress, + }); + }); + + it("should return error if voucher timestamp is in the future", () => { + const paymentPayload = { + ...basePaymentPayload, + payload: { + ...basePaymentPayload.payload, + voucher: { + ...baseVoucher, + timestamp: baseVoucher.timestamp + 3600, + }, + }, + }; + + const result = verifyVoucherContinuity(paymentPayload, baseNewVoucherPaymentRequirements); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_timestamp_too_early", + payer: buyerAddress, + }); + }); + + describe("new voucher validation", () => { + it("should return error if new voucher has non-zero nonce", () => { + const paymentPayload = { + ...basePaymentPayload, + payload: { + ...basePaymentPayload.payload, + voucher: { + ...baseVoucher, + nonce: 1, + }, + }, + }; + const result = verifyVoucherContinuity(paymentPayload, baseNewVoucherPaymentRequirements); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_non_zero_nonce", + payer: buyerAddress, + }); + }); + + it("should return error if new voucher has zero value aggregate", () => { + const paymentPayload = { + ...basePaymentPayload, + payload: { + ...basePaymentPayload.payload, + voucher: { ...baseVoucher, valueAggregate: "0" }, + }, + }; + const result = verifyVoucherContinuity(paymentPayload, baseNewVoucherPaymentRequirements); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_zero_value_aggregate", + payer: buyerAddress, + }); + }); + }); + + describe("aggregation voucher validation", () => { + it("should return error if voucher id doesn't match", () => { + const mismatchedIdVoucher = { + ...aggregatedVoucher, + id: "0x1111111111111111111111111111111111111111111111111111111111111111", + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedIdVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_id_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if buyer doesn't match", () => { + const mismatchedBuyerVoucher = { + ...aggregatedVoucher, + buyer: "0x9999999999999999999999999999999999999999", + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedBuyerVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_buyer_mismatch", + payer: "0x9999999999999999999999999999999999999999", + }); + }); + + it("should return error if seller doesn't match", () => { + const mismatchedSellerVoucher = { + ...aggregatedVoucher, + seller: "0x9999999999999999999999999999999999999999", + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedSellerVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_seller_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if value aggregate decreases", () => { + const decreasingValueVoucher = { + ...aggregatedVoucher, + valueAggregate: (BigInt(baseVoucher.valueAggregate) - BigInt(1)).toString(), + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: decreasingValueVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_value_aggregate_decreasing", + payer: buyerAddress, + }); + }); + + it("should return error if asset doesn't match", () => { + const mismatchedAssetVoucher = { + ...aggregatedVoucher, + asset: "0x9999999999999999999999999999999999999999", + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedAssetVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_asset_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if timestamp decreases", () => { + const decreasingTimestampVoucher = { + ...aggregatedVoucher, + timestamp: baseVoucher.timestamp - 100, // Earlier than previous + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: decreasingTimestampVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_timestamp_decreasing", + payer: buyerAddress, + }); + }); + + it("should return error if nonce is not incremented by 1", () => { + const wrongNonceVoucher = { + ...aggregatedVoucher, + nonce: 2, // Should be 1 + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: wrongNonceVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_nonce_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if escrow doesn't match", () => { + const mismatchedEscrowVoucher = { + ...aggregatedVoucher, + escrow: "0x9999999999999999999999999999999999999999", + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedEscrowVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_escrow_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if chainId doesn't match", () => { + const mismatchedChainIdVoucher = { + ...aggregatedVoucher, + chainId: 1, // Different chain + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: mismatchedChainIdVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_chain_id_mismatch", + payer: buyerAddress, + }); + }); + + it("should return error if expiry decreases", () => { + const decreasingExpiryVoucher = { + ...aggregatedVoucher, + expiry: baseVoucher.expiry - 1, // Earlier expiry + }; + + const paymentPayload = { + ...aggregatedPaymentPayload, + payload: { + ...aggregatedPaymentPayload.payload, + voucher: decreasingExpiryVoucher, + }, + }; + + const result = verifyVoucherContinuity( + paymentPayload, + baseAggregationVoucherPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expiry_decreasing", + payer: buyerAddress, + }); + }); + }); +}); + +describe("verifyVoucherSignature", async () => { + const mockPaymentPayload = { + x402Version: 1, + scheme: DEFERRRED_SCHEME, + network: "base-sepolia", + payload: { + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }, + }, + }; + const { signature } = await signVoucher(buyer, mockPaymentPayload.payload.voucher); + + it("should return isValid: true for valid voucher signature", async () => { + const result = await verifyVoucherSignatureWrapper( + mockPaymentPayload.payload.voucher, + signature, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return isValid: false for invalid voucher signature", async () => { + const result = await verifyVoucherSignatureWrapper( + mockPaymentPayload.payload.voucher, + "0x999b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c", + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + payer: buyerAddress, + }); + }); +}); + +describe("verifyVoucherAvailability", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }; + + const mockSignedVoucher: DeferredEvmPayloadSignedVoucher = { + ...mockVoucher, + signature: voucherSignature, + }; + + let mockVoucherStore: VoucherStore; + + beforeEach(() => { + vi.clearAllMocks(); + mockVoucherStore = { + getVoucher: vi.fn(), + } as unknown as VoucherStore; + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + it("should return valid response when previous voucher is found and matches", async () => { + vi.mocked(mockVoucherStore.getVoucher).mockResolvedValue(mockSignedVoucher); + + const result = await verifyVoucherAvailability( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, + mockVoucherStore, + ); + + expect(result).toEqual({ + isValid: true, + }); + + expect(mockVoucherStore.getVoucher).toHaveBeenCalledWith(voucherId, 0); + }); + + it("should return error when previous voucher is not found in store", async () => { + vi.mocked(mockVoucherStore.getVoucher).mockResolvedValue(null); + + const result = await verifyVoucherAvailability( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, + mockVoucherStore, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_found", + payer: buyerAddress, + }); + }); + + it("should return error when previous voucher is found but doesn't match", async () => { + const mismatchedVoucher: DeferredEvmPayloadSignedVoucher = { + ...mockSignedVoucher, + valueAggregate: "500000", // Different value + }; + + vi.mocked(mockVoucherStore.getVoucher).mockResolvedValue(mismatchedVoucher); + + const result = await verifyVoucherAvailability( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, + mockVoucherStore, + ); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_found_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should handle voucher store errors", async () => { + vi.mocked(mockVoucherStore.getVoucher).mockRejectedValue(new Error("Store error")); + + await expect( + verifyVoucherAvailability( + mockVoucher, + voucherSignature, + mockVoucher.id, + mockVoucher.nonce, + mockVoucherStore, + ), + ).rejects.toThrow("Store error"); + }); +}); + +describe("verifyVoucherDuplicate", () => { + const baseVoucher: DeferredEvmPayloadSignedVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: voucherSignature, + }; + + it("should return valid response for identical vouchers", () => { + const identicalVoucher = { ...baseVoucher }; + + const result = verifyVoucherDuplicate(baseVoucher, identicalVoucher); + + expect(result).toEqual({ + isValid: true, + }); + }); + + it("should return error if voucher IDs don't match", () => { + const differentIdVoucher = { + ...baseVoucher, + id: "0x1111111111111111111111111111111111111111111111111111111111111111", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentIdVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if buyer addresses don't match", () => { + const differentBuyerVoucher = { + ...baseVoucher, + buyer: "0x9999999999999999999999999999999999999999", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentBuyerVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if seller addresses don't match", () => { + const differentSellerVoucher = { + ...baseVoucher, + seller: "0x9999999999999999999999999999999999999999", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentSellerVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if value aggregates don't match", () => { + const differentValueVoucher = { + ...baseVoucher, + valueAggregate: "2000000", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentValueVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if assets don't match", () => { + const differentAssetVoucher = { + ...baseVoucher, + asset: "0x2222222222222222222222222222222222222222", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentAssetVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if timestamps don't match", () => { + const differentTimestampVoucher = { + ...baseVoucher, + timestamp: 1715769700, + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentTimestampVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if nonces don't match", () => { + const differentNonceVoucher = { + ...baseVoucher, + nonce: 1, + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentNonceVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if escrow addresses don't match", () => { + const differentEscrowVoucher = { + ...baseVoucher, + escrow: "0x9999999999999999999999999999999999999999", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentEscrowVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if chain IDs don't match", () => { + const differentChainIdVoucher = { + ...baseVoucher, + chainId: 1, + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentChainIdVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if expiry times don't match", () => { + const differentExpiryVoucher = { + ...baseVoucher, + expiry: baseVoucher.expiry + 3600, + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentExpiryVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should return error if signatures don't match", () => { + const differentSignatureVoucher = { + ...baseVoucher, + signature: + "0x79ce97f6d1242aa7b6f4826efb553ed453fd6c7132c665d95bc226d5f3027dd5456d61ed1bd8da5de6cea4d8154070ff458300b6b84e0c9010f434af77ad3d291c", + }; + + const result = verifyVoucherDuplicate(baseVoucher, differentSignatureVoucher); + + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: buyerAddress, + }); + }); + + it("should handle case-insensitive ID and signature comparison", () => { + const upperCaseIdVoucher = { + ...baseVoucher, + id: voucherId.toUpperCase(), + signature: voucherSignature.toUpperCase(), + }; + + const resultUpperCase = verifyVoucherDuplicate(baseVoucher, upperCaseIdVoucher); + + expect(resultUpperCase).toEqual({ + isValid: true, + }); + + const lowerCaseIdVoucher = { + ...baseVoucher, + id: voucherId.toLowerCase(), + signature: voucherSignature.toLowerCase(), + }; + + const resultLowerCase = verifyVoucherDuplicate(baseVoucher, lowerCaseIdVoucher); + + expect(resultLowerCase).toEqual({ + isValid: true, + }); + }); + + it("should handle lower case address comparison", () => { + const mixedCaseVoucher = { + ...baseVoucher, + buyer: buyerAddress.toLowerCase(), + seller: sellerAddress.toLowerCase(), + asset: assetAddress.toLowerCase(), + escrow: escrowAddress.toLowerCase(), + }; + + const result = verifyVoucherDuplicate(baseVoucher, mixedCaseVoucher); + + expect(result).toEqual({ + isValid: true, + }); + }); + + it("should handle checksummed address comparison", () => { + const mixedCaseVoucher = { + ...baseVoucher, + escrow: getAddress(escrowAddress), + asset: getAddress(assetAddress), + buyer: getAddress(buyerAddress), + seller: getAddress(sellerAddress), + }; + + const result = verifyVoucherDuplicate(baseVoucher, mixedCaseVoucher); + + expect(result).toEqual({ + isValid: true, + }); + }); +}); + +describe("verifyVoucherOnchainState", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + it("should return valid if voucher is valid", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(10_000_000), + allowance: BigInt(1_000_000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }; + const result = verifyVoucherOnchainState(mockVoucher, undefined, onchainData); + expect(result).toEqual({ isValid: true }); + }); + + it("should return valid if voucher is valid with deposit authorization", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(500_000), + availableBalance: BigInt(400_000), // Not enough without deposit auth + allowance: BigInt(1_000_000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }; + const depositAuthorization = { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c" as `0x${string}`, + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "600000", // Enough to cover the outstanding + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }, + }; + const result = verifyVoucherOnchainState(mockVoucher, depositAuthorization, onchainData); + expect(result).toEqual({ isValid: true }); + }); + + it("should return error if insufficient balance", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(100_000), + availableBalance: BigInt(100_000), + allowance: BigInt(1_000_000), + nonce: BigInt(0), + isDepositNonceUsed: false, + }; + const result = verifyVoucherOnchainState(mockVoucher, undefined, onchainData); + expect(result).toEqual({ + isValid: false, + invalidReason: "insufficient_funds", + payer: buyerAddress, + }); + }); +}); + +describe("verifyDepositAuthorizationSignatureAndContinuity", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockDepositAuthorizationWithPermit = { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c" as `0x${string}`, + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }, + }; + + const mockDepositAuthorizationWithoutPermit = { + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }, + }; + + beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + vi.setSystemTime(new Date(mockVoucher.timestamp * 1000 + 100000)); // 100 seconds after voucher timestamp + }); + + afterEach(() => { + vi.useRealTimers(); + vi.resetAllMocks(); + }); + + it("should return valid if deposit authorization is valid with permit", async () => { + const result = await verifyDepositAuthorizationSignatureAndContinuity( + mockVoucher, + mockDepositAuthorizationWithPermit, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return valid if deposit authorization is valid without permit", async () => { + const result = await verifyDepositAuthorizationSignatureAndContinuity( + mockVoucher, + mockDepositAuthorizationWithoutPermit, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return error if permit signature is invalid", async () => { + const invalidPermit = { + ...mockDepositAuthorizationWithPermit, + permit: { + ...mockDepositAuthorizationWithPermit.permit!, + signature: + "0x999b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c" as `0x${string}`, + }, + }; + + const result = await verifyDepositAuthorizationSignatureAndContinuity( + mockVoucher, + invalidPermit, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_signature", + payer: buyerAddress, + }); + }); + + it("should return error if deposit authorization signature is invalid", async () => { + const invalidDepositAuth = { + ...mockDepositAuthorizationWithPermit, + depositAuthorization: { + ...mockDepositAuthorizationWithPermit.depositAuthorization, + signature: + "0x999b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c" as `0x${string}`, + }, + }; + + const result = await verifyDepositAuthorizationSignatureAndContinuity( + mockVoucher, + invalidDepositAuth, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_signature", + payer: buyerAddress, + }); + }); +}); + +describe("verifyDepositAuthorizationOnchainState", () => { + const mockVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const mockDepositAuthorizationWithPermit = { + permit: { + owner: buyerAddress, + spender: escrowAddress, + value: "1000000", + nonce: "0", + deadline: 1715769600 + 1000 * 60 * 60 * 24 * 30, + domain: { + name: "USD Coin", + version: "2", + }, + signature: + "0x1ed1158f8c70dc6393f8c9a379bf4569eb13a0ae6f060465418cbb9acbf5fb536eda5bdb7a6a28317329df0b9aec501fdf15f02f04b60ac536b90da3ce6f3efb1c" as `0x${string}`, + }, + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }, + }; + + const mockDepositAuthorizationWithoutPermit = { + depositAuthorization: { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + amount: "1000000", + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b" as `0x${string}`, + }, + }; + + it("should return valid if deposit authorization is valid with permit", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(1_000_000), + allowance: BigInt(500_000), + nonce: BigInt(0), // Matches permit nonce + isDepositNonceUsed: false, + }; + + const result = verifyDepositAuthorizationOnchainState( + mockVoucher, + mockDepositAuthorizationWithPermit, + onchainData, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return valid if deposit authorization is valid without permit", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(1_000_000), + allowance: BigInt(2_000_000), // Sufficient allowance + nonce: BigInt(0), + isDepositNonceUsed: false, + }; + + const result = verifyDepositAuthorizationOnchainState( + mockVoucher, + mockDepositAuthorizationWithoutPermit, + onchainData, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return error if allowance is insufficient when no permit", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(1_000_000), + allowance: BigInt(500_000), // Insufficient allowance + nonce: BigInt(0), + isDepositNonceUsed: false, + }; + + const result = verifyDepositAuthorizationOnchainState( + mockVoucher, + mockDepositAuthorizationWithoutPermit, + onchainData, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_insufficient_allowance", + payer: buyerAddress, + }); + }); + + it("should return error if permit nonce is invalid", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(1_000_000), + allowance: BigInt(500_000), + nonce: BigInt(5), // Different nonce + isDepositNonceUsed: false, + }; + + const result = verifyDepositAuthorizationOnchainState( + mockVoucher, + mockDepositAuthorizationWithPermit, + onchainData, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_nonce_invalid", + payer: buyerAddress, + }); + }); + + it("should return error if deposit authorization nonce is already used", () => { + const onchainData = { + voucherOutstanding: BigInt(1_000_000), + voucherCollectable: BigInt(1_000_000), + availableBalance: BigInt(1_000_000), + allowance: BigInt(500_000), + nonce: BigInt(0), // permit nonce ok + isDepositNonceUsed: true, // deposit authorization nonce already used + }; + + const result = verifyDepositAuthorizationOnchainState( + mockVoucher, + mockDepositAuthorizationWithPermit, + onchainData, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_nonce_invalid", + payer: buyerAddress, + }); + }); +}); + +describe("verifyFlushAuthorization", () => { + let mockFlushAuthorization: DeferredEscrowFlushAuthorizationSigned; + + beforeEach(async () => { + vi.clearAllMocks(); + vi.useFakeTimers(); + vi.setSystemTime(new Date(1715769600 * 1000 + 100000)); // 100 seconds after timestamp + + // Import signFlushAuthorization to generate a valid signature + const { signFlushAuthorization } = await import("./sign"); + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + const flushAuthBase = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, // 30 days + }; + + const { signature } = await signFlushAuthorization( + buyer, + flushAuthBase, + 84532, + escrowAddress as `0x${string}`, + ); + + mockFlushAuthorization = { + ...flushAuthBase, + signature, + }; + }); + + afterEach(() => { + vi.useRealTimers(); + vi.resetAllMocks(); + }); + + it("should return valid if flush authorization is valid", async () => { + const result = await verifyFlushAuthorization( + mockFlushAuthorization, + escrowAddress as `0x${string}`, + 84532, + ); + expect(result).toEqual({ isValid: true }); + }); + + it("should return error if flush authorization signature is invalid", async () => { + const invalidFlushAuth = { + ...mockFlushAuthorization, + signature: "0xinvalidsignature" as `0x${string}`, + }; + + const result = await verifyFlushAuthorization( + invalidFlushAuth, + escrowAddress as `0x${string}`, + 84532, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_signature", + payer: buyerAddress, + }); + }); + + it("should return error if flush authorization is expired", async () => { + const { signFlushAuthorization } = await import("./sign"); + const buyer = createSigner( + "base-sepolia", + "0xcb160425c35458024591e64638d6f7720dac915a0fb035c5964f6d51de0987d9", + ); + + const expiredFlushAuthBase = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 - 1000, // expired + }; + + const { signature } = await signFlushAuthorization( + buyer, + expiredFlushAuthBase, + 84532, + escrowAddress as `0x${string}`, + ); + + const expiredFlushAuth = { + ...expiredFlushAuthBase, + signature, + }; + + const result = await verifyFlushAuthorization( + expiredFlushAuth, + escrowAddress as `0x${string}`, + 84532, + ); + expect(result).toEqual({ + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_continuity", + payer: buyerAddress, + }); + }); +}); diff --git a/typescript/packages/x402/src/schemes/deferred/evm/verify.ts b/typescript/packages/x402/src/schemes/deferred/evm/verify.ts new file mode 100644 index 0000000000..672f22c5f1 --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/evm/verify.ts @@ -0,0 +1,680 @@ +import { Account, Chain, Address, Hex, Transport, getAddress } from "viem"; +import { + DeferredEvmPayloadSignedVoucher, + DeferredEvmPayloadSignedVoucherSchema, + DeferredEvmPayloadVoucher, + DeferredPaymentPayload, + DeferredPaymentRequirements, + DEFERRRED_SCHEME, + DeferredEscrowFlushAuthorizationSigned, +} from "../../../types/verify/schemes/deferred"; +import { DeferredEscrowDepositAuthorization, VerifyResponse } from "../../../types"; +import { getNetworkId } from "../../../shared"; +import { + verifyDepositAuthorizationInnerSignature, + verifyPermitSignature, + verifyVoucherSignature, + verifyFlushAuthorizationSignature, +} from "./sign"; +import { ConnectedClient } from "../../../types/shared/evm/wallet"; +import { deferredEscrowABI } from "../../../types/shared/evm/deferredEscrowABI"; +import { VoucherStore } from "./store"; + +/** + * Data returned from the getVerificationData contract call + */ +export type OnchainVerificationData = { + voucherOutstanding: bigint; + voucherCollectable: bigint; + balance: bigint; + availableBalance: bigint; + allowance: bigint; + nonce: bigint; + isDepositNonceUsed: boolean; +}; + +/** + * Verifies the payment payload satisfies the payment requirements. + * + * @param paymentPayload - The payment payload to verify + * @param paymentRequirements - The payment requirements to verify + * @returns Verification result + */ +export function verifyPaymentRequirements( + paymentPayload: DeferredPaymentPayload, + paymentRequirements: DeferredPaymentRequirements, +): VerifyResponse { + // scheme + if (paymentPayload.scheme !== DEFERRRED_SCHEME) { + return { + isValid: false, + invalidReason: `invalid_deferred_evm_payload_scheme`, + }; + } + if (paymentRequirements.scheme !== DEFERRRED_SCHEME) { + return { + isValid: false, + invalidReason: `invalid_deferred_evm_requirements_scheme`, + }; + } + + // network + if (paymentPayload.network !== paymentRequirements.network) { + return { + isValid: false, + invalidReason: `invalid_deferred_evm_payload_network_mismatch`, + payer: paymentPayload.payload.voucher.buyer, + }; + } + let chainId: number; + try { + chainId = getNetworkId(paymentRequirements.network); + } catch { + return { + isValid: false, + invalidReason: `invalid_network_unsupported`, + payer: paymentPayload.payload.voucher.buyer, + }; + } + if (chainId !== paymentPayload.payload.voucher.chainId) { + return { + isValid: false, + invalidReason: `invalid_deferred_evm_payload_chain_id`, + payer: paymentPayload.payload.voucher.buyer, + }; + } + + // maxAmountRequired + const requiredVoucherValueAggregate = + paymentRequirements.extra.type === "new" + ? BigInt(paymentRequirements.maxAmountRequired) + : BigInt(paymentRequirements.maxAmountRequired) + + BigInt(paymentRequirements.extra.voucher.valueAggregate); + if (BigInt(paymentPayload.payload.voucher.valueAggregate) < requiredVoucherValueAggregate) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_value", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + // payTo + if (getAddress(paymentPayload.payload.voucher.seller) !== getAddress(paymentRequirements.payTo)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_recipient_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + // asset + if (paymentPayload.payload.voucher.asset !== paymentRequirements.asset) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_asset_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + return { + isValid: true, + }; +} + +/** + * Verifies the voucher structrure is valid and continuity is maintained + * + * @param paymentPayload - The payment payload to verify + * @param paymentRequirements - The payment requirements to verify + * @returns Verification result + */ +export function verifyVoucherContinuity( + paymentPayload: DeferredPaymentPayload, + paymentRequirements: DeferredPaymentRequirements, +): VerifyResponse { + const voucher = paymentPayload.payload.voucher; + + // expiration + const now = Math.floor(Date.now() / 1000); + if (voucher.expiry < now) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expired", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + // timestamp + if (voucher.timestamp > now) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_timestamp_too_early", + payer: paymentPayload.payload.voucher.buyer, + }; + } + + // -- New voucher -- + if (paymentRequirements.extra.type === "new") { + if (voucher.nonce != 0) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_non_zero_nonce", + payer: paymentPayload.payload.voucher.buyer, + }; + } + if (BigInt(voucher.valueAggregate) === 0n) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_zero_value_aggregate", + payer: paymentPayload.payload.voucher.buyer, + }; + } + } + + // -- Aggregation voucher -- + if (paymentRequirements.extra.type === "aggregation") { + const previousVoucher = paymentRequirements.extra.voucher; + // id + if (voucher.id.toLowerCase() !== previousVoucher.id.toLowerCase()) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_id_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // buyer + if (getAddress(voucher.buyer) !== getAddress(previousVoucher.buyer)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_buyer_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // seller + if (getAddress(voucher.seller) !== getAddress(previousVoucher.seller)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_seller_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // valueAggregate + if (BigInt(voucher.valueAggregate) < BigInt(previousVoucher.valueAggregate)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_value_aggregate_decreasing", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // asset + if (getAddress(voucher.asset) !== getAddress(previousVoucher.asset)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_asset_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // timestamp + if (voucher.timestamp < previousVoucher.timestamp) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_timestamp_decreasing", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // nonce + if (voucher.nonce !== previousVoucher.nonce + 1) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_nonce_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // escrow + if (getAddress(voucher.escrow) !== getAddress(previousVoucher.escrow)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_escrow_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // chainId + if (voucher.chainId !== previousVoucher.chainId) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_chain_id_mismatch", + payer: paymentPayload.payload.voucher.buyer, + }; + } + // expiry + if (voucher.expiry < previousVoucher.expiry) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_expiry_decreasing", + payer: paymentPayload.payload.voucher.buyer, + }; + } + } + + return { + isValid: true, + }; +} + +/** + * Verifies the voucher signature is valid + * + * - ✅ Verify the voucher signature is valid + * - ✅ Verify the voucher signer is the buyer + * + * @param voucher - The voucher to verify + * @param signature - The signature of the voucher + * @returns Verification result + */ +export async function verifyVoucherSignatureWrapper( + voucher: DeferredEvmPayloadVoucher, + signature: string, +): Promise { + const voucherSignatureIsValid = await verifyVoucherSignature( + voucher, + signature as Hex, + voucher.buyer as Address, + ); + if (!voucherSignatureIsValid) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + payer: voucher.buyer, + }; + } + + return { + isValid: true, + }; +} + +/** + * Verifies a voucher is available in the voucher store + * + * This function will retrieve voucher (id,nonce) and verify it + * against the voucher provided. + * + * @param voucher - The voucher to verify + * @param signature - The signature of the voucher + * @param id - The id of the voucher + * @param nonce - The nonce of the voucher + * @param voucherStore - The voucher store to use for verification + * @returns Verification result + */ +export async function verifyVoucherAvailability( + voucher: DeferredEvmPayloadVoucher, + signature: string, + id: string, + nonce: number, + voucherStore: VoucherStore, +): Promise { + const storeVoucher = await voucherStore.getVoucher(id, nonce); + if (!storeVoucher) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_found", + payer: voucher.buyer, + }; + } + const signedVoucher = DeferredEvmPayloadSignedVoucherSchema.parse({ + ...voucher, + signature, + }); + + const duplicateResult = verifyVoucherDuplicate(signedVoucher, storeVoucher); + if (!duplicateResult.isValid) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_found_not_duplicate", + payer: voucher.buyer, + }; + } + + return { + isValid: true, + }; +} + +/** + * Verifies two vouchers are the same, down to the signature + * + * @param newVoucher - The new voucher to verify + * @param previousVoucher - The previous voucher to verify against + * @returns Verification result + */ +export function verifyVoucherDuplicate( + newVoucher: DeferredEvmPayloadSignedVoucher, + previousVoucher: DeferredEvmPayloadSignedVoucher, +): VerifyResponse { + if ( + newVoucher.id.toLowerCase() === previousVoucher.id.toLowerCase() && + getAddress(newVoucher.buyer) === getAddress(previousVoucher.buyer) && + getAddress(newVoucher.seller) === getAddress(previousVoucher.seller) && + newVoucher.valueAggregate === previousVoucher.valueAggregate && + getAddress(newVoucher.asset) === getAddress(previousVoucher.asset) && + newVoucher.timestamp === previousVoucher.timestamp && + newVoucher.nonce === previousVoucher.nonce && + getAddress(newVoucher.escrow) === getAddress(previousVoucher.escrow) && + newVoucher.chainId === previousVoucher.chainId && + newVoucher.expiry === previousVoucher.expiry && + newVoucher.signature.toLowerCase() === previousVoucher.signature.toLowerCase() + ) { + return { + isValid: true, + }; + } + + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_voucher_not_duplicate", + payer: newVoucher.buyer, + }; +} + +/** + * Fetches all on-chain verification data in a single contract call + * + * @param client - The client to use for the onchain state verification + * @param voucher - The voucher to verify + * @param depositAuthNonce - The deposit authorization nonce (or undefined to use zero bytes) + * @returns On-chain verification data or VerifyResponse with error + */ +export async function getOnchainVerificationData< + transport extends Transport, + chain extends Chain, + account extends Account | undefined, +>( + client: ConnectedClient, + voucher: DeferredEvmPayloadVoucher, + depositAuthNonce?: string, +): Promise<{ data?: OnchainVerificationData } & VerifyResponse> { + try { + depositAuthNonce = + depositAuthNonce || "0x0000000000000000000000000000000000000000000000000000000000000000"; + + const [ + voucherOutstanding, + voucherCollectable, + balance, + availableBalance, + allowance, + permitNonce, + isDepositNonceUsed, + ] = await client.readContract({ + address: voucher.escrow as Address, + abi: deferredEscrowABI, + functionName: "getVerificationData", + args: [ + { + id: voucher.id as Hex, + buyer: voucher.buyer as Address, + seller: voucher.seller as Address, + valueAggregate: BigInt(voucher.valueAggregate), + asset: voucher.asset as Address, + timestamp: BigInt(voucher.timestamp), + nonce: BigInt(voucher.nonce), + escrow: voucher.escrow as Address, + chainId: BigInt(voucher.chainId), + expiry: BigInt(voucher.expiry), + }, + depositAuthNonce as Hex, + ], + }); + + return { + isValid: true, + data: { + voucherOutstanding, + voucherCollectable, + balance, + availableBalance, + allowance, + nonce: permitNonce, + isDepositNonceUsed, + }, + }; + } catch { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_contract_call_failed_verification_data", + }; + } +} + +/** + * Verifies the onchain state allows the payment to be settled. Accepts an optional deposit authorization, treating + * the associated funds as additional balance in the escrow deposit. + * + * - ✅ (on-chain) Verifies buyer has sufficient asset balance + * + * @param voucher - The voucher to verify + * @param depositAuthorization - An already verified deposit authorization, used to consider additional balance to the buyer's account + * @param onchainData - Pre-fetched onchain verification data + * @param ignoreThawing - Whether to consider thawing funds in the escrow account + * @returns Verification result + */ +export function verifyVoucherOnchainState( + voucher: DeferredEvmPayloadVoucher, + depositAuthorization: DeferredEscrowDepositAuthorization | undefined, + onchainData: OnchainVerificationData, + ignoreThawing: boolean = false, +): VerifyResponse { + // By default we ignore funds that are thawing, however when settling a voucher those should be considered + const balance = ignoreThawing ? onchainData.balance : onchainData.availableBalance; + + // If a deposit authorization is provided we consider it as additional balance in the escrow deposit + // Note that we do not verify the validity of the deposit authorization here + const authorizationBalance = depositAuthorization + ? BigInt(depositAuthorization.depositAuthorization.amount) + : 0n; + + // Verify buyer has sufficient asset balance + if (balance + authorizationBalance < onchainData.voucherOutstanding) { + return { + isValid: false, + invalidReason: "insufficient_funds", + payer: voucher.buyer, + }; + } + + return { + isValid: true, + }; +} + +/** + * Verifies the onchain state allows a deposit authorization to be executed. + * + * - ✅ (on-chain) Verifies escrow can pull from the buyer's account based on their allowance (or permit) + * - ✅ (on-chain) If there is a permit, it verifies permit nonce is valid + * - ✅ (on-chain) Verifies deposit authorization nonce has not been used + * + * @param voucher - The voucher to verify + * @param depositAuthorization - The deposit authorization to verify + * @param onchainData - Pre-fetched onchain verification data + * @returns Verification result + */ +export function verifyDepositAuthorizationOnchainState( + voucher: DeferredEvmPayloadVoucher, + depositAuthorization: DeferredEscrowDepositAuthorization, + onchainData: OnchainVerificationData, +): VerifyResponse { + // Verify escrow can pull from the buyer's account based on their allowance (or permit) + const allowance = depositAuthorization.permit + ? BigInt(depositAuthorization.permit.value) + : onchainData.allowance; + + if (BigInt(depositAuthorization.depositAuthorization.amount) > allowance) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_insufficient_allowance", + payer: depositAuthorization.depositAuthorization.buyer, + }; + } + + // Verify permit nonce if permit is present + if (depositAuthorization.permit) { + if (onchainData.nonce !== BigInt(depositAuthorization.permit.nonce)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_nonce_invalid", + payer: voucher.buyer, + }; + } + } + + // Verify deposit authorization nonce hasn't been used + if (onchainData.isDepositNonceUsed) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_nonce_invalid", + payer: voucher.buyer, + }; + } + + return { + isValid: true, + }; +} + +/** + * Verifies a deposit authorization is valid. Checks the signature of the permit and the deposit authorization. + * + * @param voucher - The voucher that the deposit authorization is escrowing for + * @param depositAuthorization - The deposit authorization to verify + * @returns Verification result + */ +export async function verifyDepositAuthorizationSignatureAndContinuity( + voucher: DeferredEvmPayloadVoucher, + depositAuthorization: DeferredEscrowDepositAuthorization, +): Promise { + const { permit, depositAuthorization: depositAuthorizationInner } = depositAuthorization; + + const DATE_NOW = Math.floor(Date.now() / 1000); + const DATE_ONE_WEEK = 604800; + + // Verify the permit signature + if (permit) { + const isPermitValid = await verifyPermitSignature( + permit, + permit.signature as Hex, + permit.owner as Address, + voucher.chainId, + voucher.asset as Address, // This ensures the permit is for the voucher's asset + ); + if (!isPermitValid) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_signature", + payer: permit.owner, + }; + } + + // Verify permit continuity + if ( + getAddress(permit.owner) !== getAddress(voucher.buyer) || + getAddress(permit.spender) !== getAddress(voucher.escrow) || + permit.deadline < DATE_NOW + DATE_ONE_WEEK + ) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_permit_continuity", + payer: permit.owner, + }; + } + } + + // Verify deposit authorization signature + const isDepositAuthorizationValid = await verifyDepositAuthorizationInnerSignature( + depositAuthorizationInner, + depositAuthorizationInner.signature as Hex, + depositAuthorizationInner.buyer as Address, + voucher.chainId, + voucher.escrow as Address, + ); + if (!isDepositAuthorizationValid) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_signature", + payer: depositAuthorizationInner.buyer, + }; + } + + // Verify deposit authorization continuity + if ( + getAddress(depositAuthorizationInner.buyer) !== getAddress(voucher.buyer) || + getAddress(depositAuthorizationInner.seller) !== getAddress(voucher.seller) || + getAddress(depositAuthorizationInner.asset) !== getAddress(voucher.asset) || + depositAuthorizationInner.expiry < DATE_NOW + DATE_ONE_WEEK + ) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_continuity", + payer: depositAuthorizationInner.buyer, + }; + } + + // Verify permit owner matches the deposit authorization buyer + if (permit) { + if (getAddress(depositAuthorizationInner.buyer) !== getAddress(permit.owner)) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_deposit_authorization_buyer_mismatch", + payer: depositAuthorizationInner.buyer, + }; + } + } + + return { + isValid: true, + }; +} + +/** + * Verifies a flush authorization is valid. Checks the signature of the flush authorization. + * + * @param flushAuthorization - The flush authorization to verify + * @param escrow - The address of the escrow contract + * @param chainId - The chain ID + * @returns Verification result + */ +export async function verifyFlushAuthorization( + flushAuthorization: DeferredEscrowFlushAuthorizationSigned, + escrow: Address, + chainId: number, +): Promise { + // Verify flush authorization signature + const isFlushAuthorizationValid = await verifyFlushAuthorizationSignature( + flushAuthorization, + flushAuthorization.signature as Hex, + flushAuthorization.buyer as Address, + chainId, + escrow, + ); + if (!isFlushAuthorizationValid) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_signature", + payer: flushAuthorization.buyer, + }; + } + + // Verify flush authorization continuity + const DATE_NOW = Math.floor(Date.now() / 1000); + if (flushAuthorization.expiry < DATE_NOW) { + return { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_flush_authorization_continuity", + payer: flushAuthorization.buyer, + }; + } + + return { + isValid: true, + }; +} diff --git a/typescript/packages/x402/src/schemes/deferred/index.ts b/typescript/packages/x402/src/schemes/deferred/index.ts new file mode 100644 index 0000000000..5bd1dde8df --- /dev/null +++ b/typescript/packages/x402/src/schemes/deferred/index.ts @@ -0,0 +1 @@ +export * as evm from "./evm"; diff --git a/typescript/packages/x402/src/schemes/exact/evm/client.test.ts b/typescript/packages/x402/src/schemes/exact/evm/client.test.ts index 02527309fd..bf8cf145ff 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/client.test.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/client.test.ts @@ -1,6 +1,10 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createSignerSepolia, SignerWallet } from "../../../types/shared/evm"; -import { PaymentRequirements, UnsignedPaymentPayload } from "../../../types/verify"; +import { + ExactEvmPayload, + PaymentRequirements, + UnsignedPaymentPayload, +} from "../../../types/verify"; import { createPaymentHeader, preparePaymentHeader, signPaymentHeader } from "./client"; import { signAuthorization } from "./sign"; import { encodePayment } from "./utils/paymentUtils"; @@ -166,7 +170,9 @@ describe("signPaymentHeader", () => { expect(result.x402Version).toBe(mockUnsignedHeader.x402Version); expect(result.scheme).toBe(mockUnsignedHeader.scheme); expect(result.network).toBe(mockUnsignedHeader.network); - expect(result.payload.authorization).toEqual(mockUnsignedHeader.payload.authorization); + expect((result.payload as ExactEvmPayload).authorization).toEqual( + mockUnsignedHeader.payload.authorization, + ); }); it("should throw an error if signing fails", async () => { diff --git a/typescript/packages/x402/src/schemes/exact/evm/client.ts b/typescript/packages/x402/src/schemes/exact/evm/client.ts index 771e51a60d..cd19554232 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/client.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/client.ts @@ -1,8 +1,13 @@ import { Address, Chain, LocalAccount, Transport } from "viem"; import { isSignerWallet, SignerWallet } from "../../../types/shared/evm"; -import { PaymentPayload, PaymentRequirements, UnsignedPaymentPayload } from "../../../types/verify"; +import { PaymentRequirements } from "../../../types/verify"; import { createNonce, signAuthorization } from "./sign"; import { encodePayment } from "./utils/paymentUtils"; +import { + ExactPaymentPayload, + UnsignedExactPaymentPayload, +} from "../../../types/verify/schemes/exact"; +import { EXACT_SCHEME } from "../../../types/verify/schemes/exact"; /** * Prepares an unsigned payment header with the given sender address and payment requirements. @@ -16,7 +21,7 @@ export function preparePaymentHeader( from: Address, x402Version: number, paymentRequirements: PaymentRequirements, -): UnsignedPaymentPayload { +): UnsignedExactPaymentPayload { const nonce = createNonce(); const validAfter = BigInt( @@ -28,7 +33,7 @@ export function preparePaymentHeader( return { x402Version, - scheme: paymentRequirements.scheme, + scheme: EXACT_SCHEME, network: paymentRequirements.network, payload: { signature: undefined, @@ -55,8 +60,8 @@ export function preparePaymentHeader( export async function signPaymentHeader( client: SignerWallet | LocalAccount, paymentRequirements: PaymentRequirements, - unsignedPaymentHeader: UnsignedPaymentPayload, -): Promise { + unsignedPaymentHeader: UnsignedExactPaymentPayload, +): Promise { const { signature } = await signAuthorization( client, unsignedPaymentHeader.payload.authorization, @@ -84,7 +89,7 @@ export async function createPayment | LocalAccount, x402Version: number, paymentRequirements: PaymentRequirements, -): Promise { +): Promise { const from = isSignerWallet(client) ? client.account!.address : client.address; const unsignedPaymentHeader = preparePaymentHeader(from, x402Version, paymentRequirements); return signPaymentHeader(client, paymentRequirements, unsignedPaymentHeader); diff --git a/typescript/packages/x402/src/schemes/exact/evm/facilitator.ts b/typescript/packages/x402/src/schemes/exact/evm/facilitator.ts index fd8a1647fa..6bf59c5ec4 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/facilitator.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/facilitator.ts @@ -3,19 +3,19 @@ import { getNetworkId } from "../../../shared"; import { getVersion, getERC20Balance } from "../../../shared/evm"; import { usdcABI as abi, - authorizationTypes, + typedDataTypes, + transferWithAuthorizationPrimaryType, config, ConnectedClient, SignerWallet, } from "../../../types/shared/evm"; import { - PaymentPayload, PaymentRequirements, SettleResponse, VerifyResponse, ExactEvmPayload, } from "../../../types/verify"; -import { SCHEME } from "../../exact"; +import { ExactPaymentPayload, EXACT_SCHEME } from "../../../types/verify/schemes/exact"; /** * Verifies a payment payload against the required payment details @@ -39,7 +39,7 @@ export async function verify< account extends Account | undefined, >( client: ConnectedClient, - payload: PaymentPayload, + payload: ExactPaymentPayload, paymentRequirements: PaymentRequirements, ): Promise { /* TODO: work with security team on brainstorming more verification steps @@ -58,7 +58,7 @@ export async function verify< const exactEvmPayload = payload.payload as ExactEvmPayload; // Verify payload version - if (payload.scheme !== SCHEME || paymentRequirements.scheme !== SCHEME) { + if (payload.scheme !== EXACT_SCHEME || paymentRequirements.scheme !== EXACT_SCHEME) { return { isValid: false, invalidReason: `unsupported_scheme`, @@ -84,8 +84,8 @@ export async function verify< } // Verify permit signature is recoverable for the owner address const permitTypedData = { - types: authorizationTypes, - primaryType: "TransferWithAuthorization" as const, + types: typedDataTypes, + primaryType: transferWithAuthorizationPrimaryType, domain: { name, version, @@ -183,7 +183,7 @@ export async function verify< */ export async function settle( wallet: SignerWallet, - paymentPayload: PaymentPayload, + paymentPayload: ExactPaymentPayload, paymentRequirements: PaymentRequirements, ): Promise { const payload = paymentPayload.payload as ExactEvmPayload; diff --git a/typescript/packages/x402/src/schemes/exact/evm/sign.ts b/typescript/packages/x402/src/schemes/exact/evm/sign.ts index b7d72af4a0..4c529c50cb 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/sign.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/sign.ts @@ -1,12 +1,11 @@ import { Chain, getAddress, Hex, LocalAccount, toHex, Transport } from "viem"; import { getNetworkId } from "../../../shared"; +import { typedDataTypes, isAccount, isSignerWallet, SignerWallet } from "../../../types/shared/evm"; +import { PaymentRequirements } from "../../../types/verify"; import { - authorizationTypes, - isAccount, - isSignerWallet, - SignerWallet, -} from "../../../types/shared/evm"; -import { ExactEvmPayloadAuthorization, PaymentRequirements } from "../../../types/verify"; + ExactEvmPayloadAuthorization, + ExactPaymentRequirementsSchema, +} from "../../../types/verify/schemes/exact"; /** * Signs an EIP-3009 authorization for USDC transfer @@ -28,14 +27,15 @@ import { ExactEvmPayloadAuthorization, PaymentRequirements } from "../../../type export async function signAuthorization( walletClient: SignerWallet | LocalAccount, { from, to, value, validAfter, validBefore, nonce }: ExactEvmPayloadAuthorization, - { asset, network, extra }: PaymentRequirements, + paymentRequirements: PaymentRequirements, ): Promise<{ signature: Hex }> { + const { asset, network, extra } = ExactPaymentRequirementsSchema.parse(paymentRequirements); const chainId = getNetworkId(network); const name = extra?.name; const version = extra?.version; const data = { - types: authorizationTypes, + types: typedDataTypes, domain: { name, version, diff --git a/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.test.ts b/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.test.ts index 5ad1d07b9f..c0e0b2ea18 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.test.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.test.ts @@ -52,7 +52,7 @@ describe("paymentUtils", () => { it("throws on invalid network in encodePayment", () => { const invalidPayment = { ...validEvmPayment, network: "invalid-network" }; - expect(() => encodePayment(invalidPayment)).toThrow("Invalid network"); + expect(() => encodePayment(invalidPayment as PaymentPayload)).toThrow("Invalid network"); }); it("throws on invalid network in decodePayment", () => { diff --git a/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.ts b/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.ts index 6d0727a3b9..12772d31b5 100644 --- a/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.ts +++ b/typescript/packages/x402/src/schemes/exact/evm/utils/paymentUtils.ts @@ -1,10 +1,16 @@ import { safeBase64Encode, safeBase64Decode } from "../../../../shared"; -import { SupportedEVMNetworks, SupportedSVMNetworks } from "../../../../types"; +import { + ExactEvmPayloadSchema, + ExactSvmPayloadSchema, + SupportedEVMNetworks, + SupportedSVMNetworks, +} from "../../../../types"; import { PaymentPayload, - PaymentPayloadSchema, ExactEvmPayload, ExactSvmPayload, + ExactPaymentPayload, + ExactPaymentPayloadSchema, } from "../../../../types/verify"; /** @@ -14,13 +20,14 @@ import { * @returns A base64 encoded string representation of the payment payload */ export function encodePayment(payment: PaymentPayload): string { - let safe: PaymentPayload; + let safe: ExactPaymentPayload; // evm if (SupportedEVMNetworks.includes(payment.network)) { - const evmPayload = payment.payload as ExactEvmPayload; + const exactPayment = ExactPaymentPayloadSchema.parse(payment); + const evmPayload = ExactEvmPayloadSchema.parse(exactPayment.payload); safe = { - ...payment, + ...exactPayment, payload: { ...evmPayload, authorization: Object.fromEntries( @@ -36,7 +43,9 @@ export function encodePayment(payment: PaymentPayload): string { // svm if (SupportedSVMNetworks.includes(payment.network)) { - safe = { ...payment, payload: payment.payload as ExactSvmPayload }; + const exactPayment = ExactPaymentPayloadSchema.parse(payment); + const svmPayload = ExactSvmPayloadSchema.parse(exactPayment.payload); + safe = { ...exactPayment, payload: svmPayload }; return safeBase64Encode(JSON.stringify(safe)); } @@ -49,7 +58,7 @@ export function encodePayment(payment: PaymentPayload): string { * @param payment - The base64 encoded payment string to decode * @returns The decoded and validated PaymentPayload object */ -export function decodePayment(payment: string): PaymentPayload { +export function decodePayment(payment: string): ExactPaymentPayload { const decoded = safeBase64Decode(payment); const parsed = JSON.parse(decoded); @@ -73,6 +82,6 @@ export function decodePayment(payment: string): PaymentPayload { throw new Error("Invalid network"); } - const validated = PaymentPayloadSchema.parse(obj); + const validated = ExactPaymentPayloadSchema.parse(obj); return validated; } diff --git a/typescript/packages/x402/src/schemes/exact/index.ts b/typescript/packages/x402/src/schemes/exact/index.ts index c4ecc8e84b..4ab33771eb 100644 --- a/typescript/packages/x402/src/schemes/exact/index.ts +++ b/typescript/packages/x402/src/schemes/exact/index.ts @@ -1,4 +1,2 @@ export * as evm from "./evm"; export * as svm from "./svm"; - -export const SCHEME = "exact"; diff --git a/typescript/packages/x402/src/schemes/exact/svm/client.test.ts b/typescript/packages/x402/src/schemes/exact/svm/client.test.ts index c271bba4ee..88bb330e54 100644 --- a/typescript/packages/x402/src/schemes/exact/svm/client.test.ts +++ b/typescript/packages/x402/src/schemes/exact/svm/client.test.ts @@ -236,7 +236,7 @@ describe("SVM Client", () => { // Act & Assert await expect( - createAndSignPayment(clientSigner, 1, paymentReqsWithoutFeePayer), + createAndSignPayment(clientSigner, 1, paymentReqsWithoutFeePayer as PaymentRequirements), ).rejects.toThrow( "feePayer is required in paymentRequirements.extra in order to set the facilitator as the fee payer for the create associated token account instruction", ); diff --git a/typescript/packages/x402/src/schemes/exact/svm/client.ts b/typescript/packages/x402/src/schemes/exact/svm/client.ts index 70ca497de5..999b1814aa 100644 --- a/typescript/packages/x402/src/schemes/exact/svm/client.ts +++ b/typescript/packages/x402/src/schemes/exact/svm/client.ts @@ -14,7 +14,11 @@ import { TransactionSigner, Instruction, } from "@solana/kit"; -import { PaymentPayload, PaymentRequirements } from "../../../types/verify"; +import { + ExactPaymentRequirementsSchema, + PaymentPayload, + PaymentRequirements, +} from "../../../types/verify"; import { X402Config } from "../../../types/config"; import { fetchMint, @@ -112,7 +116,8 @@ async function createTransferTransactionMessage( ); // create tx to simulate - const feePayer = paymentRequirements.extra?.feePayer as Address; + const { extra } = ExactPaymentRequirementsSchema.parse(paymentRequirements); + const feePayer = extra?.feePayer as Address; const txToSimulate = pipe( createTransactionMessage({ version: 0 }), tx => setTransactionMessageComputeUnitPrice(1, tx), // 1 microlamport priority fee @@ -211,7 +216,7 @@ async function createAtaInstructionOrUndefined( tokenProgramAddress: Address, config?: X402Config, ): Promise { - const { asset, payTo, extra } = paymentRequirements; + const { asset, payTo, extra } = ExactPaymentRequirementsSchema.parse(paymentRequirements); const feePayer = extra?.feePayer as Address; // feePayer is required @@ -236,7 +241,7 @@ async function createAtaInstructionOrUndefined( // if the ATA does not exist, return an instruction to create it if (!maybeAccount.exists) { return getCreateAssociatedTokenInstruction({ - payer: paymentRequirements.extra?.feePayer as TransactionSigner, + payer: extra?.feePayer as unknown as TransactionSigner, ata: destinationATAAddress, owner: payTo as Address, mint: asset as Address, diff --git a/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.test.ts b/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.test.ts index 9d67effe53..7000097e12 100644 --- a/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.test.ts +++ b/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.test.ts @@ -20,7 +20,6 @@ import { } from "@solana/kit"; import { PaymentPayload, PaymentRequirements, ExactSvmPayload } from "../../../../types/verify"; import { Network } from "../../../../types"; -import { SCHEME } from "../../"; import * as SvmShared from "../../../../shared/svm"; import * as rpc from "../../../../shared/svm/rpc"; import { @@ -42,6 +41,7 @@ import { parseSetComputeUnitLimitInstruction, parseSetComputeUnitPriceInstruction, } from "@solana-program/compute-budget"; +import { EXACT_SCHEME } from "../../../../types/verify/schemes"; vi.mock("@solana/kit", async () => { const actual = await vi.importActual("@solana/kit"); @@ -109,7 +109,7 @@ const devnetUSDCAddress = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU"; describe("verify", () => { describe("verifySchemesAndNetworks", () => { const validPayload: PaymentPayload = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", x402Version: 1, payload: { @@ -118,7 +118,7 @@ describe("verify", () => { }; const validRequirements: PaymentRequirements = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", payTo: "someAddress", maxAmountRequired: "1000", @@ -284,7 +284,7 @@ describe("verify", () => { }, }; mockPaymentRequirements = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", payTo: "payToAddress", maxAmountRequired: "1000", @@ -412,13 +412,13 @@ describe("verify", () => { mockSigner = {} as any; mockPayerAddress = (await generateKeyPairSigner()).address; mockPayload = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", x402Version: 1, payload: { transaction: "..." } as ExactSvmPayload, }; mockRequirements = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", payTo: "payToAddress", maxAmountRequired: "1000", @@ -491,7 +491,7 @@ describe("verify", () => { }); it("should return isValid: false if schemes or networks are invalid", async () => { - const invalidPayload = { ...mockPayload, scheme: "invalid" as "exact" }; + const invalidPayload = { ...mockPayload, scheme: "invalid" } as unknown as PaymentPayload; const result = await verify(mockSigner, invalidPayload, mockRequirements); expect(result.isValid).toBe(false); expect(result.invalidReason).toBe("unsupported_scheme"); @@ -528,6 +528,7 @@ describe("verify", () => { }); it("should return isValid: false if simulation fails", async () => { + console.log("SCHEME", EXACT_SCHEME); vi.mocked(SvmShared.signAndSimulateTransaction).mockResolvedValue({ value: { err: "simulation_error" }, } as any); @@ -550,7 +551,7 @@ describe("verify", () => { vi.clearAllMocks(); mockPaymentRequirements = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet", payTo: "payToAddress", maxAmountRequired: "1000", @@ -792,7 +793,7 @@ describe("verify", () => { describe("Custom RPC Configuration", () => { const mockPaymentRequirements: PaymentRequirements = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet" as Network, payTo: "TestRecipient111111111111111111111111111" as any, asset: "TestToken1111111111111111111111111111111" as any, @@ -804,7 +805,7 @@ describe("verify", () => { }; const mockPayload: PaymentPayload = { - scheme: SCHEME, + scheme: EXACT_SCHEME, network: "solana-devnet" as Network, x402Version: 1, payload: { diff --git a/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.ts b/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.ts index 65bfb936e7..c0472b9bd0 100644 --- a/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.ts +++ b/typescript/packages/x402/src/schemes/exact/svm/facilitator/verify.ts @@ -49,8 +49,8 @@ import { signAndSimulateTransaction, getTokenPayerFromTransaction, } from "../../../../shared/svm"; +import { EXACT_SCHEME } from "../../../../types/verify/schemes"; import { getRpcClient } from "../../../../shared/svm/rpc"; -import { SCHEME } from "../../"; /** * Verify the payment payload against the payment requirements. @@ -136,7 +136,7 @@ export function verifySchemesAndNetworks( payload: PaymentPayload, paymentRequirements: PaymentRequirements, ): void { - if (payload.scheme !== SCHEME || paymentRequirements.scheme !== SCHEME) { + if (payload.scheme !== EXACT_SCHEME || paymentRequirements.scheme !== EXACT_SCHEME) { throw new Error("unsupported_scheme"); } diff --git a/typescript/packages/x402/src/schemes/index.ts b/typescript/packages/x402/src/schemes/index.ts index 40d12013d5..477e67bb3e 100644 --- a/typescript/packages/x402/src/schemes/index.ts +++ b/typescript/packages/x402/src/schemes/index.ts @@ -1,2 +1,3 @@ +export * as deferred from "./deferred"; export * as exact from "./exact"; export * from "./utils"; diff --git a/typescript/packages/x402/src/shared/middleware.test.ts b/typescript/packages/x402/src/shared/middleware.test.ts index b60d1c32ca..cc6afd5404 100644 --- a/typescript/packages/x402/src/shared/middleware.test.ts +++ b/typescript/packages/x402/src/shared/middleware.test.ts @@ -4,9 +4,8 @@ import { findMatchingRoute, getDefaultAsset, processPriceToAtomicAmount, -} from "x402/shared"; -import { RoutesConfig } from "./middleware"; -import { Network } from "./network"; +} from "../shared"; +import { Network, RoutesConfig } from "../types"; describe("computeRoutePatterns", () => { it("should handle simple string price routes", () => { diff --git a/typescript/packages/x402/src/shared/network.ts b/typescript/packages/x402/src/shared/network.ts index e427b636b7..355fc51160 100644 --- a/typescript/packages/x402/src/shared/network.ts +++ b/typescript/packages/x402/src/shared/network.ts @@ -1,4 +1,9 @@ -import { EvmNetworkToChainId, Network, SvmNetworkToChainId } from "../types/shared"; +import { + EvmNetworkToChainId, + Network, + SvmNetworkToChainId, + ChainIdToNetwork, +} from "../types/shared"; /** * Converts a network name to its corresponding chain ID @@ -16,3 +21,19 @@ export function getNetworkId(network: Network): number { } throw new Error(`Unsupported network: ${network}`); } + +/** + * Converts a chain ID to its corresponding network name + * + * @param chainId - The chain ID to convert to a network name + * @returns The network name for the specified chain ID + * @throws Error if the chain ID is not supported + */ +export function getNetworkName(chainId: number): Network { + if (ChainIdToNetwork[chainId]) { + return ChainIdToNetwork[chainId]; + } + + // TODO: Solana + throw new Error(`Unsupported chain ID: ${chainId}`); +} diff --git a/typescript/packages/x402/src/shared/svm/transaction.ts b/typescript/packages/x402/src/shared/svm/transaction.ts index 502d84709b..20178a4765 100644 --- a/typescript/packages/x402/src/shared/svm/transaction.ts +++ b/typescript/packages/x402/src/shared/svm/transaction.ts @@ -1,4 +1,4 @@ -import { ExactSvmPayload } from "../../types/verify/x402Specs"; +import { ExactSvmPayload } from "../../types/verify"; import { getBase64EncodedWireTransaction, getBase64Encoder, diff --git a/typescript/packages/x402/src/types/config.ts b/typescript/packages/x402/src/types/config.ts index 90c44da57e..743a637b09 100644 --- a/typescript/packages/x402/src/types/config.ts +++ b/typescript/packages/x402/src/types/config.ts @@ -1,3 +1,5 @@ +import { SchemeContext } from "."; + /** * Configuration options for Solana (SVM) RPC connections. */ @@ -9,6 +11,11 @@ export interface SvmConfig { rpcUrl?: string; } +/** + * Extra payload to be considered in the payment header creation, scheme dependent interpretation and validation. + */ +export type ExtraPayload = Record; + /** * Configuration options for X402 client and facilitator operations. */ @@ -16,4 +23,8 @@ export interface X402Config { /** Configuration for Solana (SVM) operations */ svmConfig?: SvmConfig; // Future: evmConfig?: EvmConfig for EVM-specific configurations + /** Extra payload for header creation. */ + extraPayload?: ExtraPayload; + /** Scheme specific context. */ + schemeContext?: SchemeContext; } diff --git a/typescript/packages/x402/src/types/shared/evm/deferredEscrowABI.ts b/typescript/packages/x402/src/types/shared/evm/deferredEscrowABI.ts new file mode 100644 index 0000000000..94b42bc967 --- /dev/null +++ b/typescript/packages/x402/src/types/shared/evm/deferredEscrowABI.ts @@ -0,0 +1,726 @@ +export const deferredEscrowABI = [ + { + type: "constructor", + inputs: [{ name: "_thawingPeriod", type: "uint256", internalType: "uint256" }], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "DEPOSIT_AUTHORIZATION_TYPEHASH", + inputs: [], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "DOMAIN_SEPARATOR", + inputs: [], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "FLUSH_ALL_AUTHORIZATION_TYPEHASH", + inputs: [], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "FLUSH_AUTHORIZATION_TYPEHASH", + inputs: [], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "MAX_THAWING_PERIOD", + inputs: [], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], + stateMutability: "view", + }, + { + type: "function", + name: "THAWING_PERIOD", + inputs: [], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], + stateMutability: "view", + }, + { + type: "function", + name: "VOUCHER_TYPEHASH", + inputs: [], + outputs: [{ name: "", type: "bytes32", internalType: "bytes32" }], + stateMutability: "view", + }, + { + type: "function", + name: "cancelThaw", + inputs: [ + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "collect", + inputs: [ + { + name: "voucher", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.Voucher", + components: [ + { name: "id", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "valueAggregate", type: "uint256", internalType: "uint256" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "timestamp", type: "uint64", internalType: "uint64" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "escrow", type: "address", internalType: "address" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "collectMany", + inputs: [ + { + name: "vouchers", + type: "tuple[]", + internalType: "struct IDeferredPaymentEscrow.SignedVoucher[]", + components: [ + { + name: "voucher", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.Voucher", + components: [ + { name: "id", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "valueAggregate", type: "uint256", internalType: "uint256" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "timestamp", type: "uint64", internalType: "uint64" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "escrow", type: "address", internalType: "address" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "deposit", + inputs: [ + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "depositMany", + inputs: [ + { name: "asset", type: "address", internalType: "address" }, + { + name: "deposits", + type: "tuple[]", + internalType: "struct IDeferredPaymentEscrow.DepositInput[]", + components: [ + { name: "seller", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "depositTo", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "depositWithAuthorization", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.DepositAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "eip712Domain", + inputs: [], + outputs: [ + { name: "fields", type: "bytes1", internalType: "bytes1" }, + { name: "name", type: "string", internalType: "string" }, + { name: "version", type: "string", internalType: "string" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "verifyingContract", type: "address", internalType: "address" }, + { name: "salt", type: "bytes32", internalType: "bytes32" }, + { name: "extensions", type: "uint256[]", internalType: "uint256[]" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "flushAllWithAuthorization", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.FlushAllAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "flushWithAuthorization", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.FlushAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "getAccount", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.EscrowAccount", + components: [ + { name: "balance", type: "uint256", internalType: "uint256" }, + { name: "thawingAmount", type: "uint256", internalType: "uint256" }, + { name: "thawEndTime", type: "uint64", internalType: "uint64" }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getAccountData", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "voucherIds", type: "bytes32[]", internalType: "bytes32[]" }, + { name: "valueAggregates", type: "uint256[]", internalType: "uint256[]" }, + ], + outputs: [ + { name: "balance", type: "uint256", internalType: "uint256" }, + { name: "allowance", type: "uint256", internalType: "uint256" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getOutstandingAndCollectableAmount", + inputs: [ + { + name: "voucher", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.Voucher", + components: [ + { name: "id", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "valueAggregate", type: "uint256", internalType: "uint256" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "timestamp", type: "uint64", internalType: "uint64" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "escrow", type: "address", internalType: "address" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + ], + outputs: [ + { name: "outstanding", type: "uint256", internalType: "uint256" }, + { name: "collectable", type: "uint256", internalType: "uint256" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getVerificationData", + inputs: [ + { + name: "voucher", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.Voucher", + components: [ + { name: "id", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "valueAggregate", type: "uint256", internalType: "uint256" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "timestamp", type: "uint64", internalType: "uint64" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "escrow", type: "address", internalType: "address" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "depositAuthNonce", type: "bytes32", internalType: "bytes32" }, + ], + outputs: [ + { name: "voucherOutstanding", type: "uint256", internalType: "uint256" }, + { name: "voucherCollectable", type: "uint256", internalType: "uint256" }, + { name: "balance", type: "uint256", internalType: "uint256" }, + { name: "availableBalance", type: "uint256", internalType: "uint256" }, + { name: "allowance", type: "uint256", internalType: "uint256" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "isDepositNonceUsed", type: "bool", internalType: "bool" }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getVoucherCollected", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "voucherId", type: "bytes32", internalType: "bytes32" }, + ], + outputs: [{ name: "", type: "uint256", internalType: "uint256" }], + stateMutability: "view", + }, + { + type: "function", + name: "isDepositAuthorizationNonceUsed", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isDepositAuthorizationValid", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.DepositAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isFlushAllAuthorizationNonceUsed", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isFlushAllAuthorizationValid", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.FlushAllAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isFlushAuthorizationNonceUsed", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isFlushAuthorizationValid", + inputs: [ + { + name: "auth", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.FlushAuthorization", + components: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "nonce", type: "bytes32", internalType: "bytes32" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "isVoucherSignatureValid", + inputs: [ + { + name: "voucher", + type: "tuple", + internalType: "struct IDeferredPaymentEscrow.Voucher", + components: [ + { name: "id", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "valueAggregate", type: "uint256", internalType: "uint256" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "timestamp", type: "uint64", internalType: "uint64" }, + { name: "nonce", type: "uint256", internalType: "uint256" }, + { name: "escrow", type: "address", internalType: "address" }, + { name: "chainId", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint64", internalType: "uint64" }, + ], + }, + { name: "signature", type: "bytes", internalType: "bytes" }, + ], + outputs: [{ name: "", type: "bool", internalType: "bool" }], + stateMutability: "view", + }, + { + type: "function", + name: "thaw", + inputs: [ + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + { name: "amount", type: "uint256", internalType: "uint256" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "withdraw", + inputs: [ + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "DepositAuthorized", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "nonce", type: "bytes32", indexed: false, internalType: "bytes32" }, + ], + anonymous: false, + }, + { + type: "event", + name: "Deposited", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "newBalance", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { type: "event", name: "EIP712DomainChanged", inputs: [], anonymous: false }, + { + type: "event", + name: "FlushAllAuthorized", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "nonce", type: "bytes32", indexed: false, internalType: "bytes32" }, + { name: "accountsFlushed", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "FlushAuthorized", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "nonce", type: "bytes32", indexed: false, internalType: "bytes32" }, + { name: "thawing", type: "bool", indexed: false, internalType: "bool" }, + ], + anonymous: false, + }, + { + type: "event", + name: "ThawCancelled", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "ThawInitiated", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "newThawingAmount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "previousThawingAmount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "newThawEndTime", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "previousThawEndTime", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "VoucherAlreadyCollected", + inputs: [ + { name: "voucherId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: false, internalType: "address" }, + { name: "totalCollected", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "VoucherCollected", + inputs: [ + { name: "voucherId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: false, internalType: "address" }, + { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "totalCollected", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "VoucherNoCollectableBalance", + inputs: [ + { name: "voucherId", type: "bytes32", indexed: true, internalType: "bytes32" }, + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: false, internalType: "address" }, + { name: "outstanding", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "alreadyCollected", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "event", + name: "Withdrawn", + inputs: [ + { name: "buyer", type: "address", indexed: true, internalType: "address" }, + { name: "seller", type: "address", indexed: true, internalType: "address" }, + { name: "asset", type: "address", indexed: true, internalType: "address" }, + { name: "amount", type: "uint256", indexed: false, internalType: "uint256" }, + { name: "remainingBalance", type: "uint256", indexed: false, internalType: "uint256" }, + ], + anonymous: false, + }, + { + type: "error", + name: "AuthorizationExpired", + inputs: [ + { name: "expiry", type: "uint64", internalType: "uint64" }, + { name: "currentTime", type: "uint256", internalType: "uint256" }, + ], + }, + { + type: "error", + name: "InsufficientBalance", + inputs: [ + { name: "available", type: "uint256", internalType: "uint256" }, + { name: "requested", type: "uint256", internalType: "uint256" }, + ], + }, + { + type: "error", + name: "InvalidAddress", + inputs: [{ name: "provided", type: "address", internalType: "address" }], + }, + { + type: "error", + name: "InvalidAmount", + inputs: [{ name: "provided", type: "uint256", internalType: "uint256" }], + }, + { + type: "error", + name: "InvalidAsset", + inputs: [{ name: "provided", type: "address", internalType: "address" }], + }, + { type: "error", name: "InvalidAuthorization", inputs: [] }, + { + type: "error", + name: "InvalidChainId", + inputs: [ + { name: "provided", type: "uint256", internalType: "uint256" }, + { name: "expected", type: "uint256", internalType: "uint256" }, + ], + }, + { + type: "error", + name: "InvalidEscrow", + inputs: [ + { name: "provided", type: "address", internalType: "address" }, + { name: "expected", type: "address", internalType: "address" }, + ], + }, + { type: "error", name: "InvalidShortString", inputs: [] }, + { + type: "error", + name: "InvalidSignature", + inputs: [ + { name: "voucherId", type: "bytes32", internalType: "bytes32" }, + { name: "buyer", type: "address", internalType: "address" }, + ], + }, + { + type: "error", + name: "InvalidThawingPeriod", + inputs: [ + { name: "provided", type: "uint256", internalType: "uint256" }, + { name: "maximum", type: "uint256", internalType: "uint256" }, + ], + }, + { type: "error", name: "NoDepositsProvided", inputs: [] }, + { + type: "error", + name: "NoThawingInProgress", + inputs: [ + { name: "buyer", type: "address", internalType: "address" }, + { name: "seller", type: "address", internalType: "address" }, + { name: "asset", type: "address", internalType: "address" }, + ], + }, + { type: "error", name: "NoVouchersProvided", inputs: [] }, + { + type: "error", + name: "NonceAlreadyUsed", + inputs: [{ name: "nonce", type: "bytes32", internalType: "bytes32" }], + }, + { type: "error", name: "ReentrancyGuardReentrantCall", inputs: [] }, + { + type: "error", + name: "SafeERC20FailedOperation", + inputs: [{ name: "token", type: "address", internalType: "address" }], + }, + { + type: "error", + name: "StringTooLong", + inputs: [{ name: "str", type: "string", internalType: "string" }], + }, + { + type: "error", + name: "ThawingPeriodNotCompleted", + inputs: [ + { name: "currentTime", type: "uint256", internalType: "uint256" }, + { name: "thawEndTime", type: "uint256", internalType: "uint256" }, + ], + }, + { + type: "error", + name: "VoucherExpired", + inputs: [ + { name: "voucherId", type: "bytes32", internalType: "bytes32" }, + { name: "currentTime", type: "uint256", internalType: "uint256" }, + { name: "expiry", type: "uint256", internalType: "uint256" }, + ], + }, +] as const; diff --git a/typescript/packages/x402/src/types/shared/evm/eip3009.ts b/typescript/packages/x402/src/types/shared/evm/eip3009.ts deleted file mode 100644 index 25deca66ca..0000000000 --- a/typescript/packages/x402/src/types/shared/evm/eip3009.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const authorizationTypes = { - TransferWithAuthorization: [ - { name: "from", type: "address" }, - { name: "to", type: "address" }, - { name: "value", type: "uint256" }, - { name: "validAfter", type: "uint256" }, - { name: "validBefore", type: "uint256" }, - { name: "nonce", type: "bytes32" }, - ], -}; - -export const authorizationPrimaryType = "TransferWithAuthorization"; diff --git a/typescript/packages/x402/src/types/shared/evm/index.ts b/typescript/packages/x402/src/types/shared/evm/index.ts index 8a2e7c1c5f..232a37a039 100644 --- a/typescript/packages/x402/src/types/shared/evm/index.ts +++ b/typescript/packages/x402/src/types/shared/evm/index.ts @@ -1,4 +1,4 @@ export * from "./config"; -export * from "./eip3009"; +export * from "./typedData"; export * from "./erc20PermitABI"; export * from "./wallet"; diff --git a/typescript/packages/x402/src/types/shared/evm/typedData.ts b/typescript/packages/x402/src/types/shared/evm/typedData.ts new file mode 100644 index 0000000000..7b596860dd --- /dev/null +++ b/typescript/packages/x402/src/types/shared/evm/typedData.ts @@ -0,0 +1,56 @@ +export const typedDataTypes = { + TransferWithAuthorization: [ + { name: "from", type: "address" }, + { name: "to", type: "address" }, + { name: "value", type: "uint256" }, + { name: "validAfter", type: "uint256" }, + { name: "validBefore", type: "uint256" }, + { name: "nonce", type: "bytes32" }, + ], + Voucher: [ + { name: "id", type: "bytes32" }, + { name: "buyer", type: "address" }, + { name: "seller", type: "address" }, + { name: "valueAggregate", type: "uint256" }, + { name: "asset", type: "address" }, + { name: "timestamp", type: "uint64" }, + { name: "nonce", type: "uint256" }, + { name: "escrow", type: "address" }, + { name: "chainId", type: "uint256" }, + { name: "expiry", type: "uint64" }, + ], + Permit: [ + { name: "owner", type: "address" }, + { name: "spender", type: "address" }, + { name: "value", type: "uint256" }, + { name: "nonce", type: "uint256" }, + { name: "deadline", type: "uint256" }, + ], + DepositAuthorization: [ + { name: "buyer", type: "address" }, + { name: "seller", type: "address" }, + { name: "asset", type: "address" }, + { name: "amount", type: "uint256" }, + { name: "nonce", type: "bytes32" }, + { name: "expiry", type: "uint64" }, + ], + FlushAuthorization: [ + { name: "buyer", type: "address" }, + { name: "seller", type: "address" }, + { name: "asset", type: "address" }, + { name: "nonce", type: "bytes32" }, + { name: "expiry", type: "uint64" }, + ], + FlushAllAuthorization: [ + { name: "buyer", type: "address" }, + { name: "nonce", type: "bytes32" }, + { name: "expiry", type: "uint64" }, + ], +}; + +export const transferWithAuthorizationPrimaryType = "TransferWithAuthorization" as const; +export const deferredVoucherPrimaryType = "Voucher" as const; +export const permitPrimaryType = "Permit" as const; +export const depositAuthorizationPrimaryType = "DepositAuthorization" as const; +export const flushAuthorizationPrimaryType = "FlushAuthorization" as const; +export const flushAllAuthorizationPrimaryType = "FlushAllAuthorization" as const; diff --git a/typescript/packages/x402/src/types/verify/constants.ts b/typescript/packages/x402/src/types/verify/constants.ts new file mode 100644 index 0000000000..a1a912fc80 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/constants.ts @@ -0,0 +1,8 @@ +// Constants +export const EvmMaxAtomicUnits = 18; +export const EvmAddressRegex = /^0x[0-9a-fA-F]{40}$/; +export const SvmAddressRegex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/; +export const MixedAddressRegex = /^0x[a-fA-F0-9]{40}|[A-Za-z0-9][A-Za-z0-9-]{0,34}[A-Za-z0-9]$/; +export const HexEncoded64ByteRegex = /^0x[0-9a-fA-F]{64}$/; +export const EvmSignatureRegex = /^0x[0-9a-fA-F]+$/; // Flexible hex signature validation +export const EvmTransactionHashRegex = /^0x[0-9a-fA-F]{64}$/; diff --git a/typescript/packages/x402/src/types/verify/index.ts b/typescript/packages/x402/src/types/verify/index.ts index f92fc12bab..0c8e0d52fd 100644 --- a/typescript/packages/x402/src/types/verify/index.ts +++ b/typescript/packages/x402/src/types/verify/index.ts @@ -1,2 +1,7 @@ export * from "./x402Specs"; +export * from "./schemes"; export * from "./facilitator"; +export * from "./constants"; +export * from "./refiners"; +export * from "./schemes"; +export * from "./versions"; diff --git a/typescript/packages/x402/src/types/verify/refiners.ts b/typescript/packages/x402/src/types/verify/refiners.ts new file mode 100644 index 0000000000..a53ad294b4 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/refiners.ts @@ -0,0 +1,4 @@ +// Refiners +export const isInteger = (value: string) => Number.isInteger(Number(value)) && Number(value) >= 0; +export const isBigInt = (value: string) => BigInt(value) >= 0n; +export const hasMaxLength = (maxLength: number) => (value: string) => value.length <= maxLength; diff --git a/typescript/packages/x402/src/types/verify/schemes/base.ts b/typescript/packages/x402/src/types/verify/schemes/base.ts new file mode 100644 index 0000000000..c9ddff8b42 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/schemes/base.ts @@ -0,0 +1,32 @@ +import { z } from "zod"; +import { x402Versions } from "../versions"; +import { NetworkSchema } from "../../shared/network"; +import { isInteger } from "../refiners"; +import { EvmAddressRegex, MixedAddressRegex } from "../constants"; +import { SvmAddressRegex } from "../../shared/svm"; + +export const EvmOrSvmAddress = z + .string() + .regex(EvmAddressRegex) + .or(z.string().regex(SvmAddressRegex)); +export const MixedAddressOrSvmAddress = z + .string() + .regex(MixedAddressRegex) + .or(z.string().regex(SvmAddressRegex)); + +export const BasePaymentPayloadSchema = z.object({ + x402Version: z.number().refine(val => x402Versions.includes(val as 1)), + network: NetworkSchema, +}); + +export const BasePaymentRequirementsSchema = z.object({ + network: NetworkSchema, + maxAmountRequired: z.string().refine(isInteger), + resource: z.string().url(), + description: z.string(), + mimeType: z.string(), + outputSchema: z.record(z.any()).optional(), + payTo: EvmOrSvmAddress, + maxTimeoutSeconds: z.number().int(), + asset: MixedAddressOrSvmAddress, +}); diff --git a/typescript/packages/x402/src/types/verify/schemes/deferred.ts b/typescript/packages/x402/src/types/verify/schemes/deferred.ts new file mode 100644 index 0000000000..89aea39bc3 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/schemes/deferred.ts @@ -0,0 +1,345 @@ +import { z } from "zod"; +import { + EvmAddressRegex, + EvmSignatureRegex, + HexEncoded64ByteRegex, + EvmMaxAtomicUnits, + EvmTransactionHashRegex, +} from "../constants"; +import { hasMaxLength, isBigInt, isInteger } from "../refiners"; +import { BasePaymentPayloadSchema, BasePaymentRequirementsSchema } from "./base"; +import { VoucherStore } from "../../../schemes/deferred/evm/store"; +import { NetworkSchema } from "../../shared/network"; + +export const DEFERRRED_SCHEME = "deferred"; + +export const DeferredErrorReasons = [ + "invalid_deferred_evm_payload_scheme", + "invalid_deferred_evm_requirements_scheme", + "invalid_deferred_evm_payload_network_mismatch", + "invalid_deferred_evm_payload_chain_id", + "invalid_deferred_evm_payload_voucher_value", + "invalid_deferred_evm_payload_recipient_mismatch", + "invalid_deferred_evm_payload_asset_mismatch", + "invalid_deferred_evm_payload_signature", + "invalid_deferred_evm_payload_no_longer_valid", + "invalid_deferred_evm_payload_voucher_expired", + "invalid_deferred_evm_payload_timestamp_too_early", + "invalid_deferred_evm_payload_voucher_non_zero_nonce", + "invalid_deferred_evm_payload_voucher_id_mismatch", + "invalid_deferred_evm_payload_voucher_buyer_mismatch", + "invalid_deferred_evm_payload_voucher_seller_mismatch", + "invalid_deferred_evm_payload_voucher_asset_mismatch", + "invalid_deferred_evm_payload_voucher_escrow_mismatch", + "invalid_deferred_evm_payload_voucher_chain_id_mismatch", + "invalid_deferred_evm_payload_voucher_nonce_mismatch", + "invalid_deferred_evm_payload_voucher_value_aggregate_decreasing", + "invalid_deferred_evm_payload_voucher_timestamp_decreasing", + "invalid_deferred_evm_payload_voucher_expiry_decreasing", + "invalid_deferred_evm_payload_voucher_zero_value_aggregate", + "invalid_deferred_evm_payload_voucher_not_duplicate", + "invalid_deferred_evm_payload_voucher_could_not_settle_store", + "invalid_deferred_evm_payload_voucher_error_settling_store", + "invalid_deferred_evm_payload_voucher_not_found", + "invalid_deferred_evm_payload_voucher_found_not_duplicate", + "invalid_deferred_evm_payload_permit_signature", + "invalid_deferred_evm_payload_deposit_authorization_signature", + "invalid_deferred_evm_payload_permit_continuity", + "invalid_deferred_evm_payload_deposit_authorization_continuity", + "invalid_deferred_evm_payload_permit_nonce_invalid", + "invalid_deferred_evm_payload_deposit_authorization_nonce_invalid", + "invalid_deferred_evm_payload_deposit_authorization_failed", + "invalid_deferred_evm_payload_deposit_authorization_buyer_mismatch", + "invalid_deferred_evm_payload_deposit_authorization_insufficient_allowance", + "invalid_deferred_evm_payload_flush_authorization_signature", + "invalid_deferred_evm_payload_flush_authorization_continuity", + "invalid_deferred_evm_payload_flush_authorization_failed", + "invalid_deferred_evm_contract_call_failed_verification_data", +] as const; + +// x402DeferredEvmPayloadVoucher +export const DeferredEvmPayloadVoucherSchema = z.object({ + id: z.string().regex(HexEncoded64ByteRegex), + buyer: z.string().regex(EvmAddressRegex), + seller: z.string().regex(EvmAddressRegex), + valueAggregate: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + asset: z.string().regex(EvmAddressRegex), + timestamp: z.number().int().nonnegative(), + nonce: z.number().int().nonnegative(), + escrow: z.string().regex(EvmAddressRegex), + chainId: z.number().int().nonnegative(), + expiry: z.number().int().nonnegative(), +}); +export type DeferredEvmPayloadVoucher = z.infer; + +// x402DeferredEvmPayloadSignedVoucher +export const DeferredEvmPayloadSignedVoucherSchema = DeferredEvmPayloadVoucherSchema.extend({ + signature: z.string().regex(EvmSignatureRegex), +}); +export type DeferredEvmPayloadSignedVoucher = z.infer; + +// x402DeferredVoucherCollection +export const DeferredVoucherCollectionSchema = z.object({ + voucherId: z.string().regex(HexEncoded64ByteRegex), + voucherNonce: z.number().int().nonnegative(), + transactionHash: z.string().regex(EvmTransactionHashRegex), + collectedAmount: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + asset: z.string().regex(EvmAddressRegex), + chainId: z.number().int().nonnegative(), + collectedAt: z.number().int().nonnegative(), +}); +export type DeferredVoucherCollection = z.infer; + +// x402DeferredEscrowDepositAuthorizationPermit +export const DeferredEscrowDepositAuthorizationPermitSchema = z.object({ + owner: z.string().regex(EvmAddressRegex), + spender: z.string().regex(EvmAddressRegex), + value: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + nonce: z.string().refine(isBigInt), + deadline: z.number().int().nonnegative(), + domain: z.object({ + name: z.string(), + version: z.string(), + }), +}); +export type DeferredEscrowDepositAuthorizationPermit = z.infer< + typeof DeferredEscrowDepositAuthorizationPermitSchema +>; + +// x402DeferredEscrowDepositAuthorizationSignedPermit +export const DeferredEscrowDepositAuthorizationSignedPermitSchema = + DeferredEscrowDepositAuthorizationPermitSchema.extend({ + signature: z.string().regex(EvmSignatureRegex), + }); +export type DeferredEscrowDepositAuthorizationSignedPermit = z.infer< + typeof DeferredEscrowDepositAuthorizationSignedPermitSchema +>; + +// x402DeferredEscrowDepositAuthorizationInner +export const DeferredEscrowDepositAuthorizationInnerSchema = z.object({ + buyer: z.string().regex(EvmAddressRegex), + seller: z.string().regex(EvmAddressRegex), + asset: z.string().regex(EvmAddressRegex), + amount: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + nonce: z.string().regex(HexEncoded64ByteRegex), + expiry: z.number().int().nonnegative(), +}); +export type DeferredEscrowDepositAuthorizationInner = z.infer< + typeof DeferredEscrowDepositAuthorizationInnerSchema +>; + +// x402DeferredEscrowDepositAuthorizationSignedInner +export const DeferredEscrowDepositAuthorizationSignedInnerSchema = + DeferredEscrowDepositAuthorizationInnerSchema.extend({ + signature: z.string().regex(EvmSignatureRegex), + }); +export type DeferredEscrowDepositAuthorizationSignedInner = z.infer< + typeof DeferredEscrowDepositAuthorizationSignedInnerSchema +>; + +// x402DeferredEscrowDepositAuthorization +export const DeferredEscrowDepositAuthorizationSchema = z.object({ + permit: DeferredEscrowDepositAuthorizationSignedPermitSchema.optional(), + depositAuthorization: DeferredEscrowDepositAuthorizationSignedInnerSchema, +}); +export type DeferredEscrowDepositAuthorization = z.infer< + typeof DeferredEscrowDepositAuthorizationSchema +>; + +// x402DeferredEscrowFlushAuthorization +export const DeferredEscrowFlushAuthorizationSchema = z.object({ + buyer: z.string().regex(EvmAddressRegex), + seller: z.string().regex(EvmAddressRegex).optional(), + asset: z.string().regex(EvmAddressRegex).optional(), + nonce: z.string().regex(HexEncoded64ByteRegex), + expiry: z.number().int().nonnegative(), +}); +export type DeferredEscrowFlushAuthorization = z.infer< + typeof DeferredEscrowFlushAuthorizationSchema +>; + +// x402DeferredEscrowFlushAuthorizationSigned +export const DeferredEscrowFlushAuthorizationSignedSchema = + DeferredEscrowFlushAuthorizationSchema.extend({ + signature: z.string().regex(EvmSignatureRegex), + }); +export type DeferredEscrowFlushAuthorizationSigned = z.infer< + typeof DeferredEscrowFlushAuthorizationSignedSchema +>; + +// x402DeferredEscrowDepositAuthorizationConfig +export const DeferredEscrowDepositAuthorizationConfigSchema = z.object({ + asset: z.string().regex(EvmAddressRegex), + assetDomain: z.object({ + name: z.string(), + version: z.string(), + }), + threshold: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + amount: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), +}); +export type DeferredEscrowDepositAuthorizationConfig = z.infer< + typeof DeferredEscrowDepositAuthorizationConfigSchema +>; + +// x402DeferredEvmPayload +export const DeferredEvmPayloadSchema = z.object({ + signature: z.string().regex(EvmSignatureRegex), + voucher: DeferredEvmPayloadVoucherSchema, + depositAuthorization: DeferredEscrowDepositAuthorizationSchema.optional(), +}); +export type DeferredEvmPayload = z.infer; + +// x402DeferredPaymentPayload +export const DeferredPaymentPayloadSchema = BasePaymentPayloadSchema.extend({ + scheme: z.literal(DEFERRRED_SCHEME), + payload: DeferredEvmPayloadSchema, +}); +export type DeferredPaymentPayload = z.infer; + +// x402UnsignedDeferredPaymentPayload +export const UnsignedDeferredPaymentPayloadSchema = BasePaymentPayloadSchema.extend({ + scheme: z.literal(DEFERRRED_SCHEME), + payload: DeferredEvmPayloadSchema.omit({ signature: true }).extend({ + signature: z.undefined(), + }), +}); +export type UnsignedDeferredPaymentPayload = z.infer; + +// x402DeferredEvmPaymentRequirementsExtraAccountBalance +export const DeferredEvmPaymentRequirementsExtraAccountDetailsSchema = z.object({ + balance: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + assetAllowance: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + assetPermitNonce: z.string().refine(isBigInt), + facilitator: z.string(), +}); +export type DeferredEvmPaymentRequirementsExtraAccountDetails = z.infer< + typeof DeferredEvmPaymentRequirementsExtraAccountDetailsSchema +>; + +// x402DeferredEvmPaymentRequirementsExtraNewVoucher +export const DeferredEvmPaymentRequirementsExtraNewVoucherSchema = z.object({ + type: z.literal("new"), + account: DeferredEvmPaymentRequirementsExtraAccountDetailsSchema.optional(), + voucher: DeferredEvmPayloadVoucherSchema.pick({ id: true, escrow: true }), +}); +export type DeferredEvmPaymentRequirementsExtraNewVoucher = z.infer< + typeof DeferredEvmPaymentRequirementsExtraNewVoucherSchema +>; + +// x402DeferredEvmPaymentRequirementsExtraAggregationVoucher +export const DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema = z.object({ + type: z.literal("aggregation"), + account: DeferredEvmPaymentRequirementsExtraAccountDetailsSchema.optional(), + signature: z.string().regex(EvmSignatureRegex), + voucher: DeferredEvmPayloadVoucherSchema, +}); +export type DeferredEvmPaymentRequirementsExtraAggregationVoucher = z.infer< + typeof DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema +>; + +// x402DeferredPaymentRequirements +export const DeferredPaymentRequirementsSchema = BasePaymentRequirementsSchema.extend({ + scheme: z.literal(DEFERRRED_SCHEME), + extra: z.discriminatedUnion("type", [ + DeferredEvmPaymentRequirementsExtraNewVoucherSchema, + DeferredEvmPaymentRequirementsExtraAggregationVoucherSchema, + ]), +}); +export type DeferredPaymentRequirements = z.infer; + +// x402DeferredSchemeContext +export const DeferredSchemeContextSchema = z.object({ + voucherStore: z.instanceof(VoucherStore), +}); +export type DeferredSchemeContext = z.infer; + +// x402DeferredErrorResponse +export const DeferredErrorResponseSchema = z.object({ + error: z.string(), + details: z.any().optional(), +}); +export type DeferredErrorResponse = z.infer; + +// x402DeferredVoucherResponse +export const DeferredVoucherResponseSchema = z.union([ + DeferredEvmPayloadSignedVoucherSchema, + DeferredErrorResponseSchema, +]); +export type DeferredVoucherResponse = z.infer; + +// x402DeferredVouchersResponse +export const DeferredVouchersResponseSchema = z.union([ + z.object({ + data: z.array(DeferredEvmPayloadSignedVoucherSchema), + count: z.number(), + pagination: z.object({ + limit: z.number(), + offset: z.number(), + }), + }), + DeferredErrorResponseSchema, +]); +export type DeferredVouchersResponse = z.infer; + +// x402DeferredVoucherCollectionResponse +export const DeferredVoucherCollectionResponseSchema = z.object({ + voucherId: z.string(), + voucherNonce: z.number(), + transactionHash: z.string().regex(EvmTransactionHashRegex), + collectedAmount: z.string(), + asset: z.string(), + chainId: z.number(), + timestamp: z.number(), +}); +export type DeferredVoucherCollectionResponse = z.infer< + typeof DeferredVoucherCollectionResponseSchema +>; + +// x402DeferredVoucherCollectionsResponse +export const DeferredVoucherCollectionsResponseSchema = z.union([ + z.object({ + data: z.array(DeferredVoucherCollectionSchema), + count: z.number(), + pagination: z.object({ + limit: z.number(), + offset: z.number(), + }), + }), + DeferredErrorResponseSchema, +]); +export type DeferredVoucherCollectionsResponse = z.infer< + typeof DeferredVoucherCollectionsResponseSchema +>; + +// x402DeferredDepositWithAuthorizationResponse +export const DeferredDepositWithAuthorizationResponseSchema = z.object({ + success: z.boolean(), + errorReason: z.string().optional(), + payer: z.string().regex(EvmAddressRegex).optional(), + transaction: z.string().regex(EvmTransactionHashRegex), + network: NetworkSchema.optional(), +}); +export type DeferredDepositWithAuthorizationResponse = z.infer< + typeof DeferredDepositWithAuthorizationResponseSchema +>; + +// x402DeferredFlushWithAuthorizationResponse +export const DeferredFlushWithAuthorizationResponseSchema = z.object({ + success: z.boolean(), + errorReason: z.string().optional(), + payer: z.string().regex(EvmAddressRegex).optional(), + transaction: z.string().regex(EvmTransactionHashRegex), + network: NetworkSchema.optional(), +}); +export type DeferredFlushWithAuthorizationResponse = z.infer< + typeof DeferredFlushWithAuthorizationResponseSchema +>; + +// x402DeferredAccountDetailsResponse +export const DeferredBuyerDataResponseSchema = z.object({ + balance: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + assetAllowance: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + assetPermitNonce: z.string().refine(isBigInt), + voucher: DeferredEvmPayloadSignedVoucherSchema.optional(), +}); +export type DeferredBuyerDataResponse = z.infer; diff --git a/typescript/packages/x402/src/types/verify/schemes/exact.ts b/typescript/packages/x402/src/types/verify/schemes/exact.ts new file mode 100644 index 0000000000..308e651c66 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/schemes/exact.ts @@ -0,0 +1,100 @@ +import { z } from "zod"; +import { + EvmAddressRegex, + EvmSignatureRegex, + HexEncoded64ByteRegex, + EvmMaxAtomicUnits, + SvmAddressRegex, +} from "../constants"; +import { hasMaxLength, isInteger } from "../refiners"; +import { Base64EncodedRegex } from "../../../shared"; +import { BasePaymentPayloadSchema, BasePaymentRequirementsSchema } from "./base"; + +export const EXACT_SCHEME = "exact"; + +export const ExactErrorReasons = [ + "invalid_exact_evm_payload_authorization_valid_after", + "invalid_exact_evm_payload_authorization_valid_before", + "invalid_exact_evm_payload_authorization_value", + "invalid_exact_evm_payload_signature", + "invalid_exact_evm_payload_recipient_mismatch", + "invalid_exact_evm_payload_from_mismatch", + "invalid_exact_evm_payload_to_mismatch", + "invalid_exact_evm_payload_value_mismatch", + "invalid_exact_evm_payload_valid_after_mismatch", + "invalid_exact_evm_payload_valid_before_mismatch", + "invalid_exact_evm_payload_nonce_mismatch", + "invalid_exact_svm_payload_transaction", + "invalid_exact_svm_payload_transaction_amount_mismatch", + "invalid_exact_svm_payload_transaction_create_ata_instruction", + "invalid_exact_svm_payload_transaction_create_ata_instruction_incorrect_payee", + "invalid_exact_svm_payload_transaction_create_ata_instruction_incorrect_asset", + "invalid_exact_svm_payload_transaction_instructions", + "invalid_exact_svm_payload_transaction_instructions_length", + "invalid_exact_svm_payload_transaction_instructions_compute_limit_instruction", + "invalid_exact_svm_payload_transaction_instructions_compute_price_instruction", + "invalid_exact_svm_payload_transaction_instructions_compute_price_instruction_too_high", + "invalid_exact_svm_payload_transaction_instruction_not_spl_token_transfer_checked", + "invalid_exact_svm_payload_transaction_instruction_not_token_2022_transfer_checked", + "invalid_exact_svm_payload_transaction_not_a_transfer_instruction", + "invalid_exact_svm_payload_transaction_cannot_derive_receiver_ata", + "invalid_exact_svm_payload_transaction_receiver_ata_not_found", + "invalid_exact_svm_payload_transaction_sender_ata_not_found", + "invalid_exact_svm_payload_transaction_simulation_failed", + "invalid_exact_svm_payload_transaction_transfer_to_incorrect_ata", + "settle_exact_svm_block_height_exceeded", + "settle_exact_svm_transaction_confirmation_timed_out", +] as const; + +// x402ExactEvmPayloadAuthorization +export const ExactEvmPayloadAuthorizationSchema = z.object({ + from: z.string().regex(EvmAddressRegex), + to: z.string().regex(EvmAddressRegex), + value: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), + validAfter: z.string().refine(isInteger), + validBefore: z.string().refine(isInteger), + nonce: z.string().regex(HexEncoded64ByteRegex), +}); +export type ExactEvmPayloadAuthorization = z.infer; + +// x402ExactEvmPayload +export const ExactEvmPayloadSchema = z.object({ + signature: z.string().regex(EvmSignatureRegex), + authorization: ExactEvmPayloadAuthorizationSchema, +}); +export type ExactEvmPayload = z.infer; + +// x402ExactSvmPayload +export const ExactSvmPayloadSchema = z.object({ + transaction: z.string().regex(Base64EncodedRegex), +}); +export type ExactSvmPayload = z.infer; + +// x402ExactPaymentPayload +export const ExactPaymentPayloadSchema = BasePaymentPayloadSchema.extend({ + scheme: z.literal(EXACT_SCHEME), + payload: z.union([ExactEvmPayloadSchema, ExactSvmPayloadSchema]), +}); +export type ExactPaymentPayload = z.infer; + +// x402UnsignedPaymentPayload +export const UnsignedExactPaymentPayloadSchema = BasePaymentPayloadSchema.extend({ + scheme: z.literal(EXACT_SCHEME), + payload: ExactEvmPayloadSchema.omit({ signature: true }).extend({ + signature: z.undefined(), + }), +}); +export type UnsignedExactPaymentPayload = z.infer; + +// x402ExactPaymentRequirements +export const ExactPaymentRequirementsSchema = BasePaymentRequirementsSchema.extend({ + scheme: z.literal(EXACT_SCHEME), + extra: z + .object({ + name: z.string().optional(), + version: z.string().optional(), + feePayer: z.string().regex(SvmAddressRegex).optional(), + }) + .optional(), +}); +export type ExactPaymentRequirements = z.infer; diff --git a/typescript/packages/x402/src/types/verify/schemes/index.ts b/typescript/packages/x402/src/types/verify/schemes/index.ts new file mode 100644 index 0000000000..5de123cfb7 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/schemes/index.ts @@ -0,0 +1,3 @@ +export * from "./base"; +export * from "./deferred"; +export * from "./exact"; diff --git a/typescript/packages/x402/src/types/verify/versions.ts b/typescript/packages/x402/src/types/verify/versions.ts new file mode 100644 index 0000000000..dca25fddd1 --- /dev/null +++ b/typescript/packages/x402/src/types/verify/versions.ts @@ -0,0 +1 @@ +export const x402Versions = [1] as const; diff --git a/typescript/packages/x402/src/types/verify/x402Specs.ts b/typescript/packages/x402/src/types/verify/x402Specs.ts index 0e666e95a4..3cfe939fc6 100644 --- a/typescript/packages/x402/src/types/verify/x402Specs.ts +++ b/typescript/packages/x402/src/types/verify/x402Specs.ts @@ -1,42 +1,32 @@ import { z } from "zod"; import { NetworkSchema } from "../shared"; -import { SvmAddressRegex } from "../shared/svm"; -import { Base64EncodedRegex } from "../../shared/base64"; - -// Constants -const EvmMaxAtomicUnits = 18; -const EvmAddressRegex = /^0x[0-9a-fA-F]{40}$/; -const MixedAddressRegex = /^0x[a-fA-F0-9]{40}|[A-Za-z0-9][A-Za-z0-9-]{0,34}[A-Za-z0-9]$/; -const HexEncoded64ByteRegex = /^0x[0-9a-fA-F]{64}$/; -const EvmSignatureRegex = /^0x[0-9a-fA-F]+$/; // Flexible hex signature validation +import { MixedAddressRegex } from "./constants"; +import { + EXACT_SCHEME, + ExactErrorReasons, + ExactPaymentPayloadSchema, + ExactPaymentRequirementsSchema, + UnsignedExactPaymentPayloadSchema, +} from "./schemes/exact"; +import { + DEFERRRED_SCHEME, + DeferredErrorReasons, + DeferredPaymentPayloadSchema, + DeferredPaymentRequirementsSchema, + DeferredSchemeContextSchema, + UnsignedDeferredPaymentPayloadSchema, +} from "./schemes/deferred"; +import { x402Versions } from "./versions"; +import { EvmOrSvmAddress } from "./schemes/base"; + // Enums -export const schemes = ["exact"] as const; -export const x402Versions = [1] as const; +export const X402_SCHEMES = [EXACT_SCHEME, DEFERRRED_SCHEME] as const; +export type X402_SCHEMES = (typeof X402_SCHEMES)[number]; export const ErrorReasons = [ "insufficient_funds", - "invalid_exact_evm_payload_authorization_valid_after", - "invalid_exact_evm_payload_authorization_valid_before", - "invalid_exact_evm_payload_authorization_value", - "invalid_exact_evm_payload_signature", - "invalid_exact_evm_payload_recipient_mismatch", - "invalid_exact_svm_payload_transaction", - "invalid_exact_svm_payload_transaction_amount_mismatch", - "invalid_exact_svm_payload_transaction_create_ata_instruction", - "invalid_exact_svm_payload_transaction_create_ata_instruction_incorrect_payee", - "invalid_exact_svm_payload_transaction_create_ata_instruction_incorrect_asset", - "invalid_exact_svm_payload_transaction_instructions", - "invalid_exact_svm_payload_transaction_instructions_length", - "invalid_exact_svm_payload_transaction_instructions_compute_limit_instruction", - "invalid_exact_svm_payload_transaction_instructions_compute_price_instruction", - "invalid_exact_svm_payload_transaction_instructions_compute_price_instruction_too_high", - "invalid_exact_svm_payload_transaction_instruction_not_spl_token_transfer_checked", - "invalid_exact_svm_payload_transaction_instruction_not_token_2022_transfer_checked", - "invalid_exact_svm_payload_transaction_not_a_transfer_instruction", - "invalid_exact_svm_payload_transaction_receiver_ata_not_found", - "invalid_exact_svm_payload_transaction_sender_ata_not_found", - "invalid_exact_svm_payload_transaction_simulation_failed", - "invalid_exact_svm_payload_transaction_transfer_to_incorrect_ata", "invalid_network", + "invalid_network_unsupported", + "invalid_client_network", "invalid_payload", "invalid_payment_requirements", "invalid_scheme", @@ -45,74 +35,35 @@ export const ErrorReasons = [ "unsupported_scheme", "invalid_x402_version", "invalid_transaction_state", - "invalid_x402_version", - "settle_exact_svm_block_height_exceeded", - "settle_exact_svm_transaction_confirmation_timed_out", - "unsupported_scheme", - "unexpected_settle_error", + "invalid_transaction_reverted", "unexpected_verify_error", + "unexpected_settle_error", + "missing_scheme_context", + ...ExactErrorReasons, + ...DeferredErrorReasons, ] as const; -// Refiners -const isInteger: (value: string) => boolean = value => - Number.isInteger(Number(value)) && Number(value) >= 0; -const hasMaxLength = (maxLength: number) => (value: string) => value.length <= maxLength; - // x402PaymentRequirements -const EvmOrSvmAddress = z.string().regex(EvmAddressRegex).or(z.string().regex(SvmAddressRegex)); -const mixedAddressOrSvmAddress = z - .string() - .regex(MixedAddressRegex) - .or(z.string().regex(SvmAddressRegex)); -export const PaymentRequirementsSchema = z.object({ - scheme: z.enum(schemes), - network: NetworkSchema, - maxAmountRequired: z.string().refine(isInteger), - resource: z.string().url(), - description: z.string(), - mimeType: z.string(), - outputSchema: z.record(z.any()).optional(), - payTo: EvmOrSvmAddress, - maxTimeoutSeconds: z.number().int(), - asset: mixedAddressOrSvmAddress, - extra: z.record(z.any()).optional(), -}); +export const PaymentRequirementsSchema = z.discriminatedUnion("scheme", [ + ExactPaymentRequirementsSchema, + DeferredPaymentRequirementsSchema, +]); export type PaymentRequirements = z.infer; - -// x402ExactEvmPayload -export const ExactEvmPayloadAuthorizationSchema = z.object({ - from: z.string().regex(EvmAddressRegex), - to: z.string().regex(EvmAddressRegex), - value: z.string().refine(isInteger).refine(hasMaxLength(EvmMaxAtomicUnits)), - validAfter: z.string().refine(isInteger), - validBefore: z.string().refine(isInteger), - nonce: z.string().regex(HexEncoded64ByteRegex), -}); -export type ExactEvmPayloadAuthorization = z.infer; - -export const ExactEvmPayloadSchema = z.object({ - signature: z.string().regex(EvmSignatureRegex), - authorization: ExactEvmPayloadAuthorizationSchema, -}); -export type ExactEvmPayload = z.infer; - -// x402ExactSvmPayload -export const ExactSvmPayloadSchema = z.object({ - transaction: z.string().regex(Base64EncodedRegex), -}); -export type ExactSvmPayload = z.infer; +export type PaymentRequirementsExtra = z.infer["extra"]; // x402PaymentPayload -export const PaymentPayloadSchema = z.object({ - x402Version: z.number().refine(val => x402Versions.includes(val as 1)), - scheme: z.enum(schemes), - network: NetworkSchema, - payload: z.union([ExactEvmPayloadSchema, ExactSvmPayloadSchema]), -}); +export const PaymentPayloadSchema = z.discriminatedUnion("scheme", [ + ExactPaymentPayloadSchema, + DeferredPaymentPayloadSchema, +]); export type PaymentPayload = z.infer; -export type UnsignedPaymentPayload = Omit & { - payload: Omit & { signature: undefined }; -}; + +// x402UnsignedPaymentPayload +export const UnsignedPaymentPayloadSchema = z.discriminatedUnion("scheme", [ + UnsignedExactPaymentPayloadSchema, + UnsignedDeferredPaymentPayloadSchema, +]); +export type UnsignedPaymentPayload = z.infer; // x402 Resource Server Response export const x402ResponseSchema = z.object({ @@ -186,6 +137,12 @@ export const VerifyRequestSchema = z.object({ paymentRequirements: PaymentRequirementsSchema, }); export type VerifyRequest = z.infer; +// x402Request +export const X402RequestSchema = z.object({ + paymentRequirements: PaymentRequirementsSchema, + paymentPayload: PaymentPayloadSchema, +}); +export type X402Request = z.infer; // x402VerifyResponse export const VerifyResponseSchema = z.object({ @@ -201,7 +158,7 @@ export const SettleResponseSchema = z.object({ errorReason: z.enum(ErrorReasons).optional(), payer: EvmOrSvmAddress.optional(), transaction: z.string().regex(MixedAddressRegex), - network: NetworkSchema, + network: NetworkSchema.optional(), }); export type SettleResponse = z.infer; @@ -228,7 +185,7 @@ export type ListDiscoveryResourcesResponse = z.infer x402Versions.includes(val as 1)), - scheme: z.enum(schemes), + scheme: z.enum(X402_SCHEMES), network: NetworkSchema, extra: z.record(z.any()).optional(), }); @@ -239,3 +196,9 @@ export const SupportedPaymentKindsResponseSchema = z.object({ kinds: z.array(SupportedPaymentKindSchema), }); export type SupportedPaymentKindsResponse = z.infer; + +// x402SchemeContext +export const SchemeContextSchema = z.object({ + deferred: DeferredSchemeContextSchema.optional(), +}); +export type SchemeContext = z.infer; diff --git a/typescript/packages/x402/src/verify/useDeferred.test.ts b/typescript/packages/x402/src/verify/useDeferred.test.ts new file mode 100644 index 0000000000..7c5eddc6eb --- /dev/null +++ b/typescript/packages/x402/src/verify/useDeferred.test.ts @@ -0,0 +1,1125 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from "vitest"; +import { useDeferredFacilitator } from "./useDeferred"; +import { DEFAULT_FACILITATOR_URL } from "./useFacilitator"; +import { + DeferredEvmPayloadSignedVoucher, + DeferredErrorResponse, + DeferredVoucherResponse, + DeferredVouchersResponse, + PaymentRequirements, + PaymentPayload, +} from "../types"; + +// Mock fetch globally +const mockFetch = vi.fn(); +global.fetch = mockFetch; + +describe("useDeferredFacilitator", () => { + const customFacilitatorUrl = "https://custom.facilitator.com/api"; + + const buyerAddress = "0xf33332f96E5EA32c90a5301b646Bf5e93EA1D892"; + const sellerAddress = "0x1234567890123456789012345678901234567890"; + const escrowAddress = "0xffffff12345678901234567890123456789fffff"; + const assetAddress = "0x1111111111111111111111111111111111111111"; + const voucherId = "0x7a3e9b10e8a59f9b4e87219b7e5f3e69ac1b7e4625b5de38b1ff8d470ab7f4f1"; + const voucherSignature = + "0x899b52ba76bebfc79405b67d9004ed769a998b34a6be8695c265f32fee56b1a903f563f2abe1e02cc022e332e2cef2c146fb057567316966303480afdd88aff11c"; + + const mockSignedVoucher: DeferredEvmPayloadSignedVoucher = { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: voucherSignature, + }; + + beforeEach(() => { + vi.clearAllMocks(); + mockFetch.mockReset(); + }); + + afterEach(() => { + vi.resetAllMocks(); + }); + + describe("useDeferredFacilitator initialization and configuration", () => { + it("should instantiate a valid facilitator", () => { + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + expect(facilitator).toHaveProperty("getVoucher"); + expect(facilitator).toHaveProperty("getVoucherSeries"); + expect(facilitator).toHaveProperty("getVouchers"); + expect(facilitator).toHaveProperty("getAvailableVoucher"); + expect(facilitator).toHaveProperty("storeVoucher"); + expect(facilitator).toHaveProperty("verifyVoucher"); + expect(facilitator).toHaveProperty("settleVoucher"); + expect(facilitator).toHaveProperty("getVoucherCollections"); + expect(facilitator).toHaveProperty("flushEscrow"); + }); + + it("should use default facilitator URL when no config provided", async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockSignedVoucher), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + await facilitator.getVoucher(voucherId, 0); + + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}/0`, + ); + }); + + it("should use custom facilitator URL when config provided", async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockSignedVoucher), + }); + + const facilitator = useDeferredFacilitator({ url: customFacilitatorUrl }); + await facilitator.getVoucher(voucherId, 0); + + expect(mockFetch).toHaveBeenCalledWith( + `${customFacilitatorUrl}/deferred/vouchers/${voucherId}/0`, + ); + }); + }); + + describe("getVoucher", () => { + it("should fetch voucher successfully", async () => { + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockSignedVoucher), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucher(voucherId, 0); + + expect(result).toEqual(mockSignedVoucher); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}/0`, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Voucher not found", + }; + + mockFetch.mockResolvedValueOnce({ + status: 404, + statusText: "Not Found", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucher(voucherId, 0)).rejects.toThrow("Voucher not found"); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Internal server error", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucher(voucherId, 0)).rejects.toThrow("Internal server error"); + }); + + it("should use fallback error message when no error field", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucher(voucherId, 0)).rejects.toThrow( + "Failed to fetch voucher history: Internal Server Error", + ); + }); + }); + + describe("getVoucherSeries", () => { + it("should fetch voucher series without pagination", async () => { + const mockResponse: DeferredVouchersResponse = { + data: [mockSignedVoucher], + count: 1, + pagination: { limit: 10, offset: 0 }, + }; + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherSeries(voucherId, {}); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}`, + ); + }); + + it("should fetch voucher series with pagination", async () => { + const mockResponse: DeferredVouchersResponse = { + data: [mockSignedVoucher], + count: 1, + pagination: { limit: 10, offset: 5 }, + }; + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherSeries(voucherId, { limit: 10, offset: 5 }); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}?limit=10&offset=5`, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Series not found", + }; + + mockFetch.mockResolvedValueOnce({ + status: 404, + statusText: "Not Found", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherSeries(voucherId, {})).rejects.toThrow("Series not found"); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Internal server error", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherSeries(voucherId, {})).rejects.toThrow( + "Internal server error", + ); + }); + + it("should use fallback error message when no error field", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherSeries(voucherId, {})).rejects.toThrow( + "Failed to fetch voucher history: Internal Server Error", + ); + }); + }); + + describe("getVouchers", () => { + it("should fetch vouchers with basic query", async () => { + const mockResponse: DeferredVouchersResponse = { + data: [mockSignedVoucher], + count: 1, + pagination: { limit: 10, offset: 0 }, + }; + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVouchers( + { buyer: buyerAddress, seller: sellerAddress }, + {}, + ); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers?buyer=${buyerAddress}&seller=${sellerAddress}`, + ); + }); + + it("should fetch vouchers with pagination and filters", async () => { + const mockResponse: DeferredVouchersResponse = { + data: [mockSignedVoucher], + count: 1, + pagination: { limit: 50, offset: 10 }, + }; + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVouchers( + { buyer: buyerAddress, seller: sellerAddress, latest: true }, + { limit: 50, offset: 10 }, + ); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers?limit=50&offset=10&latest=true&buyer=${buyerAddress}&seller=${sellerAddress}`, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Query failed", + }; + + mockFetch.mockResolvedValueOnce({ + status: 400, + statusText: "Bad Request", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.getVouchers({ buyer: buyerAddress, seller: sellerAddress }, {}), + ).rejects.toThrow("Query failed"); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Internal server error", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.getVouchers({ buyer: buyerAddress, seller: sellerAddress }, {}), + ).rejects.toThrow("Internal server error"); + }); + + it("should use fallback error message when no error field", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.getVouchers({ buyer: buyerAddress, seller: sellerAddress }, {}), + ).rejects.toThrow("Failed to fetch voucher history: Internal Server Error"); + }); + }); + + describe("getAvailableVoucher", () => { + it("should fetch available voucher successfully", async () => { + const mockResponse: DeferredVoucherResponse = mockSignedVoucher; + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getAvailableVoucher(buyerAddress, sellerAddress); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/available/${buyerAddress}/${sellerAddress}`, + ); + }); + + it("should handle 404 status gracefully", async () => { + mockFetch.mockResolvedValueOnce({ + status: 404, + json: () => Promise.resolve({ error: "voucher_not_found" }), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getAvailableVoucher(buyerAddress, sellerAddress); + + expect(result).toEqual({ error: "voucher_not_found" }); + }); + + it("should throw error for other non-200 status codes", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Server error", + }; + + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getAvailableVoucher(buyerAddress, sellerAddress)).rejects.toThrow( + "Server error", + ); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Voucher validation failed", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getAvailableVoucher(buyerAddress, sellerAddress)).rejects.toThrow( + "Voucher validation failed", + ); + }); + + it("should use fallback error message for non-200 status", async () => { + mockFetch.mockResolvedValueOnce({ + status: 403, + statusText: "Forbidden", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getAvailableVoucher(buyerAddress, sellerAddress)).rejects.toThrow( + "Failed to fetch available voucher: Forbidden", + ); + }); + }); + + describe("verifyVoucher", () => { + it("should verify voucher successfully", async () => { + const mockResponse = { isValid: true, payer: buyerAddress }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.verifyVoucher(voucherId, 0); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}/0/verify`, + { + method: "POST", + }, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Verification failed", + }; + + mockFetch.mockResolvedValueOnce({ + status: 400, + statusText: "Bad Request", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.verifyVoucher(voucherId, 0)).rejects.toThrow( + "Failed to verify voucher: Bad Request", + ); + }); + + it("should use fallback error message", async () => { + mockFetch.mockResolvedValueOnce({ + status: 404, + statusText: "Not Found", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.verifyVoucher(voucherId, 0)).rejects.toThrow( + "Failed to verify voucher: Not Found", + ); + }); + }); + + describe("settleVoucher", () => { + it("should settle voucher successfully", async () => { + const mockResponse = { + success: true, + transaction: "0x1234567890abcdef", + payer: buyerAddress, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.settleVoucher(voucherId, 0); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/${voucherId}/0/settle`, + { + method: "POST", + }, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Settlement failed", + }; + + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.settleVoucher(voucherId, 0)).rejects.toThrow( + "Failed to settle voucher: Internal Server Error", + ); + }); + + it("should use fallback error message", async () => { + mockFetch.mockResolvedValueOnce({ + status: 503, + statusText: "Service Unavailable", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.settleVoucher(voucherId, 0)).rejects.toThrow( + "Failed to settle voucher: Service Unavailable", + ); + }); + }); + + describe("storeVoucher", () => { + const mockPaymentPayload = { + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: voucherSignature, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "1000000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }, + }, + } as PaymentPayload; + + const mockPaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "1000000", + resource: "https://example.com/resource", + description: "Test payment", + mimeType: "application/json", + payTo: sellerAddress, + maxTimeoutSeconds: 300, + asset: assetAddress, + extra: { + type: "new", + voucher: { + id: voucherId, + escrow: escrowAddress, + }, + }, + } as PaymentRequirements; + + it("should verify and store voucher successfully", async () => { + const mockResponse: DeferredVoucherResponse = mockSignedVoucher; + + mockFetch.mockResolvedValueOnce({ + status: 201, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith(`${DEFAULT_FACILITATOR_URL}/deferred/vouchers`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: mockPaymentRequirements, + }), + }); + }); + + it("should throw error for non-201 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Verification failed", + }; + + mockFetch.mockResolvedValueOnce({ + status: 400, + statusText: "Bad Request", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements), + ).rejects.toThrow("Verification failed"); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Invalid voucher signature", + }; + + mockFetch.mockResolvedValueOnce({ + status: 201, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements), + ).rejects.toThrow("Invalid voucher signature"); + }); + + it("should throw error when response contains invalidReason field", async () => { + const mockVerifyResponse = { + isValid: false, + invalidReason: "invalid_deferred_evm_payload_signature", + payer: buyerAddress, + }; + + mockFetch.mockResolvedValueOnce({ + status: 201, + json: () => Promise.resolve(mockVerifyResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements), + ).rejects.toThrow("invalid_deferred_evm_payload_signature"); + }); + + it("should use fallback error message when no specific error provided", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements), + ).rejects.toThrow("Failed to verify and store voucher: Internal Server Error"); + }); + + it("should handle complex payment requirements with aggregation", async () => { + const aggregationRequirements = { + ...mockPaymentRequirements, + extra: { + type: "aggregation", + signature: voucherSignature, + voucher: { + id: voucherId, + buyer: buyerAddress, + seller: sellerAddress, + valueAggregate: "500000", + asset: assetAddress, + timestamp: 1715769600, + nonce: 0, + escrow: escrowAddress, + chainId: 84532, + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + }, + }, + } as PaymentRequirements; + + const mockResponse: DeferredVoucherResponse = { + ...mockSignedVoucher, + valueAggregate: "1500000", + nonce: 1, + }; + + mockFetch.mockResolvedValueOnce({ + status: 201, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.storeVoucher(mockPaymentPayload, aggregationRequirements); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith(`${DEFAULT_FACILITATOR_URL}/deferred/vouchers`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: aggregationRequirements, + }), + }); + }); + + it("should use custom facilitator URL", async () => { + const mockResponse: DeferredVoucherResponse = mockSignedVoucher; + + mockFetch.mockResolvedValueOnce({ + status: 201, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: customFacilitatorUrl }); + const result = await facilitator.storeVoucher(mockPaymentPayload, mockPaymentRequirements); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${customFacilitatorUrl}/deferred/vouchers`, + expect.any(Object), + ); + }); + }); + + describe("getVoucherCollections", () => { + it("should fetch voucher collections without query or pagination", async () => { + const mockResponse = { + data: [ + { + id: voucherId, + nonce: 0, + transaction: "0xabcdef1234567890", + timestamp: 1715769600, + }, + ], + count: 1, + pagination: { limit: 10, offset: 0 }, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherCollections({}, {}); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/collections`, + ); + }); + + it("should fetch voucher collections with id and nonce query", async () => { + const mockResponse = { + data: [ + { + id: voucherId, + nonce: 2, + transaction: "0xabcdef1234567890", + timestamp: 1715769600, + }, + ], + count: 1, + pagination: { limit: 10, offset: 0 }, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherCollections({ id: voucherId, nonce: 2 }, {}); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/collections?id=${voucherId}&nonce=2`, + ); + }); + + it("should fetch voucher collections with pagination", async () => { + const mockResponse = { + data: [ + { + id: voucherId, + nonce: 0, + transaction: "0xabcdef1234567890", + timestamp: 1715769600, + }, + ], + count: 1, + pagination: { limit: 20, offset: 10 }, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherCollections({}, { limit: 20, offset: 10 }); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/collections?limit=20&offset=10`, + ); + }); + + it("should fetch voucher collections with all parameters", async () => { + const mockResponse = { + data: [ + { + id: voucherId, + nonce: 3, + transaction: "0xabcdef1234567890", + timestamp: 1715769600, + }, + ], + count: 1, + pagination: { limit: 50, offset: 25 }, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getVoucherCollections( + { id: voucherId, nonce: 3 }, + { limit: 50, offset: 25 }, + ); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/vouchers/collections?limit=50&offset=25&id=${voucherId}&nonce=3`, + ); + }); + + it("should throw error for non-200 status", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Collections not found", + }; + + mockFetch.mockResolvedValueOnce({ + status: 404, + statusText: "Not Found", + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherCollections({}, {})).rejects.toThrow( + "Collections not found", + ); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse: DeferredErrorResponse = { + error: "Database error", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherCollections({}, {})).rejects.toThrow("Database error"); + }); + + it("should use fallback error message when no error field", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect(facilitator.getVoucherCollections({}, {})).rejects.toThrow( + "Failed to fetch voucher collections: Internal Server Error", + ); + }); + + it("should use custom facilitator URL", async () => { + const mockResponse = { + data: [], + count: 0, + pagination: { limit: 10, offset: 0 }, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: customFacilitatorUrl }); + const result = await facilitator.getVoucherCollections({}, {}); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${customFacilitatorUrl}/deferred/vouchers/collections`, + ); + }); + }); + + describe("getBuyerData", () => { + it("should fetch buyer data successfully with query parameters", async () => { + const mockResponse = { + balance: "10000000", + assetAllowance: "5000000", + assetPermitNonce: "3", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.getBuyerData( + buyerAddress, + sellerAddress, + assetAddress, + escrowAddress, + 84532, + ); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/buyers/${buyerAddress}?seller=${sellerAddress}&asset=${assetAddress}&escrow=${escrowAddress}&chainId=84532`, + { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }, + ); + }); + + it("should throw error when response contains error field", async () => { + const mockErrorResponse = { + error: "Buyer data not found", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockErrorResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.getBuyerData(buyerAddress, sellerAddress, assetAddress, escrowAddress, 84532), + ).rejects.toThrow("Buyer data not found"); + }); + + it("should use custom facilitator URL", async () => { + const mockResponse = { + balance: "10000000", + assetAllowance: "5000000", + assetPermitNonce: "3", + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: customFacilitatorUrl }); + const result = await facilitator.getBuyerData( + buyerAddress, + sellerAddress, + assetAddress, + escrowAddress, + 84532, + ); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${customFacilitatorUrl}/deferred/buyers/${buyerAddress}?seller=${sellerAddress}&asset=${assetAddress}&escrow=${escrowAddress}&chainId=84532`, + expect.objectContaining({ + method: "GET", + }), + ); + }); + }); + + describe("flushEscrow", () => { + const mockFlushAuthorization = { + buyer: buyerAddress, + seller: sellerAddress, + asset: assetAddress, + nonce: "0x0000000000000000000000000000000000000000000000000000000000000000", + expiry: 1715769600 + 1000 * 60 * 60 * 24 * 30, + signature: + "0xbfdc3d0ae7663255972fdf5ce6dfc7556a5ac1da6768e4f4a942a2fa885737db5ddcb7385de4f4b6d483b97beb6a6103b46971f63905a063deb7b0cfc33473411b", + }; + + it("should flush escrow successfully", async () => { + const mockResponse = { + success: true, + transaction: "0xabcdef1234567890", + payer: buyerAddress, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + const result = await facilitator.flushEscrow(mockFlushAuthorization, escrowAddress, 84532); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${DEFAULT_FACILITATOR_URL}/deferred/buyers/${buyerAddress}/flush`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + flushAuthorization: mockFlushAuthorization, + escrow: escrowAddress, + chainId: 84532, + }), + }, + ); + }); + + it("should throw error for non-200 status", async () => { + mockFetch.mockResolvedValueOnce({ + status: 400, + statusText: "Bad Request", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.flushEscrow(mockFlushAuthorization, escrowAddress, 84532), + ).rejects.toThrow("Failed to flush escrow: Bad Request"); + }); + + it("should use fallback error message", async () => { + mockFetch.mockResolvedValueOnce({ + status: 500, + statusText: "Internal Server Error", + json: () => Promise.resolve({}), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.flushEscrow(mockFlushAuthorization, escrowAddress, 84532), + ).rejects.toThrow("Failed to flush escrow: Internal Server Error"); + }); + + it("should handle failed flush response", async () => { + const mockResponse = { + success: false, + errorReason: "invalid_signature", + transaction: "", + payer: buyerAddress, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: DEFAULT_FACILITATOR_URL }); + + await expect( + facilitator.flushEscrow(mockFlushAuthorization, escrowAddress, 84532), + ).rejects.toThrow("invalid_signature"); + }); + + it("should use custom facilitator URL", async () => { + const mockResponse = { + success: true, + transaction: "0xabcdef1234567890", + payer: buyerAddress, + }; + + mockFetch.mockResolvedValueOnce({ + status: 200, + json: () => Promise.resolve(mockResponse), + }); + + const facilitator = useDeferredFacilitator({ url: customFacilitatorUrl }); + const result = await facilitator.flushEscrow(mockFlushAuthorization, escrowAddress, 84532); + + expect(result).toEqual(mockResponse); + expect(mockFetch).toHaveBeenCalledWith( + `${customFacilitatorUrl}/deferred/buyers/${buyerAddress}/flush`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + flushAuthorization: mockFlushAuthorization, + escrow: escrowAddress, + chainId: 84532, + }), + }, + ); + }); + }); +}); diff --git a/typescript/packages/x402/src/verify/useDeferred.ts b/typescript/packages/x402/src/verify/useDeferred.ts new file mode 100644 index 0000000000..f1aed4055f --- /dev/null +++ b/typescript/packages/x402/src/verify/useDeferred.ts @@ -0,0 +1,375 @@ +import { toJsonSafe } from "../shared/json"; +import { + DeferredBuyerDataResponse, + DeferredErrorResponse, + DeferredEscrowFlushAuthorizationSigned, + DeferredFlushWithAuthorizationResponse, + DeferredVoucherCollectionsResponse, + DeferredVoucherResponse, + DeferredVouchersResponse, + FacilitatorConfig, + PaymentPayload, + PaymentRequirements, + SettleResponse, + VerifyResponse, +} from "../types"; + +/** + * Creates a facilitator client for interacting with the X402 payment deferred facilitator service + * + * @param facilitator - The facilitator config to use. + * @returns An object containing functions for interacting with a deferred facilitator + */ +export function useDeferredFacilitator(facilitator: FacilitatorConfig) { + /** + * Fetches a voucher by its id and nonce. + * + * @param id - The id of the voucher to fetch + * @param nonce - The nonce of the voucher to fetch + * @returns The voucher + */ + async function getVoucher(id: string, nonce: number): Promise { + const response = await fetch(`${facilitator.url}/deferred/vouchers/${id}/${nonce}`); + const responseJson = (await response.json()) as DeferredVoucherResponse; + + if (response.status !== 200 || "error" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + `Failed to fetch voucher history: ${response.statusText}`; + throw new Error(errorMessage); + } + return responseJson; + } + + /** + * Fetches a voucher series by its id, sorted by nonce in descending order. + * + * @param id - The id of the voucher to fetch + * @param pagination - The pagination parameters + * @param pagination.limit - The maximum number of vouchers to return + * @param pagination.offset - The offset to start from + * @returns The vouchers + */ + async function getVoucherSeries( + id: string, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise { + const { limit, offset } = pagination; + + const params = new URLSearchParams(); + if (limit !== undefined) params.append("limit", limit.toString()); + if (offset !== undefined) params.append("offset", offset.toString()); + const queryString = params.toString(); + + const response = await fetch( + `${facilitator.url}/deferred/vouchers/${id}${queryString ? `?${queryString}` : ""}`, + ); + const responseJson = (await response.json()) as DeferredVouchersResponse; + + if (response.status !== 200 || "error" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + `Failed to fetch voucher history: ${response.statusText}`; + throw new Error(errorMessage); + } + return responseJson; + } + + /** + * Fetches vouchers for a given buyer and seller + * + * @param query - The query parameters + * @param query.buyer - The buyer address + * @param query.seller - The seller address + * @param query.latest - Whether to return the latest voucher for each id + * @param pagination - The pagination parameters + * @param pagination.limit - The maximum number of vouchers to return + * @param pagination.offset - The offset to start from + * @returns The vouchers + */ + async function getVouchers( + query: { + buyer: string; + seller: string; + latest?: boolean; + }, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise { + const { buyer, seller, latest } = query; + const { limit, offset } = pagination; + + const params = new URLSearchParams(); + if (limit !== undefined) params.append("limit", limit.toString()); + if (offset !== undefined) params.append("offset", offset.toString()); + if (latest !== undefined) params.append("latest", latest.toString()); + if (buyer !== undefined) params.append("buyer", buyer); + if (seller !== undefined) params.append("seller", seller); + const queryString = params.toString(); + + const response = await fetch( + `${facilitator.url}/deferred/vouchers${queryString ? `?${queryString}` : ""}`, + ); + const responseJson = (await response.json()) as DeferredVouchersResponse; + + if (response.status !== 200 || "error" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + `Failed to fetch voucher history: ${response.statusText}`; + throw new Error(errorMessage); + } + return responseJson; + } + + /** + * Fetches the latest available voucher for a given buyer and seller + * + * @param buyer - The buyer address + * @param seller - The seller address + * @returns The voucher + */ + async function getAvailableVoucher( + buyer: string, + seller: string, + ): Promise { + const response = await fetch( + `${facilitator.url}/deferred/vouchers/available/${buyer}/${seller}`, + ); + const responseJson = (await response.json()) as DeferredVoucherResponse; + + // If the voucher is not found we don't throw, clients should just create a new voucher from scratch + if (response.status === 404) { + return { + error: "voucher_not_found", + }; + } + + if (response.status !== 200 || "error" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + `Failed to fetch available voucher: ${response.statusText}`; + throw new Error(errorMessage); + } + + return responseJson; + } + + /** + * Stores a voucher in the facilitator. Before storing, it verifies the payload and payment + * requirements, equivalent to calling POST /verify + * + * @param payload - The payment payload + * @param paymentRequirements - The payment requirements + * @returns The voucher response + */ + async function storeVoucher( + payload: PaymentPayload, + paymentRequirements: PaymentRequirements, + ): Promise { + const response = await fetch(`${facilitator.url}/deferred/vouchers`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + x402Version: payload.x402Version, + paymentPayload: toJsonSafe(payload), + paymentRequirements: toJsonSafe(paymentRequirements), + }), + }); + + const responseJson = (await response.json()) as VerifyResponse | DeferredVoucherResponse; + + if (response.status !== 201 || "error" in responseJson || "invalidReason" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + (responseJson as VerifyResponse).invalidReason || + `Failed to verify and store voucher: ${response.statusText}`; + throw new Error(errorMessage); + } + + return responseJson as VerifyResponse | DeferredVoucherResponse; + } + + /** + * Verifies a voucher signature and onchain state. + * + * @param id - The id of the voucher to verify + * @param nonce - The nonce of the voucher to verify + * @returns The verification result + */ + async function verifyVoucher(id: string, nonce: number): Promise { + const response = await fetch(`${facilitator.url}/deferred/vouchers/${id}/${nonce}/verify`, { + method: "POST", + }); + const responseJson = (await response.json()) as VerifyResponse; + + if (response.status !== 200 || "invalidReason" in responseJson) { + const errorMessage = + (responseJson as VerifyResponse).invalidReason || + `Failed to verify voucher: ${response.statusText}`; + throw new Error(errorMessage); + } + + return responseJson; + } + + /** + * Settles a voucher by its id and nonce. + * + * @param id - The id of the voucher to settle + * @param nonce - The nonce of the voucher to settle + * @returns The settlement result + */ + async function settleVoucher(id: string, nonce: number): Promise { + const response = await fetch(`${facilitator.url}/deferred/vouchers/${id}/${nonce}/settle`, { + method: "POST", + }); + const responseJson = (await response.json()) as SettleResponse; + + if (response.status !== 200 || "errorReason" in responseJson) { + const errorMessage = `Failed to settle voucher: ${response.statusText}`; + throw new Error(errorMessage); + } + + return responseJson; + } + + /** + * Fetches the latest voucher for a given buyer and seller + * + * @param query - The query parameters + * @param query.id - The id of the voucher + * @param query.nonce - The nonce of the voucher + * @param pagination - The pagination parameters + * @param pagination.limit - The maximum number of vouchers to return + * @param pagination.offset - The offset to start from + * @returns The vouchers + */ + async function getVoucherCollections( + query: { + id?: string; + nonce?: number; + }, + pagination: { + limit?: number; + offset?: number; + }, + ): Promise { + const { id, nonce } = query; + const { limit, offset } = pagination; + + const params = new URLSearchParams(); + if (limit !== undefined) params.append("limit", limit.toString()); + if (offset !== undefined) params.append("offset", offset.toString()); + if (id !== undefined) params.append("id", id); + if (nonce !== undefined) params.append("nonce", nonce.toString()); + const queryString = params.toString(); + + const response = await fetch( + `${facilitator.url}/deferred/vouchers/collections${queryString ? `?${queryString}` : ""}`, + ); + const responseJson = (await response.json()) as DeferredVoucherCollectionsResponse; + + if (response.status !== 200 || "error" in responseJson) { + const errorMessage = + (responseJson as DeferredErrorResponse).error || + `Failed to fetch voucher collections: ${response.statusText}`; + throw new Error(errorMessage); + } + return responseJson; + } + + /** + * Fetches the details of an escrow account for a given buyer, seller, and asset + * + * @param buyer - The buyer address + * @param seller - The seller address + * @param asset - The asset address + * @param escrow - The escrow address + * @param chainId - The chain ID + * @returns The balance of the escrow account + */ + async function getBuyerData( + buyer: string, + seller: string, + asset: string, + escrow: string, + chainId: number, + ): Promise { + const params = new URLSearchParams(); + params.append("seller", seller); + params.append("asset", asset); + params.append("escrow", escrow); + params.append("chainId", chainId.toString()); + const queryString = params.toString(); + + const response = await fetch(`${facilitator.url}/deferred/buyers/${buyer}?${queryString}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + const responseJson = (await response.json()) as + | DeferredBuyerDataResponse + | DeferredErrorResponse; + if ("error" in responseJson) { + throw new Error(responseJson.error); + } + return responseJson; + } + + /** + * Flushes an escrow account using a flush authorization signature + * + * @param flushAuthorization - The signed flush authorization + * @param escrow - The escrow address + * @param chainId - The chain ID + * + * @returns The flush result + */ + async function flushEscrow( + flushAuthorization: DeferredEscrowFlushAuthorizationSigned, + escrow: string, + chainId: number, + ): Promise { + const response = await fetch( + `${facilitator.url}/deferred/buyers/${flushAuthorization.buyer}/flush`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ flushAuthorization, escrow, chainId }), + }, + ); + const responseJson = (await response.json()) as DeferredFlushWithAuthorizationResponse; + + if (response.status !== 200 || "errorReason" in responseJson) { + const errorMessage = + (responseJson as DeferredFlushWithAuthorizationResponse).errorReason || + `Failed to flush escrow: ${response.statusText}`; + throw new Error(errorMessage); + } + + return responseJson; + } + + return { + getVoucher, + getVouchers, + getVoucherSeries, + getAvailableVoucher, + storeVoucher, + verifyVoucher, + settleVoucher, + getVoucherCollections, + getBuyerData, + flushEscrow, + }; +} diff --git a/typescript/packages/x402/src/verify/useFacilitator.test.ts b/typescript/packages/x402/src/verify/useFacilitator.test.ts index d81e5dc526..28ae6f4c50 100644 --- a/typescript/packages/x402/src/verify/useFacilitator.test.ts +++ b/typescript/packages/x402/src/verify/useFacilitator.test.ts @@ -32,6 +32,98 @@ describe("useFacilitator", () => { asset: "0x1234567890123456789012345678901234567890", }; + const mockDeferredPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "20", + resource: "http://localhost:3002/subgraph/1234", + description: "", + mimeType: "", + payTo: "0xC93d37AD45c907eE1b27a02b2E1bd823BA9D379C", + maxTimeoutSeconds: 60, + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + extra: { + type: "new", + voucher: { + id: "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + escrow: "0x1a9ea876cfe472514967d2e5cf326fb49dc68559", + }, + }, + }; + + const mockDeferredPaymentPayload: PaymentPayload = { + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: + "0xa80421aca752ab2e10b7e073f636bb50ccaec54f2813f8c194b45256460b5603340ce9ce75c12d0dabbe64e2011907f4c887064a39c36f633cb232b45dbec4611c", + voucher: { + nonce: 0, + id: "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + escrow: "0x1a9ea876cfe472514967d2e5cf326fb49dc68559", + buyer: "0x80cdF1957EBb7a2DF22dd8913753A4423FF4272E", + seller: "0xC93d37AD45c907eE1b27a02b2E1bd823BA9D379C", + valueAggregate: "20", + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + timestamp: 1756226264, + chainId: 84532, + expiry: 1758818264, + }, + }, + }; + + const mockDeferredAggregatedPaymentRequirements: PaymentRequirements = { + scheme: "deferred", + network: "base-sepolia", + maxAmountRequired: "20", + resource: "http://localhost:3002/subgraph/1234", + description: "", + mimeType: "", + payTo: "0xC93d37AD45c907eE1b27a02b2E1bd823BA9D379C", + maxTimeoutSeconds: 60, + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + extra: { + type: "aggregation", + signature: + "0xa80421aca752ab2e10b7e073f636bb50ccaec54f2813f8c194b45256460b5603340ce9ce75c12d0dabbe64e2011907f4c887064a39c36f633cb232b45dbec4611c", + voucher: { + nonce: 0, + id: "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + escrow: "0x1a9ea876cfe472514967d2e5cf326fb49dc68559", + buyer: "0x80cdF1957EBb7a2DF22dd8913753A4423FF4272E", + seller: "0xC93d37AD45c907eE1b27a02b2E1bd823BA9D379C", + valueAggregate: "20", + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + timestamp: 1756226264, + chainId: 84532, + expiry: 1758818264, + }, + }, + }; + + const mockDeferredAggregatedPaymentPayload: PaymentPayload = { + x402Version: 1, + scheme: "deferred", + network: "base-sepolia", + payload: { + signature: + "0xdf13d8f233a508ed40a85236df422d38edd0ad5ca3cb4e73d86cb712869919e82606bc2c29ace0d2a804b61808f099cf78d83b709ef1ea631b1496149cbfb1ea1c", + voucher: { + nonce: 1, + id: "0x198e73e1cecf59db4fbf8ca10000000000000000000000000000000000000000", + escrow: "0x1a9ea876cfe472514967d2e5cf326fb49dc68559", + buyer: "0x80cdF1957EBb7a2DF22dd8913753A4423FF4272E", + seller: "0xC93d37AD45c907eE1b27a02b2E1bd823BA9D379C", + valueAggregate: "40", + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + timestamp: 1756226267, + chainId: 84532, + expiry: 1758818267, + }, + }, + }; + beforeEach(() => { vi.clearAllMocks(); global.fetch = vi.fn().mockResolvedValue({ @@ -42,132 +134,251 @@ describe("useFacilitator", () => { }); describe("verify", () => { - it("should call fetch with the correct data and default URL", async () => { - const { verify } = useFacilitator(); - await verify(mockPaymentPayload, mockPaymentRequirements); - - expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/verify", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - x402Version: mockPaymentPayload.x402Version, - paymentPayload: mockPaymentPayload, - paymentRequirements: mockPaymentRequirements, - }), + describe("exact scheme", () => { + it("should call fetch with the correct data and default URL", async () => { + const { verify } = useFacilitator(); + await verify(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/verify", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: mockPaymentRequirements, + }), + }); }); - }); - - it("should use custom URL when provided", async () => { - const customUrl = "https://custom-facilitator.org"; - const { verify } = useFacilitator({ url: customUrl }); - await verify(mockPaymentPayload, mockPaymentRequirements); - expect(fetch).toHaveBeenCalledWith(`${customUrl}/verify`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - x402Version: mockPaymentPayload.x402Version, - paymentPayload: mockPaymentPayload, - paymentRequirements: mockPaymentRequirements, - }), + it("should use custom URL when provided", async () => { + const customUrl = "https://custom-facilitator.org"; + const { verify } = useFacilitator({ url: customUrl }); + await verify(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith(`${customUrl}/verify`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: mockPaymentRequirements, + }), + }); }); - }); - it("should include auth headers when createAuthHeaders is provided", async () => { - const mockHeaders = { - verify: { Authorization: "Bearer test-token" }, - settle: { Authorization: "Bearer test-token" }, - }; - const { verify } = useFacilitator({ - url: "https://x402.org/facilitator", - createAuthHeaders: async () => mockHeaders, + it("should include auth headers when createAuthHeaders is provided", async () => { + const mockHeaders = { + verify: { Authorization: "Bearer test-token" }, + settle: { Authorization: "Bearer test-token" }, + supported: { Authorization: "Bearer test-token" }, + }; + const { verify } = useFacilitator({ + url: "https://x402.org/facilitator", + createAuthHeaders: async () => mockHeaders, + }); + await verify(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith( + "https://x402.org/facilitator/verify", + expect.objectContaining({ + headers: { "Content-Type": "application/json", ...mockHeaders.verify }, + }), + ); }); - await verify(mockPaymentPayload, mockPaymentRequirements); - expect(fetch).toHaveBeenCalledWith( - "https://x402.org/facilitator/verify", - expect.objectContaining({ - headers: { "Content-Type": "application/json", ...mockHeaders.verify }, - }), - ); + it("should throw error on non-200 response", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 400, + statusText: "Bad Request", + json: async () => ({}), + }); + const { verify } = useFacilitator(); + + await expect(verify(mockPaymentPayload, mockPaymentRequirements)).rejects.toThrow( + "Failed to verify payment: Bad Request", + ); + }); }); - it("should throw error on non-200 response", async () => { - global.fetch = vi.fn().mockResolvedValue({ - status: 400, - statusText: "Bad Request", - json: async () => ({}), + describe("deferred scheme", () => { + it("should call fetch with the correct data and default URL", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 200, + json: async () => ({ + isValid: true, + payer: mockDeferredPaymentPayload.payload.voucher.buyer, + }), + }); + + const { verify } = useFacilitator(); + await verify(mockDeferredPaymentPayload, mockDeferredPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/verify", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockDeferredPaymentPayload.x402Version, + paymentPayload: mockDeferredPaymentPayload, + paymentRequirements: mockDeferredPaymentRequirements, + }), + }); }); - const { verify } = useFacilitator(); - await expect(verify(mockPaymentPayload, mockPaymentRequirements)).rejects.toThrow( - "Failed to verify payment: Bad Request", - ); + it("should call fetch with the correct data and default URL for aggregated payment", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 200, + json: async () => ({ + isValid: true, + payer: mockDeferredAggregatedPaymentPayload.payload.voucher.buyer, + }), + }); + + const { verify } = useFacilitator(); + const result = await verify( + mockDeferredAggregatedPaymentPayload, + mockDeferredAggregatedPaymentRequirements, + ); + + expect(result).toEqual({ + isValid: true, + payer: mockDeferredAggregatedPaymentPayload.payload.voucher.buyer, + }); + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/verify", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockDeferredAggregatedPaymentPayload.x402Version, + paymentPayload: mockDeferredAggregatedPaymentPayload, + paymentRequirements: mockDeferredAggregatedPaymentRequirements, + }), + }); + }); }); }); describe("settle", () => { - it("should call fetch with the correct data and default URL", async () => { - const { settle } = useFacilitator(); - await settle(mockPaymentPayload, mockPaymentRequirements); - - expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/settle", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - x402Version: mockPaymentPayload.x402Version, - paymentPayload: mockPaymentPayload, - paymentRequirements: mockPaymentRequirements, - }), + describe("exact scheme", () => { + it("should call fetch with the correct data and default URL", async () => { + const { settle } = useFacilitator(); + await settle(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/settle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: mockPaymentRequirements, + }), + }); }); - }); - it("should use custom URL when provided", async () => { - const customUrl = "https://custom-facilitator.org"; - const { settle } = useFacilitator({ url: customUrl }); - await settle(mockPaymentPayload, mockPaymentRequirements); - - expect(fetch).toHaveBeenCalledWith(`${customUrl}/settle`, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ - x402Version: mockPaymentPayload.x402Version, - paymentPayload: mockPaymentPayload, - paymentRequirements: mockPaymentRequirements, - }), + it("should use custom URL when provided", async () => { + const customUrl = "https://custom-facilitator.org"; + const { settle } = useFacilitator({ url: customUrl }); + await settle(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith(`${customUrl}/settle`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockPaymentPayload.x402Version, + paymentPayload: mockPaymentPayload, + paymentRequirements: mockPaymentRequirements, + }), + }); }); - }); - it("should include auth headers when createAuthHeaders is provided", async () => { - const mockHeaders = { - verify: { Authorization: "Bearer test-token" }, - settle: { Authorization: "Bearer test-token" }, - }; - const { settle } = useFacilitator({ - url: "https://x402.org/facilitator", - createAuthHeaders: async () => mockHeaders, + it("should include auth headers when createAuthHeaders is provided", async () => { + const mockHeaders = { + verify: { Authorization: "Bearer test-token" }, + settle: { Authorization: "Bearer test-token" }, + supported: { Authorization: "Bearer test-token" }, + }; + const { settle } = useFacilitator({ + url: "https://x402.org/facilitator", + createAuthHeaders: async () => mockHeaders, + }); + await settle(mockPaymentPayload, mockPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith( + "https://x402.org/facilitator/settle", + expect.objectContaining({ + headers: { "Content-Type": "application/json", ...mockHeaders.settle }, + }), + ); }); - await settle(mockPaymentPayload, mockPaymentRequirements); - expect(fetch).toHaveBeenCalledWith( - "https://x402.org/facilitator/settle", - expect.objectContaining({ - headers: { "Content-Type": "application/json", ...mockHeaders.settle }, - }), - ); + it("should throw error on non-200 response", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 400, + statusText: "Bad Request", + json: async () => ({}), + }); + const { settle } = useFacilitator(); + + await expect(settle(mockPaymentPayload, mockPaymentRequirements)).rejects.toThrow( + "Failed to settle payment: 400 Bad Request", + ); + }); }); - it("should throw error on non-200 response", async () => { - global.fetch = vi.fn().mockResolvedValue({ - status: 400, - statusText: "Bad Request", - json: async () => ({}), + describe("deferred scheme", () => { + it("should call fetch with the correct data and default URL", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 200, + json: async () => ({ + success: true, + transaction: "0x1234567890abcdef", + payer: mockDeferredPaymentPayload.payload.voucher.buyer, + }), + }); + + const { settle } = useFacilitator(); + await settle(mockDeferredPaymentPayload, mockDeferredPaymentRequirements); + + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/settle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockDeferredPaymentPayload.x402Version, + paymentPayload: mockDeferredPaymentPayload, + paymentRequirements: mockDeferredPaymentRequirements, + }), + }); }); - const { settle } = useFacilitator(); - await expect(settle(mockPaymentPayload, mockPaymentRequirements)).rejects.toThrow( - "Failed to settle payment: 400 Bad Request", - ); + it("should call fetch with the correct data and default URL for aggregated payment", async () => { + global.fetch = vi.fn().mockResolvedValue({ + status: 200, + json: async () => ({ + success: true, + transaction: "0xabcdef1234567890", + payer: mockDeferredAggregatedPaymentPayload.payload.voucher.buyer, + }), + }); + + const { settle } = useFacilitator(); + const result = await settle( + mockDeferredAggregatedPaymentPayload, + mockDeferredAggregatedPaymentRequirements, + ); + + expect(result).toEqual({ + success: true, + transaction: "0xabcdef1234567890", + payer: mockDeferredAggregatedPaymentPayload.payload.voucher.buyer, + }); + expect(fetch).toHaveBeenCalledWith("https://x402.org/facilitator/settle", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + x402Version: mockDeferredAggregatedPaymentPayload.x402Version, + paymentPayload: mockDeferredAggregatedPaymentPayload, + paymentRequirements: mockDeferredAggregatedPaymentRequirements, + }), + }); + }); }); }); diff --git a/typescript/packages/x402/src/verify/useFacilitator.ts b/typescript/packages/x402/src/verify/useFacilitator.ts index 518f40947d..6fe66f1edb 100644 --- a/typescript/packages/x402/src/verify/useFacilitator.ts +++ b/typescript/packages/x402/src/verify/useFacilitator.ts @@ -11,14 +11,16 @@ import { SettleResponse, VerifyResponse, } from "../types/verify"; +import { useDeferredFacilitator } from "./useDeferred"; -const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator"; +export const DEFAULT_FACILITATOR_URL = "https://x402.org/facilitator"; export type CreateHeaders = () => Promise<{ verify: Record; settle: Record; supported: Record; list?: Record; + deferred?: Record; }>; /** @@ -169,7 +171,13 @@ export function useFacilitator(facilitator?: FacilitatorConfig) { return data as ListDiscoveryResourcesResponse; } - return { verify, settle, supported, list }; + return { + verify, + settle, + supported, + list, + deferred: useDeferredFacilitator(facilitator || { url: DEFAULT_FACILITATOR_URL }), + }; } -export const { verify, settle, supported, list } = useFacilitator(); +export const { verify, settle, supported, list, deferred } = useFacilitator(); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index 8cfeb3f98c..6df72a4832 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -10,74 +10,74 @@ importers: devDependencies: tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) turbo: specifier: ^2.5.0 - version: 2.5.6 + version: 2.5.4 typescript: specifier: ^5.8.3 - version: 5.9.2 + version: 5.8.3 packages/coinbase-x402: dependencies: '@coinbase/cdp-sdk': specifier: ^1.29.0 - version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: 1.38.2(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) packages/x402: dependencies: @@ -86,457 +86,460 @@ importers: version: 1.2.6 '@solana-program/compute-budget': specifier: ^0.8.0 - version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token': specifier: ^0.5.1 - version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) + version: 0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))) '@solana-program/token-2022': specifier: ^0.4.2 - version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)) + version: 0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/transaction-confirmation': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) wagmi: specifier: ^2.15.6 - version: 2.16.9(@tanstack/query-core@5.85.9)(@tanstack/react-query@5.85.9(react@19.1.1))(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.15.6(@tanstack/query-core@5.81.2)(@tanstack/react-query@5.81.2(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67) zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@coinbase/onchainkit': specifier: ^0.38.14 - version: 0.38.19(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) + version: 0.38.15(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(bufferutil@4.0.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67) '@craftamap/esbuild-plugin-html': specifier: ^0.9.0 - version: 0.9.0(bufferutil@4.0.9)(esbuild@0.25.9)(utf-8-validate@5.0.10) + version: 0.9.0(bufferutil@4.0.9)(esbuild@0.25.5)(utf-8-validate@5.0.10) '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@types/react': specifier: ^19 - version: 19.1.12 + version: 19.1.8 '@types/react-dom': specifier: ^19 - version: 19.1.9(@types/react@19.1.12) + version: 19.1.6(@types/react@19.1.8) '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@wagmi/connectors': specifier: ^5.8.1 - version: 5.9.9(@types/react@19.1.12)(@vercel/functions@2.2.13)(@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 5.8.5(@types/react@19.1.8)(@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67) '@wagmi/core': specifier: ^2.17.1 - version: 2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + version: 2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) buffer: specifier: ^6.0.3 version: 6.0.3 esbuild: specifier: ^0.25.4 - version: 0.25.9 + version: 0.25.5 eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 react: specifier: ^19.0.0 - version: 19.1.1 + version: 19.1.0 react-dom: specifier: ^19.0.0 - version: 19.1.1(react@19.1.1) + version: 19.1.0(react@19.1.0) tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) packages/x402-axios: dependencies: axios: specifier: ^1.7.9 - version: 1.11.0 + version: 1.10.0 viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) + vitest-mock-extended: + specifier: ^3.1.0 + version: 3.1.0(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0)) packages/x402-express: dependencies: '@coinbase/cdp-sdk': specifier: ^1.22.0 - version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: 1.22.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) express: specifier: ^4.18.2 version: 4.21.2 viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/express': specifier: ^5.0.1 version: 5.0.3 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) packages/x402-fetch: dependencies: viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) packages/x402-hono: dependencies: '@coinbase/cdp-sdk': specifier: ^1.22.0 - version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: 1.22.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) hono: specifier: ^4.7.1 - version: 4.9.6 + version: 4.8.2 viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) packages/x402-next: dependencies: '@coinbase/cdp-sdk': specifier: ^1.22.0 - version: 1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: 1.22.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) '@solana/kit': specifier: ^2.1.1 - version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) next: specifier: ^15.0.0 - version: 15.5.2(@babel/core@7.28.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 15.3.4(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) x402: specifier: workspace:^ version: link:../x402 zod: specifier: ^3.24.2 - version: 3.25.76 + version: 3.25.67 devDependencies: '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@types/node': specifier: ^22.13.4 - version: 22.18.0 + version: 22.15.33 '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) prettier: specifier: 3.5.2 version: 3.5.2 tsup: specifier: ^8.4.0 - version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1) + version: 8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0) tsx: specifier: ^4.19.2 - version: 4.20.5 + version: 4.20.3 typescript: specifier: ^5.7.3 - version: 5.9.2 + version: 5.8.3 vite: specifier: ^6.2.6 - version: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + version: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) vite-tsconfig-paths: specifier: ^5.1.4 - version: 5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + version: 5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) vitest: specifier: ^3.0.5 - version: 3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1) + version: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) site: dependencies: '@heroicons/react': specifier: ^2.2.0 - version: 2.2.0(react@19.1.1) + version: 2.2.0(react@19.1.0) '@vercel/functions': specifier: ^2.2.8 version: 2.2.13 next: specifier: ^15.2.4 - version: 15.5.2(@babel/core@7.28.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) + version: 15.3.4(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) react: specifier: ^19.0.0 - version: 19.1.1 + version: 19.1.0 react-dom: specifier: ^19.0.0 - version: 19.1.1(react@19.1.1) + version: 19.1.0(react@19.1.0) viem: specifier: ^2.21.26 - version: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + version: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) wagmi: specifier: ^2.15.6 - version: 2.16.9(@tanstack/query-core@5.85.9)(@tanstack/react-query@5.85.9(react@19.1.1))(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + version: 2.15.6(@tanstack/query-core@5.81.2)(@tanstack/react-query@5.81.2(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67) x402: specifier: workspace:* version: link:../packages/x402 x402-legacy: specifier: npm:x402@0.1.2 - version: x402@0.1.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + version: x402@0.1.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) x402-next: specifier: workspace:* version: link:../packages/x402-next @@ -546,40 +549,40 @@ importers: version: 3.3.1 '@eslint/js': specifier: ^9.24.0 - version: 9.34.0 + version: 9.29.0 '@svgr/webpack': specifier: ^8.1.0 - version: 8.1.0(typescript@5.9.2) + version: 8.1.0(typescript@5.8.3) '@types/node': specifier: ^20 - version: 20.19.12 + version: 20.19.1 '@types/react': specifier: ^19 - version: 19.1.12 + version: 19.1.8 '@types/react-dom': specifier: ^19 - version: 19.1.9(@types/react@19.1.12) + version: 19.1.6(@types/react@19.1.8) '@typescript-eslint/eslint-plugin': specifier: ^8.29.1 - version: 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) '@typescript-eslint/parser': specifier: ^8.29.1 - version: 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint: specifier: ^9.24.0 - version: 9.34.0(jiti@1.21.7) + version: 9.29.0(jiti@1.21.7) eslint-config-next: specifier: 15.1.7 - version: 15.1.7(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + version: 15.1.7(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) eslint-plugin-import: specifier: ^2.31.0 - version: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + version: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-jsdoc: specifier: ^50.6.9 - version: 50.8.0(eslint@9.34.0(jiti@1.21.7)) + version: 50.8.0(eslint@9.29.0(jiti@1.21.7)) eslint-plugin-prettier: specifier: ^5.2.6 - version: 5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2) + version: 5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2) postcss: specifier: ^8 version: 8.5.6 @@ -591,7 +594,7 @@ importers: version: 3.4.17 typescript: specifier: ^5 - version: 5.9.2 + version: 5.8.3 packages: @@ -613,16 +616,16 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.28.0': - resolution: {integrity: sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==} + '@babel/compat-data@7.27.5': + resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} engines: {node: '>=6.9.0'} - '@babel/core@7.28.3': - resolution: {integrity: sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==} + '@babel/core@7.27.4': + resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} engines: {node: '>=6.9.0'} - '@babel/generator@7.28.3': - resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + '@babel/generator@7.27.5': + resolution: {integrity: sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==} engines: {node: '>=6.9.0'} '@babel/helper-annotate-as-pure@7.27.3': @@ -633,8 +636,8 @@ packages: resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} engines: {node: '>=6.9.0'} - '@babel/helper-create-class-features-plugin@7.28.3': - resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==} + '@babel/helper-create-class-features-plugin@7.27.1': + resolution: {integrity: sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -645,15 +648,11 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.5': - resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} + '@babel/helper-define-polyfill-provider@0.6.4': + resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - '@babel/helper-globals@7.28.0': - resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} - engines: {node: '>=6.9.0'} - '@babel/helper-member-expression-to-functions@7.27.1': resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==} engines: {node: '>=6.9.0'} @@ -662,8 +661,8 @@ packages: resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.28.3': - resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + '@babel/helper-module-transforms@7.27.3': + resolution: {integrity: sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -704,16 +703,16 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helper-wrap-function@7.28.3': - resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==} + '@babel/helper-wrap-function@7.27.1': + resolution: {integrity: sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.3': - resolution: {integrity: sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==} + '@babel/helpers@7.27.6': + resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.28.3': - resolution: {integrity: sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==} + '@babel/parser@7.27.5': + resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} engines: {node: '>=6.0.0'} hasBin: true @@ -741,8 +740,8 @@ packages: peerDependencies: '@babel/core': ^7.13.0 - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3': - resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==} + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1': + resolution: {integrity: sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 @@ -789,8 +788,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-async-generator-functions@7.28.0': - resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==} + '@babel/plugin-transform-async-generator-functions@7.27.1': + resolution: {integrity: sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -807,8 +806,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-block-scoping@7.28.0': - resolution: {integrity: sha512-gKKnwjpdx5sER/wl0WN0efUBFzF/56YZO0RJrSYP4CljXnP31ByY7fol89AzomdlLNzI36AvOTmYHsnZTCkq8Q==} + '@babel/plugin-transform-block-scoping@7.27.5': + resolution: {integrity: sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -819,14 +818,14 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-class-static-block@7.28.3': - resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==} + '@babel/plugin-transform-class-static-block@7.27.1': + resolution: {integrity: sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.28.3': - resolution: {integrity: sha512-DoEWC5SuxuARF2KdKmGUq3ghfPMO6ZzR12Dnp5gubwbeWJo4dbNWXJPVlwvh4Zlq6Z7YVvL8VFxeSOJgjsx4Sg==} + '@babel/plugin-transform-classes@7.27.1': + resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -837,8 +836,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.28.0': - resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==} + '@babel/plugin-transform-destructuring@7.27.3': + resolution: {integrity: sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -867,12 +866,6 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-explicit-resource-management@7.28.0': - resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==} - engines: {node: '>=6.9.0'} - peerDependencies: - '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-exponentiation-operator@7.27.1': resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==} engines: {node: '>=6.9.0'} @@ -969,8 +962,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.28.0': - resolution: {integrity: sha512-9VNGikXxzu5eCiQjdE4IZn8sb9q7Xsk5EXLDBKUYg1e/Tve8/05+KJEtcxGxAgCY5t/BpKQM+JEL/yT4tvgiUA==} + '@babel/plugin-transform-object-rest-spread@7.27.3': + resolution: {integrity: sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -993,8 +986,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.27.7': - resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} + '@babel/plugin-transform-parameters@7.27.1': + resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1023,8 +1016,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-react-display-name@7.28.0': - resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==} + '@babel/plugin-transform-react-display-name@7.27.1': + resolution: {integrity: sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1047,8 +1040,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-regenerator@7.28.3': - resolution: {integrity: sha512-K3/M/a4+ESb5LEldjQb+XSrpY0nF+ZBFlTCbSnKaYAMfD8v33O6PMs4uYnOk19HlcsI8WMu3McdFPTiQHF/1/A==} + '@babel/plugin-transform-regenerator@7.27.5': + resolution: {integrity: sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1095,8 +1088,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-typescript@7.28.0': - resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==} + '@babel/plugin-transform-typescript@7.27.1': + resolution: {integrity: sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1125,8 +1118,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/preset-env@7.28.3': - resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==} + '@babel/preset-env@7.27.2': + resolution: {integrity: sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -1148,30 +1141,30 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.3': - resolution: {integrity: sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==} + '@babel/runtime@7.27.6': + resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} '@babel/template@7.27.2': resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.28.3': - resolution: {integrity: sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==} + '@babel/traverse@7.27.4': + resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} engines: {node: '>=6.9.0'} - '@babel/types@7.28.2': - resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + '@babel/types@7.27.6': + resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} engines: {node: '>=6.9.0'} - '@base-org/account@1.1.1': - resolution: {integrity: sha512-IfVJPrDPhHfqXRDb89472hXkpvJuQQR7FDI9isLPHEqSYt/45whIoBxSPgZ0ssTt379VhQo4+87PWI1DoLSfAQ==} + '@coinbase/cdp-sdk@1.22.0': + resolution: {integrity: sha512-B5zOdRC0kJw+CKO2jJ1hVVefiEzXEtVMSdm8iHyjg5qn0CMtbgwBLllPlTMYR3u+idZatYEQAIc6ExVrP6X0Rw==} - '@coinbase/cdp-sdk@1.36.1': - resolution: {integrity: sha512-eL8PfuPMXdEKQ6v/AwlDbTpiLhmnwZPAxzJ2m90qL8oEQuYenALX8JqNKg0LBQ/sPM6c2vx109YvFx49g5vSYQ==} + '@coinbase/cdp-sdk@1.38.2': + resolution: {integrity: sha512-S7+xKeZGGi+zc1PotTeQ5Pvn5a0e4ikT+iMm8pPfPjObNlDEUKLei4cmRHON1wXgql0oDXn30YKTo1LQWxXvDw==} - '@coinbase/onchainkit@0.38.19': - resolution: {integrity: sha512-4uiujoTO5/8/dpWVZoTlBC7z0Y1N5fgBYDR6pKN/r6a8pX83ObUuOSGhSzJ8Xbu8NpPU6TXX+VuzLiwiLg/irg==} + '@coinbase/onchainkit@0.38.15': + resolution: {integrity: sha512-RlZ2I7XlNyuVaWjF7/1WY6LzQTaRH2bTitb1wfwTEfxM8a1matfitpNZ2vAVLjRqDvHvgzegfyW6xxS+dUuCxA==} peerDependencies: react: ^18 || ^19 react-dom: ^18 || ^19 @@ -1179,8 +1172,8 @@ packages: '@coinbase/wallet-sdk@3.9.3': resolution: {integrity: sha512-N/A2DRIf0Y3PHc1XAMvbBUu4zisna6qAdqABMZwBMNEfWrXpAwx16pZGkYCLGE+Rvv1edbcB2LYDRnACNcmCiw==} - '@coinbase/wallet-sdk@4.3.6': - resolution: {integrity: sha512-4q8BNG1ViL4mSAAvPAtpwlOs1gpC+67eQtgIwNvT3xyeyFFd+guwkc8bcX5rTmQhXpqnhzC4f0obACbP9CqMSA==} + '@coinbase/wallet-sdk@4.3.3': + resolution: {integrity: sha512-h8gMLQNvP5TIJVXFOyQZaxbi1Mg5alFR4Z2/PEIngdyXZEoQGcVhzyQGuDa3t9zpllxvqfAaKfzDhsfCo+nhSQ==} '@craftamap/esbuild-plugin-html@0.9.0': resolution: {integrity: sha512-V5LFrcGXQWU1SSzYPwxEFjF8IjeXW0oTKh5c0xyhGuTNoEnjgJRNSX83HnZ5TTAutJfo/MAyWA4Z9/fIwbMhUQ==} @@ -1188,8 +1181,8 @@ packages: peerDependencies: esbuild: '>=0.15.10' - '@csstools/color-helpers@5.1.0': - resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + '@csstools/color-helpers@5.0.2': + resolution: {integrity: sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==} engines: {node: '>=18'} '@csstools/css-calc@2.1.4': @@ -1199,8 +1192,8 @@ packages: '@csstools/css-parser-algorithms': ^3.0.5 '@csstools/css-tokenizer': ^3.0.4 - '@csstools/css-color-parser@3.1.0': - resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + '@csstools/css-color-parser@3.0.10': + resolution: {integrity: sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==} engines: {node: '>=18'} peerDependencies: '@csstools/css-parser-algorithms': ^3.0.5 @@ -1216,183 +1209,177 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} - '@ecies/ciphers@0.2.4': - resolution: {integrity: sha512-t+iX+Wf5nRKyNzk8dviW3Ikb/280+aEJAnw9YXvCp2tYGPSkMki+NRY+8aNLmVFv3eNtMdvViPNOPxS8SZNP+w==} + '@ecies/ciphers@0.2.3': + resolution: {integrity: sha512-tapn6XhOueMwht3E2UzY0ZZjYokdaw9XtL9kEyjhQ/Fb9vL9xTFbOaI+fV0AWvTpYu4BNloC6getKW6NtSg4mA==} engines: {bun: '>=1', deno: '>=2', node: '>=16'} peerDependencies: '@noble/ciphers': ^1.0.0 - '@emnapi/core@1.5.0': - resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + '@emnapi/core@1.4.3': + resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} - '@emnapi/runtime@1.5.0': - resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + '@emnapi/runtime@1.4.3': + resolution: {integrity: sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==} - '@emnapi/wasi-threads@1.1.0': - resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + '@emnapi/wasi-threads@1.0.2': + resolution: {integrity: sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==} '@es-joy/jsdoccomment@0.50.2': resolution: {integrity: sha512-YAdE/IJSpwbOTiaURNCKECdAwqrJuFiZhylmesBcIRawtYKnBR2wxPhoIewMg+Yu+QuYvHfJNReWpoxGBKOChA==} engines: {node: '>=18'} - '@esbuild/aix-ppc64@0.25.9': - resolution: {integrity: sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==} + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.9': - resolution: {integrity: sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==} + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.9': - resolution: {integrity: sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==} + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.9': - resolution: {integrity: sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==} + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.9': - resolution: {integrity: sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==} + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.9': - resolution: {integrity: sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==} + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} engines: {node: '>=18'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.25.9': - resolution: {integrity: sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==} + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.9': - resolution: {integrity: sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==} + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.25.9': - resolution: {integrity: sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==} + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.9': - resolution: {integrity: sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==} + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.9': - resolution: {integrity: sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==} + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.25.9': - resolution: {integrity: sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==} + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.9': - resolution: {integrity: sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==} + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.9': - resolution: {integrity: sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==} + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.25.9': - resolution: {integrity: sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==} + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.25.9': - resolution: {integrity: sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==} + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.9': - resolution: {integrity: sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==} + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.9': - resolution: {integrity: sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==} + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.9': - resolution: {integrity: sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==} + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.9': - resolution: {integrity: sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==} + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.9': - resolution: {integrity: sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==} + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/openharmony-arm64@0.25.9': - resolution: {integrity: sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.25.9': - resolution: {integrity: sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==} + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.25.9': - resolution: {integrity: sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==} + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.9': - resolution: {integrity: sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==} + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.9': - resolution: {integrity: sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==} + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} engines: {node: '>=18'} cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.8.0': - resolution: {integrity: sha512-MJQFqrZgcW0UNYLGOuQpey/oTN59vyWwplvCGZztn1cKz9agZPPYpJB7h2OMmuu7VLqkvEjN8feFZJmxNF9D+Q==} + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 @@ -1401,32 +1388,36 @@ packages: resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} - '@eslint/config-array@0.21.0': - resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==} + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/config-helpers@0.3.1': - resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==} + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/core@0.15.2': - resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==} + '@eslint/core@0.15.0': + resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/eslintrc@3.3.1': resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.34.0': - resolution: {integrity: sha512-EoyvqQnBNsV1CWaEJ559rxXL4c8V92gxirbawSmVUOWXlsRxxQXl6LmCpdUblgxgSkDIqKnhzba2SjRTI/A5Rw==} + '@eslint/js@9.29.0': + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@eslint/object-schema@2.1.6': resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/plugin-kit@0.3.5': - resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==} + '@eslint/plugin-kit@0.3.2': + resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@ethereumjs/common@3.2.0': @@ -1445,33 +1436,24 @@ packages: resolution: {integrity: sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==} engines: {node: '>=14'} - '@farcaster/frame-sdk@0.1.9': - resolution: {integrity: sha512-r5cAKgHn4w8Q1jaCi84uKqItfNRd6h8Lk0YyQaz5kMoEIeJ4C0gXPpyqKPYP2TVDFuvaexg2KvzCO2CQdygWyQ==} - engines: {node: '>=22.11.0'} + '@farcaster/frame-core@0.1.8': + resolution: {integrity: sha512-Y0JATQXa/iaqH3pCp8Dby4iCQbwqw7JJaGtfzUazdr7R+zs3EDP8DYQCkqJu6iGjE9gqgecB+b+DOZpnqMTDNw==} - '@farcaster/miniapp-core@0.3.8': - resolution: {integrity: sha512-LaRG1L3lxHqo5pP/E2CX9hNqusR0C8hX3QTV2+hzmQJz6IGvmSpH6Q9ivlLyDfbdqokiMFo5Y3Z1EX1zBHMEQQ==} + '@farcaster/frame-sdk@0.0.60': + resolution: {integrity: sha512-MHQwdFT1VPe3kS0NvnORBPb/DQXr8qpdSDgIgfrdVCB8byQ5uFELlr3gQMuFYFyLFQVXgbMl75z8O6+hvorqow==} - '@farcaster/miniapp-sdk@0.1.9': - resolution: {integrity: sha512-hn0dlIy0JP2Hx6PgKcn9bjYwyPS/SQgYJ/a0qjzG8ZsDfUdjsMPf3yI/jicBipTml/UUoKcbqXM68fsrsbNMKA==} - - '@farcaster/miniapp-wagmi-connector@1.0.0': - resolution: {integrity: sha512-vMRZbekUUctnAUvBFhNoEsJlujRRdxop94fDy5LrKiRR9ax0wtp8gCvLYO+LpaP2PtGs0HFpRwlHNDJWvBR8bg==} + '@farcaster/frame-wagmi-connector@0.0.53': + resolution: {integrity: sha512-+fonXzzj3KxTeUHbtt7lZUC4v/MhC2Y2KpQw7WcVamiDwedm+jTMqBIdrMrWRej/HSWrBQ1rDIOlsWxnv9CDng==} peerDependencies: - '@farcaster/miniapp-sdk': ^0.1.0 + '@farcaster/frame-sdk': ^0.0.64 '@wagmi/core': ^2.14.1 viem: ^2.21.55 - '@farcaster/quick-auth@0.0.6': - resolution: {integrity: sha512-tiZndhpfDtEhaKlkmS5cVDuS+A/tafqZT3y9I44rC69m3beJok6e8dIH2JhxVy3EvOWTyTBnrmNn6GOOh+qK6A==} + '@farcaster/quick-auth@0.0.5': + resolution: {integrity: sha512-Z8hWz/7c33zlmII2AJHja/Wz0C03mm2o+CEBtBylmiun1wC4FMgx1Fal699VQvBUG1lpcJ662WxuRNxKogktDw==} peerDependencies: typescript: 5.8.3 - '@gemini-wallet/core@0.2.0': - resolution: {integrity: sha512-vv9aozWnKrrPWQ3vIFcWk7yta4hQW1Ie0fsNNPeXnjAxkbXr2hqMagEptLuMxpEP2W3mnRu05VDNKzcvAuuZDw==} - peerDependencies: - viem: '>=2.0.0' - '@graphql-typed-document-node/core@3.2.0': resolution: {integrity: sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==} peerDependencies: @@ -1482,8 +1464,8 @@ packages: peerDependencies: react: '>= 16 || ^19.0.0-rc' - '@hono/node-server@1.19.1': - resolution: {integrity: sha512-h44e5s+ByUriaRIbeS/C74O8v90m0A95luyYQGMF7KEn96KkYMXO7bZAwombzTpjQTU4e0TkU8U1WBIXlwuwtA==} + '@hono/node-server@1.14.4': + resolution: {integrity: sha512-DnxpshhYewr2q9ZN8ez/M5mmc3sucr8CT1sIgIy1bkeUXut9XWDkqHoFHRhWIQgkYnKpVRxunyhK7WzpJeJ6qQ==} engines: {node: '>=18.14.1'} peerDependencies: hono: ^4 @@ -1492,136 +1474,134 @@ packages: resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} - '@humanfs/node@0.16.7': - resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} - '@img/sharp-darwin-arm64@0.34.3': - resolution: {integrity: sha512-ryFMfvxxpQRsgZJqBd4wsttYQbCxsJksrv9Lw/v798JcQ8+w84mBWuXwl+TT0WJ/WrYOLaYpwQXi3sA9nTIaIg==} + '@img/sharp-darwin-arm64@0.34.2': + resolution: {integrity: sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [darwin] - '@img/sharp-darwin-x64@0.34.3': - resolution: {integrity: sha512-yHpJYynROAj12TA6qil58hmPmAwxKKC7reUqtGLzsOHfP7/rniNGTL8tjWX6L3CTV4+5P4ypcS7Pp+7OB+8ihA==} + '@img/sharp-darwin-x64@0.34.2': + resolution: {integrity: sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [darwin] - '@img/sharp-libvips-darwin-arm64@1.2.0': - resolution: {integrity: sha512-sBZmpwmxqwlqG9ueWFXtockhsxefaV6O84BMOrhtg/YqbTaRdqDE7hxraVE3y6gVM4eExmfzW4a8el9ArLeEiQ==} + '@img/sharp-libvips-darwin-arm64@1.1.0': + resolution: {integrity: sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==} cpu: [arm64] os: [darwin] - '@img/sharp-libvips-darwin-x64@1.2.0': - resolution: {integrity: sha512-M64XVuL94OgiNHa5/m2YvEQI5q2cl9d/wk0qFTDVXcYzi43lxuiFTftMR1tOnFQovVXNZJ5TURSDK2pNe9Yzqg==} + '@img/sharp-libvips-darwin-x64@1.1.0': + resolution: {integrity: sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==} cpu: [x64] os: [darwin] - '@img/sharp-libvips-linux-arm64@1.2.0': - resolution: {integrity: sha512-RXwd0CgG+uPRX5YYrkzKyalt2OJYRiJQ8ED/fi1tq9WQW2jsQIn0tqrlR5l5dr/rjqq6AHAxURhj2DVjyQWSOA==} + '@img/sharp-libvips-linux-arm64@1.1.0': + resolution: {integrity: sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linux-arm@1.2.0': - resolution: {integrity: sha512-mWd2uWvDtL/nvIzThLq3fr2nnGfyr/XMXlq8ZJ9WMR6PXijHlC3ksp0IpuhK6bougvQrchUAfzRLnbsen0Cqvw==} + '@img/sharp-libvips-linux-arm@1.1.0': + resolution: {integrity: sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==} cpu: [arm] os: [linux] - '@img/sharp-libvips-linux-ppc64@1.2.0': - resolution: {integrity: sha512-Xod/7KaDDHkYu2phxxfeEPXfVXFKx70EAFZ0qyUdOjCcxbjqyJOEUpDe6RIyaunGxT34Anf9ue/wuWOqBW2WcQ==} + '@img/sharp-libvips-linux-ppc64@1.1.0': + resolution: {integrity: sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==} cpu: [ppc64] os: [linux] - '@img/sharp-libvips-linux-s390x@1.2.0': - resolution: {integrity: sha512-eMKfzDxLGT8mnmPJTNMcjfO33fLiTDsrMlUVcp6b96ETbnJmd4uvZxVJSKPQfS+odwfVaGifhsB07J1LynFehw==} + '@img/sharp-libvips-linux-s390x@1.1.0': + resolution: {integrity: sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==} cpu: [s390x] os: [linux] - '@img/sharp-libvips-linux-x64@1.2.0': - resolution: {integrity: sha512-ZW3FPWIc7K1sH9E3nxIGB3y3dZkpJlMnkk7z5tu1nSkBoCgw2nSRTFHI5pB/3CQaJM0pdzMF3paf9ckKMSE9Tg==} + '@img/sharp-libvips-linux-x64@1.1.0': + resolution: {integrity: sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==} cpu: [x64] os: [linux] - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': - resolution: {integrity: sha512-UG+LqQJbf5VJ8NWJ5Z3tdIe/HXjuIdo4JeVNADXBFuG7z9zjoegpzzGIyV5zQKi4zaJjnAd2+g2nna8TZvuW9Q==} + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': + resolution: {integrity: sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==} cpu: [arm64] os: [linux] - '@img/sharp-libvips-linuxmusl-x64@1.2.0': - resolution: {integrity: sha512-SRYOLR7CXPgNze8akZwjoGBoN1ThNZoqpOgfnOxmWsklTGVfJiGJoC/Lod7aNMGA1jSsKWM1+HRX43OP6p9+6Q==} + '@img/sharp-libvips-linuxmusl-x64@1.1.0': + resolution: {integrity: sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==} cpu: [x64] os: [linux] - '@img/sharp-linux-arm64@0.34.3': - resolution: {integrity: sha512-QdrKe3EvQrqwkDrtuTIjI0bu6YEJHTgEeqdzI3uWJOH6G1O8Nl1iEeVYRGdj1h5I21CqxSvQp1Yv7xeU3ZewbA==} + '@img/sharp-linux-arm64@0.34.2': + resolution: {integrity: sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linux-arm@0.34.3': - resolution: {integrity: sha512-oBK9l+h6KBN0i3dC8rYntLiVfW8D8wH+NPNT3O/WBHeW0OQWCjfWksLUaPidsrDKpJgXp3G3/hkmhptAW0I3+A==} + '@img/sharp-linux-arm@0.34.2': + resolution: {integrity: sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - '@img/sharp-linux-ppc64@0.34.3': - resolution: {integrity: sha512-GLtbLQMCNC5nxuImPR2+RgrviwKwVql28FWZIW1zWruy6zLgA5/x2ZXk3mxj58X/tszVF69KK0Is83V8YgWhLA==} - engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - cpu: [ppc64] - os: [linux] - - '@img/sharp-linux-s390x@0.34.3': - resolution: {integrity: sha512-3gahT+A6c4cdc2edhsLHmIOXMb17ltffJlxR0aC2VPZfwKoTGZec6u5GrFgdR7ciJSsHT27BD3TIuGcuRT0KmQ==} + '@img/sharp-linux-s390x@0.34.2': + resolution: {integrity: sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [s390x] os: [linux] - '@img/sharp-linux-x64@0.34.3': - resolution: {integrity: sha512-8kYso8d806ypnSq3/Ly0QEw90V5ZoHh10yH0HnrzOCr6DKAPI6QVHvwleqMkVQ0m+fc7EH8ah0BB0QPuWY6zJQ==} + '@img/sharp-linux-x64@0.34.2': + resolution: {integrity: sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-linuxmusl-arm64@0.34.3': - resolution: {integrity: sha512-vAjbHDlr4izEiXM1OTggpCcPg9tn4YriK5vAjowJsHwdBIdx0fYRsURkxLG2RLm9gyBq66gwtWI8Gx0/ov+JKQ==} + '@img/sharp-linuxmusl-arm64@0.34.2': + resolution: {integrity: sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - '@img/sharp-linuxmusl-x64@0.34.3': - resolution: {integrity: sha512-gCWUn9547K5bwvOn9l5XGAEjVTTRji4aPTqLzGXHvIr6bIDZKNTA34seMPgM0WmSf+RYBH411VavCejp3PkOeQ==} + '@img/sharp-linuxmusl-x64@0.34.2': + resolution: {integrity: sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - '@img/sharp-wasm32@0.34.3': - resolution: {integrity: sha512-+CyRcpagHMGteySaWos8IbnXcHgfDn7pO2fiC2slJxvNq9gDipYBN42/RagzctVRKgxATmfqOSulgZv5e1RdMg==} + '@img/sharp-wasm32@0.34.2': + resolution: {integrity: sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [wasm32] - '@img/sharp-win32-arm64@0.34.3': - resolution: {integrity: sha512-MjnHPnbqMXNC2UgeLJtX4XqoVHHlZNd+nPt1kRPmj63wURegwBhZlApELdtxM2OIZDRv/DFtLcNhVbd1z8GYXQ==} + '@img/sharp-win32-arm64@0.34.2': + resolution: {integrity: sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [win32] - '@img/sharp-win32-ia32@0.34.3': - resolution: {integrity: sha512-xuCdhH44WxuXgOM714hn4amodJMZl3OEvf0GVTm0BEyMeA2to+8HEdRPShH0SLYptJY1uBw+SCFP9WVQi1Q/cw==} + '@img/sharp-win32-ia32@0.34.2': + resolution: {integrity: sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [ia32] os: [win32] - '@img/sharp-win32-x64@0.34.3': - resolution: {integrity: sha512-OWwz05d++TxzLEv4VnsTz5CmZ6mI6S05sfQGEMrNrQcOEERbX46332IvE7pO/EUiw7jUrrS40z/M7kPyjfl04g==} + '@img/sharp-win32-x64@0.34.2': + resolution: {integrity: sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [win32] @@ -1630,24 +1610,29 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} - '@jridgewell/gen-mapping@0.3.13': - resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.3.8': + resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} + engines: {node: '>=6.0.0'} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/sourcemap-codec@1.5.5': - resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - '@jridgewell/trace-mapping@0.3.30': - resolution: {integrity: sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@lit-labs/ssr-dom-shim@1.4.0': - resolution: {integrity: sha512-ficsEARKnmmW5njugNYKipTm4SFnbik7CXtoencDZzmzo/dQ+2Q0bgkzJuoJP20Aj0F+izzJjOqsnkd6F/o1bw==} + '@lit-labs/ssr-dom-shim@1.3.0': + resolution: {integrity: sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==} - '@lit/reactive-element@2.1.1': - resolution: {integrity: sha512-N+dm5PAYdQ8e6UlywyyrgI2t++wFGXfHx+dSJ1oBrg6FAxUj40jId++EaRm80MKX5JnlH1sBsyZ5h0bcZKemCg==} + '@lit/reactive-element@2.1.0': + resolution: {integrity: sha512-L2qyoZSQClcBmq0qajBVbhYEcG6iK0XfLn66ifLe/RfC0/ihpc+pl0Wdn8bJ8o+hj38cG0fGXRgSS20MuXn7qA==} '@metamask/eth-json-rpc-provider@1.0.1': resolution: {integrity: sha512-whiUMPlAOrVGmX8aKYVPvlKyG4CpQXiNNyt74vE1xb5sPvmx5oA7B/kOi/JdBvhGQq97U1/AVdXEdk2zkP8qyA==} @@ -1680,10 +1665,6 @@ packages: resolution: {integrity: sha512-1ugFO1UoirU2esS3juZanS/Fo8C8XYocCuBpfZI5N7ECtoG+zu0wF+uWZASik6CkO6w9n/Iebt4iI4pT0vptpg==} engines: {node: '>=16.0.0'} - '@metamask/rpc-errors@7.0.2': - resolution: {integrity: sha512-YYYHsVYd46XwY2QZzpGeU4PSdRhHdxnzkB8piWGvJW2xbikZ3R+epAYEL4q/K8bh9JPTucsUdwRFnACor1aOYw==} - engines: {node: ^18.20 || ^20.17 || >=22} - '@metamask/safe-event-emitter@2.0.0': resolution: {integrity: sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==} @@ -1710,10 +1691,6 @@ packages: resolution: {integrity: sha512-fLgJnDOXFmuVlB38rUN5SmU7hAFQcCjrg3Vrxz67KTY7YHFnSNEKvX4avmEBdOI0yTCxZjwMCFEqsC8k2+Wd3g==} engines: {node: '>=16.0.0'} - '@metamask/utils@11.7.0': - resolution: {integrity: sha512-IamqpZF8Lr4WeXJ84fD+Sy+v1Zo05SYuMPHHBrZWpzVbnHAmXQpL4ckn9s5dfA+zylp3WGypaBPb6SBZdOhuNQ==} - engines: {node: ^18.18 || ^20.14 || >=22} - '@metamask/utils@5.0.2': resolution: {integrity: sha512-yfmE79bRQtnMzarnKfX7AEJBwFTxvTyw3nBQlu/5rmGXrjAeAMltoGxO62TFurxrQAFMNa/fEjIHNvungZp0+g==} engines: {node: '>=14.0.0'} @@ -1726,59 +1703,59 @@ packages: resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==} engines: {node: '>=16.0.0'} - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@napi-rs/wasm-runtime@0.2.11': + resolution: {integrity: sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==} - '@next/env@15.5.2': - resolution: {integrity: sha512-Qe06ew4zt12LeO6N7j8/nULSOe3fMXE4dM6xgpBQNvdzyK1sv5y4oAP3bq4LamrvGCZtmRYnW8URFCeX5nFgGg==} + '@next/env@15.3.4': + resolution: {integrity: sha512-ZkdYzBseS6UjYzz6ylVKPOK+//zLWvD6Ta+vpoye8cW11AjiQjGYVibF0xuvT4L0iJfAPfZLFidaEzAOywyOAQ==} '@next/eslint-plugin-next@15.1.7': resolution: {integrity: sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==} - '@next/swc-darwin-arm64@15.5.2': - resolution: {integrity: sha512-8bGt577BXGSd4iqFygmzIfTYizHb0LGWqH+qgIF/2EDxS5JsSdERJKA8WgwDyNBZgTIIA4D8qUtoQHmxIIquoQ==} + '@next/swc-darwin-arm64@15.3.4': + resolution: {integrity: sha512-z0qIYTONmPRbwHWvpyrFXJd5F9YWLCsw3Sjrzj2ZvMYy9NPQMPZ1NjOJh4ojr4oQzcGYwgJKfidzehaNa1BpEg==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@15.5.2': - resolution: {integrity: sha512-2DjnmR6JHK4X+dgTXt5/sOCu/7yPtqpYt8s8hLkHFK3MGkka2snTv3yRMdHvuRtJVkPwCGsvBSwmoQCHatauFQ==} + '@next/swc-darwin-x64@15.3.4': + resolution: {integrity: sha512-Z0FYJM8lritw5Wq+vpHYuCIzIlEMjewG2aRkc3Hi2rcbULknYL/xqfpBL23jQnCSrDUGAo/AEv0Z+s2bff9Zkw==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@15.5.2': - resolution: {integrity: sha512-3j7SWDBS2Wov/L9q0mFJtEvQ5miIqfO4l7d2m9Mo06ddsgUK8gWfHGgbjdFlCp2Ek7MmMQZSxpGFqcC8zGh2AA==} + '@next/swc-linux-arm64-gnu@15.3.4': + resolution: {integrity: sha512-l8ZQOCCg7adwmsnFm8m5q9eIPAHdaB2F3cxhufYtVo84pymwKuWfpYTKcUiFcutJdp9xGHC+F1Uq3xnFU1B/7g==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-arm64-musl@15.5.2': - resolution: {integrity: sha512-s6N8k8dF9YGc5T01UPQ08yxsK6fUow5gG1/axWc1HVVBYQBgOjca4oUZF7s4p+kwhkB1bDSGR8QznWrFZ/Rt5g==} + '@next/swc-linux-arm64-musl@15.3.4': + resolution: {integrity: sha512-wFyZ7X470YJQtpKot4xCY3gpdn8lE9nTlldG07/kJYexCUpX1piX+MBfZdvulo+t1yADFVEuzFfVHfklfEx8kw==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - '@next/swc-linux-x64-gnu@15.5.2': - resolution: {integrity: sha512-o1RV/KOODQh6dM6ZRJGZbc+MOAHww33Vbs5JC9Mp1gDk8cpEO+cYC/l7rweiEalkSm5/1WGa4zY7xrNwObN4+Q==} + '@next/swc-linux-x64-gnu@15.3.4': + resolution: {integrity: sha512-gEbH9rv9o7I12qPyvZNVTyP/PWKqOp8clvnoYZQiX800KkqsaJZuOXkWgMa7ANCCh/oEN2ZQheh3yH8/kWPSEg==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-linux-x64-musl@15.5.2': - resolution: {integrity: sha512-/VUnh7w8RElYZ0IV83nUcP/J4KJ6LLYliiBIri3p3aW2giF+PAVgZb6mk8jbQSB3WlTai8gEmCAr7kptFa1H6g==} + '@next/swc-linux-x64-musl@15.3.4': + resolution: {integrity: sha512-Cf8sr0ufuC/nu/yQ76AnarbSAXcwG/wj+1xFPNbyNo8ltA6kw5d5YqO8kQuwVIxk13SBdtgXrNyom3ZosHAy4A==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - '@next/swc-win32-arm64-msvc@15.5.2': - resolution: {integrity: sha512-sMPyTvRcNKXseNQ/7qRfVRLa0VhR0esmQ29DD6pqvG71+JdVnESJaHPA8t7bc67KD5spP3+DOCNLhqlEI2ZgQg==} + '@next/swc-win32-arm64-msvc@15.3.4': + resolution: {integrity: sha512-ay5+qADDN3rwRbRpEhTOreOn1OyJIXS60tg9WMYTWCy3fB6rGoyjLVxc4dR9PYjEdR2iDYsaF5h03NA+XuYPQQ==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@15.5.2': - resolution: {integrity: sha512-W5VvyZHnxG/2ukhZF/9Ikdra5fdNftxI6ybeVKYvBPDtyx7x4jPPSNduUkfH5fo3zG0JQ0bPxgy41af2JX5D4Q==} + '@next/swc-win32-x64-msvc@15.3.4': + resolution: {integrity: sha512-4kDt31Bc9DGyYs41FTL1/kNpDeHyha2TC0j5sRRoKCyrhNcfZ/nRQkAUlF27mETwm8QyHqIjHJitfcza2Iykfg==} engines: {node: '>= 10'} cpu: [x64] os: [win32] @@ -1802,12 +1779,8 @@ packages: resolution: {integrity: sha512-warwspo+UYUPep0Q+vtdVB4Ugn8GGQj8iyB3gnRWsztmUHTI3S1nhdiWNsPUGL0vud7JlRRk1XEu7Lq1KGTnMQ==} engines: {node: ^14.21.3 || >=16} - '@noble/curves@1.9.1': - resolution: {integrity: sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA==} - engines: {node: ^14.21.3 || >=16} - - '@noble/curves@1.9.7': - resolution: {integrity: sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==} + '@noble/curves@1.9.2': + resolution: {integrity: sha512-HxngEd2XUcg9xi20JkwlLCtYwfoFw4JGkuZpT+WlsPD4gB/cxkvTD8fSsoAnphGZhFdZYKeQIPCuFlWPm1uE0g==} engines: {node: ^14.21.3 || >=16} '@noble/hashes@1.4.0': @@ -1850,8 +1823,8 @@ packages: resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} engines: {node: '>=14'} - '@pkgr/core@0.2.9': - resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + '@pkgr/core@0.2.7': + resolution: {integrity: sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} '@reown/appkit-common@1.7.8': @@ -1883,116 +1856,111 @@ packages: '@reown/appkit@1.7.8': resolution: {integrity: sha512-51kTleozhA618T1UvMghkhKfaPcc9JlKwLJ5uV+riHyvSoWPKPRIa5A6M1Wano5puNyW0s3fwywhyqTHSilkaA==} - '@rollup/rollup-android-arm-eabi@4.50.0': - resolution: {integrity: sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==} + '@rollup/rollup-android-arm-eabi@4.44.0': + resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.50.0': - resolution: {integrity: sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==} + '@rollup/rollup-android-arm64@4.44.0': + resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.50.0': - resolution: {integrity: sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==} + '@rollup/rollup-darwin-arm64@4.44.0': + resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.50.0': - resolution: {integrity: sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==} + '@rollup/rollup-darwin-x64@4.44.0': + resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.50.0': - resolution: {integrity: sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==} + '@rollup/rollup-freebsd-arm64@4.44.0': + resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.50.0': - resolution: {integrity: sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==} + '@rollup/rollup-freebsd-x64@4.44.0': + resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.50.0': - resolution: {integrity: sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==} + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': + resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.50.0': - resolution: {integrity: sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==} + '@rollup/rollup-linux-arm-musleabihf@4.44.0': + resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.50.0': - resolution: {integrity: sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==} + '@rollup/rollup-linux-arm64-gnu@4.44.0': + resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.50.0': - resolution: {integrity: sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==} + '@rollup/rollup-linux-arm64-musl@4.44.0': + resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.50.0': - resolution: {integrity: sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==} + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': + resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.50.0': - resolution: {integrity: sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==} + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': + resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.50.0': - resolution: {integrity: sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==} + '@rollup/rollup-linux-riscv64-gnu@4.44.0': + resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.50.0': - resolution: {integrity: sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==} + '@rollup/rollup-linux-riscv64-musl@4.44.0': + resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.50.0': - resolution: {integrity: sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==} + '@rollup/rollup-linux-s390x-gnu@4.44.0': + resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.50.0': - resolution: {integrity: sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==} + '@rollup/rollup-linux-x64-gnu@4.44.0': + resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.50.0': - resolution: {integrity: sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==} + '@rollup/rollup-linux-x64-musl@4.44.0': + resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} cpu: [x64] os: [linux] - '@rollup/rollup-openharmony-arm64@4.50.0': - resolution: {integrity: sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==} - cpu: [arm64] - os: [openharmony] - - '@rollup/rollup-win32-arm64-msvc@4.50.0': - resolution: {integrity: sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==} + '@rollup/rollup-win32-arm64-msvc@4.44.0': + resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.50.0': - resolution: {integrity: sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==} + '@rollup/rollup-win32-ia32-msvc@4.44.0': + resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.50.0': - resolution: {integrity: sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==} + '@rollup/rollup-win32-x64-msvc@4.44.0': + resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} cpu: [x64] os: [win32] '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} - '@rushstack/eslint-patch@1.12.0': - resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==} + '@rushstack/eslint-patch@1.11.0': + resolution: {integrity: sha512-zxnHvoMQVqewTJr/W4pKjF0bMGiKJv1WX7bSrkl46Hg0QjESbzBROWK0Wg4RphzSOS5Jiy7eFimmM3UgMrMZbQ==} '@safe-global/safe-apps-provider@0.18.6': resolution: {integrity: sha512-4LhMmjPWlIO8TTDC2AwLk44XKXaK6hfBTWyljDm0HQ6TWlOEijVWNrt2s3OCVMSxlXAcEzYfqyu1daHZooTC2Q==} @@ -2078,6 +2046,12 @@ packages: peerDependencies: typescript: '>=5' + '@solana/codecs-core@2.1.1': + resolution: {integrity: sha512-iPQW3UZ2Vi7QFBo2r9tw0NubtH8EdrhhmZulx6lC8V5a+qjaxovtM/q/UW2BTNpqqHLfO0tIcLyBLrNH4HTWPg==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/codecs-core@2.3.0': resolution: {integrity: sha512-oG+VZzN6YhBHIoSKgS5ESM9VIGzhWjEHEGNPSibiDTxFhsFWxNaz8LbMDPjBUE69r9wmdGLkrQ+wVPbnJcZPvw==} engines: {node: '>=20.18.0'} @@ -2100,6 +2074,12 @@ packages: peerDependencies: typescript: '>=5' + '@solana/codecs-numbers@2.1.1': + resolution: {integrity: sha512-m20IUPJhPUmPkHSlZ2iMAjJ7PaYUvlMtFhCQYzm9BEBSI6OCvXTG3GAPpAnSGRBfg5y+QNqqmKn4QHU3B6zzCQ==} + engines: {node: '>=20.18.0'} + peerDependencies: + typescript: '>=5.3.3' + '@solana/codecs-numbers@2.3.0': resolution: {integrity: sha512-jFvvwKJKffvG7Iz9dmN51OGB7JBcy2CJ6Xf3NqD/VP90xak66m/Lg48T01u5IQ/hc15mChVHiBm+HHuOFDUrQg==} engines: {node: '>=20.18.0'} @@ -2136,6 +2116,13 @@ packages: peerDependencies: typescript: '>=5' + '@solana/errors@2.1.1': + resolution: {integrity: sha512-sj6DaWNbSJFvLzT8UZoabMefQUfSW/8tXK7NTiagsDmh+Q87eyQDDC9L3z+mNmx9b6dEf6z660MOIplDD2nfEw==} + engines: {node: '>=20.18.0'} + hasBin: true + peerDependencies: + typescript: '>=5.3.3' + '@solana/errors@2.3.0': resolution: {integrity: sha512-66RI9MAbwYV0UtP7kGcTBVLxJgUxoZGm8Fbc0ah+lGiAw17Gugco6+9GrJCV83VyF2mDWyYnYM9qdI3yjgpnaQ==} engines: {node: '>=20.18.0'} @@ -2293,8 +2280,8 @@ packages: peerDependencies: '@solana/web3.js': ^1.95.3 - '@solana/spl-token@0.4.14': - resolution: {integrity: sha512-u09zr96UBpX4U685MnvQsNzlvw9TiY005hk1vJmJr7gMJldoPG1eYU5/wNEyOA5lkMLiR/gOi9SFD4MefOYEsA==} + '@solana/spl-token@0.4.13': + resolution: {integrity: sha512-cite/pYWQZZVvLbg5lsodSovbetK/eA24gaR0eeUeMuBAMNrT8XFCwaygKy0N2WSg3gSyjjNpIeAGBAKZaY/1w==} engines: {node: '>=16'} peerDependencies: '@solana/web3.js': ^1.95.5 @@ -2329,8 +2316,8 @@ packages: peerDependencies: typescript: '>=5.3.3' - '@solana/web3.js@1.98.4': - resolution: {integrity: sha512-vv9lfnvjUsRiq//+j5pBdXig0IQdtzA0BRZ3bXEP4KaIyF1CcaydWqgyzQgfZMNIsWNWmG+AUHwPy4AHOD6gpw==} + '@solana/web3.js@1.98.2': + resolution: {integrity: sha512-BqVwEG+TaG2yCkBMbD3C4hdpustR4FpuUFRPUmqRZYYlPI9Hg4XMWxHWOWRzHE9Lkc9NDjzXFX7lDXSgzC7R1A==} '@svgr/babel-plugin-add-jsx-attribute@8.0.0': resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==} @@ -2410,17 +2397,20 @@ packages: resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==} engines: {node: '>=14'} + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} '@swc/helpers@0.5.17': resolution: {integrity: sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==} - '@tanstack/query-core@5.85.9': - resolution: {integrity: sha512-5fxb9vwyftYE6KFLhhhDyLr8NO75+Wpu7pmTo+TkwKmMX2oxZDoLwcqGP8ItKSpUMwk3urWgQDZfyWr5Jm9LsQ==} + '@tanstack/query-core@5.81.2': + resolution: {integrity: sha512-QLYkPdrudoMATDFa3MiLEwRhNnAlzHWDf0LKaXUqJd0/+QxN8uTPi7bahRlxoAyH0UbLMBdeDbYzWALj7THOtw==} - '@tanstack/react-query@5.85.9': - resolution: {integrity: sha512-2T5zgSpcOZXGkH/UObIbIkGmUPQqZqn7esVQFXLOze622h4spgWf5jmvrqAo9dnI13/hyMcNsF1jsoDcb59nJQ==} + '@tanstack/react-query@5.81.2': + resolution: {integrity: sha512-pe8kFlTrL2zFLlcAj2kZk9UaYYHDk9/1hg9EBaoO3cxDhOZf1FRGJeziSXKrVZyxIfs7b3aoOj/bw7Lie0mDUg==} peerDependencies: react: ^18 || ^19 @@ -2428,8 +2418,8 @@ packages: resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} - '@tybys/wasm-util@0.10.0': - resolution: {integrity: sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ==} + '@tybys/wasm-util@0.9.0': + resolution: {integrity: sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==} '@types/body-parser@1.19.6': resolution: {integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==} @@ -2449,8 +2439,8 @@ packages: '@types/estree@1.0.8': resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/express-serve-static-core@5.0.7': - resolution: {integrity: sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==} + '@types/express-serve-static-core@5.0.6': + resolution: {integrity: sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==} '@types/express@5.0.3': resolution: {integrity: sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==} @@ -2464,9 +2454,6 @@ packages: '@types/json5@0.0.29': resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} - '@types/lodash@4.17.20': - resolution: {integrity: sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==} - '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -2476,11 +2463,11 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@20.19.12': - resolution: {integrity: sha512-lSOjyS6vdO2G2g2CWrETTV3Jz2zlCXHpu1rcubLKpz9oj+z/1CceHlj+yq53W+9zgb98nSov/wjEKYDNauD+Hw==} + '@types/node@20.19.1': + resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==} - '@types/node@22.18.0': - resolution: {integrity: sha512-m5ObIqwsUp6BZzyiy4RdZpzWGub9bqLJMvZDD0QMXhxjqMHMENlj+SqF5QxoUwaQNFe+8kz8XM8ZQhqkQPTgMQ==} + '@types/node@22.15.33': + resolution: {integrity: sha512-wzoocdnnpSxZ+6CjW4ADCK1jVmd1S/J3ArNWfn8FDDQtRm8dkDg7TA+mvek2wNrfCgwuZxqEOiB9B1XCJ6+dbw==} '@types/qs@6.14.0': resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==} @@ -2488,13 +2475,13 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-dom@19.1.9': - resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==} + '@types/react-dom@19.1.6': + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} peerDependencies: '@types/react': ^19.0.0 - '@types/react@19.1.12': - resolution: {integrity: sha512-cMoR+FoAf/Jyq6+Df2/Z41jISvGZZ2eTlnsaJRptmZ76Caldwy1odD4xTr/gNV9VLj0AWgg/nmkevIyUfIIq5w==} + '@types/react@19.1.8': + resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} '@types/send@0.17.5': resolution: {integrity: sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==} @@ -2514,157 +2501,157 @@ packages: '@types/ws@8.18.1': resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.42.0': - resolution: {integrity: sha512-Aq2dPqsQkxHOLfb2OPv43RnIvfj05nw8v/6n3B2NABIPpHnjQnaLo9QGMTvml+tv4korl/Cjfrb/BYhoL8UUTQ==} + '@typescript-eslint/eslint-plugin@8.35.0': + resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.42.0 + '@typescript-eslint/parser': ^8.35.0 eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/parser@8.42.0': - resolution: {integrity: sha512-r1XG74QgShUgXph1BYseJ+KZd17bKQib/yF3SR+demvytiRXrwd12Blnz5eYGm8tXaeRdd4x88MlfwldHoudGg==} + '@typescript-eslint/parser@8.35.0': + resolution: {integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/project-service@8.42.0': - resolution: {integrity: sha512-vfVpLHAhbPjilrabtOSNcUDmBboQNrJUiNAGoImkZKnMjs2TIcWG33s4Ds0wY3/50aZmTMqJa6PiwkwezaAklg==} + '@typescript-eslint/project-service@8.35.0': + resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/scope-manager@8.42.0': - resolution: {integrity: sha512-51+x9o78NBAVgQzOPd17DkNTnIzJ8T/O2dmMBLoK9qbY0Gm52XJcdJcCl18ExBMiHo6jPMErUQWUv5RLE51zJw==} + '@typescript-eslint/scope-manager@8.35.0': + resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.42.0': - resolution: {integrity: sha512-kHeFUOdwAJfUmYKjR3CLgZSglGHjbNTi1H8sTYRYV2xX6eNz4RyJ2LIgsDLKf8Yi0/GL1WZAC/DgZBeBft8QAQ==} + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/type-utils@8.42.0': - resolution: {integrity: sha512-9KChw92sbPTYVFw3JLRH1ockhyR3zqqn9lQXol3/YbI6jVxzWoGcT3AsAW0mu1MY0gYtsXnUGV/AKpkAj5tVlQ==} + '@typescript-eslint/type-utils@8.35.0': + resolution: {integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/types@8.42.0': - resolution: {integrity: sha512-LdtAWMiFmbRLNP7JNeY0SqEtJvGMYSzfiWBSmx+VSZ1CH+1zyl8Mmw1TT39OrtsRvIYShjJWzTDMPWZJCpwBlw==} + '@typescript-eslint/types@8.35.0': + resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.42.0': - resolution: {integrity: sha512-ku/uYtT4QXY8sl9EDJETD27o3Ewdi72hcXg1ah/kkUgBvAYHLwj2ofswFFNXS+FL5G+AGkxBtvGt8pFBHKlHsQ==} + '@typescript-eslint/typescript-estree@8.35.0': + resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/utils@8.42.0': - resolution: {integrity: sha512-JnIzu7H3RH5BrKC4NoZqRfmjqCIS1u3hGZltDYJgkVdqAezl4L9d1ZLw+36huCujtSBSAirGINF/S4UxOcR+/g==} + '@typescript-eslint/utils@8.35.0': + resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 - typescript: '>=4.8.4 <6.0.0' + typescript: '>=4.8.4 <5.9.0' - '@typescript-eslint/visitor-keys@8.42.0': - resolution: {integrity: sha512-3WbiuzoEowaEn8RSnhJBrxSwX8ULYE9CXaPepS2C2W3NSA5NNIvBaslpBSBElPq0UGr0xVJlXFWOAKIkyylydQ==} + '@typescript-eslint/visitor-keys@8.35.0': + resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + '@unrs/resolver-binding-android-arm-eabi@1.9.2': + resolution: {integrity: sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==} cpu: [arm] os: [android] - '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + '@unrs/resolver-binding-android-arm64@1.9.2': + resolution: {integrity: sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==} cpu: [arm64] os: [android] - '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + '@unrs/resolver-binding-darwin-arm64@1.9.2': + resolution: {integrity: sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==} cpu: [arm64] os: [darwin] - '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + '@unrs/resolver-binding-darwin-x64@1.9.2': + resolution: {integrity: sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==} cpu: [x64] os: [darwin] - '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + '@unrs/resolver-binding-freebsd-x64@1.9.2': + resolution: {integrity: sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==} cpu: [x64] os: [freebsd] - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.2': + resolution: {integrity: sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.2': + resolution: {integrity: sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + '@unrs/resolver-binding-linux-arm64-gnu@1.9.2': + resolution: {integrity: sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==} cpu: [arm64] os: [linux] - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + '@unrs/resolver-binding-linux-arm64-musl@1.9.2': + resolution: {integrity: sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==} cpu: [arm64] os: [linux] - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.2': + resolution: {integrity: sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==} cpu: [ppc64] os: [linux] - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.2': + resolution: {integrity: sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==} cpu: [riscv64] os: [linux] - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + '@unrs/resolver-binding-linux-riscv64-musl@1.9.2': + resolution: {integrity: sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==} cpu: [riscv64] os: [linux] - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + '@unrs/resolver-binding-linux-s390x-gnu@1.9.2': + resolution: {integrity: sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==} cpu: [s390x] os: [linux] - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + '@unrs/resolver-binding-linux-x64-gnu@1.9.2': + resolution: {integrity: sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==} cpu: [x64] os: [linux] - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + '@unrs/resolver-binding-linux-x64-musl@1.9.2': + resolution: {integrity: sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==} cpu: [x64] os: [linux] - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + '@unrs/resolver-binding-wasm32-wasi@1.9.2': + resolution: {integrity: sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + '@unrs/resolver-binding-win32-arm64-msvc@1.9.2': + resolution: {integrity: sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==} cpu: [arm64] os: [win32] - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + '@unrs/resolver-binding-win32-ia32-msvc@1.9.2': + resolution: {integrity: sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==} cpu: [ia32] os: [win32] - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + '@unrs/resolver-binding-win32-x64-msvc@1.9.2': + resolution: {integrity: sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==} cpu: [x64] os: [win32] @@ -2710,18 +2697,18 @@ packages: '@vitest/utils@3.2.4': resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} - '@wagmi/connectors@5.9.9': - resolution: {integrity: sha512-6+eqU7P2OtxU2PkIw6kHojfYYUJykYG2K5rSkzVh29RDCAjhJqGEZW5f1b8kV5rUBORip1NpST8QTBNi96JHGQ==} + '@wagmi/connectors@5.8.5': + resolution: {integrity: sha512-CHh4uYP6MziCMlSVXmuAv7wMoYWdxXliuzwCRAxHNNkgXE7z37ez5XzJu0Sm39NUau3Fl8WSjwKo4a4w9BOYNA==} peerDependencies: - '@wagmi/core': 2.20.3 + '@wagmi/core': 2.17.3 typescript: '>=5.0.4' viem: 2.x peerDependenciesMeta: typescript: optional: true - '@wagmi/core@2.20.3': - resolution: {integrity: sha512-gsbuHnWxf0AYZISvR8LvF/vUCIq6/ZwT5f5/FKd6wLA7Wq05NihCvmQpIgrcVbpSJPL67wb6S8fXm3eJGJA1vQ==} + '@wagmi/core@2.17.3': + resolution: {integrity: sha512-fgZR9fAiCFtGaosTspkTx5lidccq9Z5xRWOk1HG0VfB6euQGw2//Db7upiP4uQ7DPst2YS9yQN2A1m9+iJLYCw==} peerDependencies: '@tanstack/query-core': '>=5.0.0' typescript: '>=5.0.4' @@ -2842,17 +2829,6 @@ packages: zod: optional: true - abitype@1.1.0: - resolution: {integrity: sha512-6Vh4HcRxNMLA0puzPjM5GBgT4aAcFGKZzSgAXvuZ27shJP6NEpielTuqbBmZILR5/xd0PizkBGy5hReKz9jl5A==} - peerDependencies: - typescript: '>=5.0.4' - zod: ^3.22.0 || ^4.0.0 - peerDependenciesMeta: - typescript: - optional: true - zod: - optional: true - accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} @@ -2867,8 +2843,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - agent-base@7.1.4: - resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} engines: {node: '>= 14'} agentkeepalive@4.6.0: @@ -2882,8 +2858,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.2.0: - resolution: {integrity: sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==} + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} engines: {node: '>=12'} ansi-styles@4.3.0: @@ -2984,25 +2960,28 @@ packages: peerDependencies: axios: 0.x || 1.x - axios@1.11.0: - resolution: {integrity: sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==} + axios@1.10.0: + resolution: {integrity: sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==} + + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} - babel-plugin-polyfill-corejs2@0.4.14: - resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} + babel-plugin-polyfill-corejs2@0.4.13: + resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-corejs3@0.13.0: - resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==} + babel-plugin-polyfill-corejs3@0.11.1: + resolution: {integrity: sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.5: - resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} + babel-plugin-polyfill-regenerator@0.6.4: + resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -3025,8 +3004,8 @@ packages: resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==} engines: {node: '>= 10.0.0'} - bignumber.js@9.3.1: - resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} + bignumber.js@9.3.0: + resolution: {integrity: sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -3048,8 +3027,8 @@ packages: borsh@0.7.0: resolution: {integrity: sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA==} - bowser@2.12.1: - resolution: {integrity: sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==} + bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} brace-expansion@1.1.12: resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} @@ -3061,8 +3040,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.4: - resolution: {integrity: sha512-4jYpcjabC606xJ3kw2QwGEZKX0Aw7sgQdZCvIK9dhVSPh76BKo+C+btT1RRofH7B+8iNpEbgGNVWiLki5q93yg==} + browserslist@4.25.0: + resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3085,6 +3064,10 @@ packages: peerDependencies: esbuild: '>=0.18' + busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -3121,19 +3104,19 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001739: - resolution: {integrity: sha512-y+j60d6ulelrNSwpPyrHdl+9mJnQzHBr08xm48Qno0nSk4h3Qojh+ziv2qE6rXf4k3tadF4o1J/1tAbVm1NtnA==} + caniuse-lite@1.0.30001724: + resolution: {integrity: sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==} - chai@5.3.3: - resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} - engines: {node: '>=18'} + chai@5.2.0: + resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} + engines: {node: '>=12'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} - chalk@5.6.0: - resolution: {integrity: sha512-46QrSQFyVSEyYAgQ22hQ+zDa60YHA4fBstHmtSApj1Y5vKtG27fWowW03jCk5KcbXEWPZUIR894aARCA/G1kfQ==} + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} charenc@0.0.2: @@ -3190,8 +3173,12 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commander@14.0.0: - resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + commander@13.1.0: + resolution: {integrity: sha512-/rFeCpNJQbhSZjGVwO9RFV3xPqbnERS8MmIQzCtD/zl6gpJuV/bMLuN92oG3F7d8oDEHHRrujSXNUr8fpjntKw==} + engines: {node: '>=18'} + + commander@14.0.1: + resolution: {integrity: sha512-2JkV3gUZUVrbNA+1sjBOYLsMZ5cEEl8GTFP2a4AVz5hvasAMCQ1D2l2le/cX+pV4N6ZU17zjUahLpIXRrnWL8A==} engines: {node: '>=20'} commander@2.20.3: @@ -3240,8 +3227,8 @@ packages: resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} engines: {node: '>= 0.6'} - core-js-compat@3.45.1: - resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==} + core-js-compat@3.43.0: + resolution: {integrity: sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} @@ -3276,8 +3263,8 @@ packages: crypt@0.0.2: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} - css-select@5.2.2: - resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==} + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} css-tree@2.2.1: resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} @@ -3287,8 +3274,8 @@ packages: resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} - css-what@6.2.2: - resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} cssesc@3.0.0: @@ -3300,8 +3287,8 @@ packages: resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} - cssstyle@4.6.0: - resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + cssstyle@4.5.0: + resolution: {integrity: sha512-/7gw8TGrvH/0g564EnhgFZogTMVe+lifpB7LWU+PEsiq5o83TUXR3fDbzTRXOJhoJwck5IS9ez3Em5LNMMO2aw==} engines: {node: '>=18'} csstype@3.1.3: @@ -3371,8 +3358,8 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} - decimal.js@10.6.0: - resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + decimal.js@10.5.0: + resolution: {integrity: sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==} decode-uri-component@0.2.2: resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} @@ -3477,8 +3464,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.214: - resolution: {integrity: sha512-TpvUNdha+X3ybfU78NoQatKvQEm1oq3lf2QbnmCEdw+Bd9RuIAY+hJTvq1avzHM0f7EJfnH3vbCnbzKzisc/9Q==} + electron-to-chromium@1.5.173: + resolution: {integrity: sha512-2bFhXP2zqSfQHugjqJIDFVwa+qIxyNApenmXTp9EjaKtdPrES5Qcn9/aSFy/NaP2E+fWG/zxKu/LBvY36p5VNQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -3562,8 +3549,8 @@ packages: es6-promisify@5.0.0: resolution: {integrity: sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==} - esbuild@0.25.9: - resolution: {integrity: sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==} + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} engines: {node: '>=18'} hasBin: true @@ -3646,8 +3633,8 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 - eslint-plugin-prettier@5.5.4: - resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + eslint-plugin-prettier@5.5.0: + resolution: {integrity: sha512-8qsOYwkkGrahrgoUv76NZi23koqXOGiiEzXMrT8Q7VcYaUISR+5MorIUxfWqYXN0fN/31WbSrxCxFkVQ43wwrA==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' @@ -3684,8 +3671,8 @@ packages: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - eslint@9.34.0: - resolution: {integrity: sha512-RNCHRX5EwdrESy3Jc9o8ie8Bog+PeYvvSR8sDGoZxNFTvZ4dlxUB3WzQ3bQMztFrSRODGrLLj8g6OFuGY/aiQg==} + eslint@9.29.0: + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true peerDependencies: @@ -3748,8 +3735,8 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} - expect-type@1.2.2: - resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + expect-type@1.2.1: + resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} engines: {node: '>=12.0.0'} express@4.21.2: @@ -3800,9 +3787,8 @@ packages: fastq@1.19.1: resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} - fdir@6.5.0: - resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} - engines: {node: '>=12.0.0'} + fdir@6.4.6: + resolution: {integrity: sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: @@ -3846,8 +3832,8 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} - follow-redirects@1.15.11: - resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + follow-redirects@1.15.9: + resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} engines: {node: '>=4.0'} peerDependencies: debug: '*' @@ -3863,6 +3849,10 @@ packages: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + form-data@4.0.3: + resolution: {integrity: sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==} + engines: {node: '>= 6'} + form-data@4.0.4: resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} engines: {node: '>= 6'} @@ -3925,6 +3915,10 @@ packages: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + globals@14.0.0: resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} engines: {node: '>=18'} @@ -3952,8 +3946,8 @@ packages: resolution: {integrity: sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==} engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0} - h3@1.15.4: - resolution: {integrity: sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==} + h3@1.15.3: + resolution: {integrity: sha512-z6GknHqyX0h9aQaTx22VZDf6QyZn+0Nh+Ym8O/u0SGSkyF5cuTJYKlc8MkzW3Nzf9LE1ivcpmYC3FUGpywhuUQ==} has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} @@ -3982,8 +3976,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.9.6: - resolution: {integrity: sha512-doVjXhSFvYZ7y0dNokjwwSahcrAfdz+/BCLvAMa/vHLzjj8+CFyV5xteThGUsKdkaasgN+gF2mUxao+SGLpUeA==} + hono@4.8.2: + resolution: {integrity: sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w==} engines: {node: '>=16.9.0'} html-encoding-sniffer@4.0.0: @@ -4013,9 +4007,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - idb-keyval@6.2.1: - resolution: {integrity: sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg==} - idb-keyval@6.2.2: resolution: {integrity: sha512-yjD9nARJ/jb1g+CvD0tlhUHOrJ9Sy0P8T9MF3YaLlHnSRpwPfpTX0XIvpmw3gAJUmEu3FiICLBDPXVwyEvrleg==} @@ -4230,8 +4221,8 @@ packages: jose@5.10.0: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} - jose@6.1.0: - resolution: {integrity: sha512-TTQJyoEoKcC1lscpVDCSsVgYzUDg/0Bt3WE//WiTPK6uOCQC2KZS4MpugbMWt/zyjkopgZoXhZuCi00gLudfUA==} + jose@6.0.11: + resolution: {integrity: sha512-QxG7EaliDARm1O1S8BGakqncGT9s25bKL1WSf6/oa17Tkqwi8D2ZNglqCF+DsYF88/rV66Q/Q2mFAy697E1DUg==} joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} @@ -4333,11 +4324,11 @@ packages: lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} - lit-element@4.2.1: - resolution: {integrity: sha512-WGAWRGzirAgyphK2urmYOV72tlvnxw7YfyLDgQ+OZnM9vQQBQnumQ7jUJe6unEzwGU3ahFOjuz1iz1jjrpCPuw==} + lit-element@4.2.0: + resolution: {integrity: sha512-MGrXJVAI5x+Bfth/pU9Kst1iWID6GHDLEzFEnyULB/sFiRLgkd8NPK/PeeXxktA3T6EIIaq8U3KcbTU5XFcP2Q==} - lit-html@3.3.1: - resolution: {integrity: sha512-S9hbyDu/vs1qNrithiNyeyv64c9yqiW9l+DBgI18fL+MTvOtWoFR0FWiyq1TxaYef5wNlpEmzlXoBlZEO+WjoA==} + lit-html@3.3.0: + resolution: {integrity: sha512-RHoswrFAxY2d8Cf2mm4OZ1DgzCoBKUKSPvA1fhtSELxUERq2aQQ2h05pO9j81gS1o7RIRJ+CePLogfyahwmynw==} lit@3.3.0: resolution: {integrity: sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==} @@ -4370,8 +4361,8 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - loupe@3.2.1: - resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + loupe@3.1.4: + resolution: {integrity: sha512-wJzkKwJrheKtknCOKNEtDK4iqg/MxmZheEMtSTYvnzRdEYaZzmgH976nenp8WdJRdx5Vc1X/9MO0Oszl6ezeXg==} lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} @@ -4382,8 +4373,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - magic-string@0.30.18: - resolution: {integrity: sha512-yi8swmWbO17qHhwIBNeeZxTceJMeBvWJaId6dyvTSOwTipqeHhMhOrz6513r1sOKnpvQ7zkhlG8tPrpilwTxHQ==} + magic-string@0.30.17: + resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} @@ -4455,8 +4446,8 @@ packages: typescript: optional: true - mlly@1.8.0: - resolution: {integrity: sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==} + mlly@1.7.4: + resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -4475,8 +4466,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - napi-postinstall@0.3.3: - resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==} + napi-postinstall@0.2.4: + resolution: {integrity: sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true @@ -4487,13 +4478,13 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - next@15.5.2: - resolution: {integrity: sha512-H8Otr7abj1glFhbGnvUt3gz++0AF1+QoCXEBmd/6aKbfdFwrn0LpA836Ed5+00va/7HQSDD+mOoVhn3tNy3e/Q==} + next@15.3.4: + resolution: {integrity: sha512-mHKd50C+mCjam/gcnwqL1T1vPx/XQNFlXqFIVdgQdVAFY9iIQtY0IfaVflEYzKiqjeA7B0cYYMaCrmAYFjs4rA==} engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} hasBin: true peerDependencies: '@opentelemetry/api': ^1.1.0 - '@playwright/test': ^1.51.1 + '@playwright/test': ^1.41.2 babel-plugin-react-compiler: '*' react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 @@ -4514,8 +4505,8 @@ packages: node-addon-api@2.0.2: resolution: {integrity: sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==} - node-fetch-native@1.6.7: - resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + node-fetch-native@1.6.6: + resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} node-fetch@2.7.0: resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} @@ -4530,8 +4521,8 @@ packages: resolution: {integrity: sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==} hasBin: true - node-mock-http@1.0.2: - resolution: {integrity: sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==} + node-mock-http@1.0.0: + resolution: {integrity: sha512-0uGYQ1WQL1M5kKvGRXWQ3uZCHtLTO8hln3oBjIusM75WoesZ909uQJs/Hb946i2SS+Gsrhkaa6iAO17jRIv6DQ==} node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -4543,8 +4534,8 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - nwsapi@2.2.21: - resolution: {integrity: sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==} + nwsapi@2.2.20: + resolution: {integrity: sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==} obj-multiplex@1.0.0: resolution: {integrity: sha512-0GNJAOsHoBHeNTvl5Vt6IWnpUEcc3uSRxzBri7EDyIcMgYvnY2JL2qdeV5zTMjWQX5OHcD5amcW2HFfDh0gjIA==} @@ -4622,16 +4613,8 @@ packages: typescript: optional: true - ox@0.6.9: - resolution: {integrity: sha512-wi5ShvzE4eOcTwQVsIPdFr+8ycyX+5le/96iAJutaZAvCes1J0+RvpEPg5QDPDiaR0XQQAvZVl7AwqQcINuUug==} - peerDependencies: - typescript: '>=5.4.0' - peerDependenciesMeta: - typescript: - optional: true - - ox@0.9.3: - resolution: {integrity: sha512-KzyJP+fPV4uhuuqrTZyok4DC7vFzi7HLUFiUNEmpbyh59htKWkOC98IONC1zgXJPbHAhQgqs6B0Z6StCGhmQvg==} + ox@0.8.1: + resolution: {integrity: sha512-e+z5epnzV+Zuz91YYujecW8cF01mzmrUtWotJ0oEPym/G82uccs7q0WDHTYL3eiONbTUEvcZrptAKLgTBD3u2A==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: @@ -4707,8 +4690,8 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.1: - resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + pathval@2.0.0: + resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} engines: {node: '>= 14.16'} picocolors@1.1.1: @@ -4718,8 +4701,8 @@ packages: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.2: + resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} engines: {node: '>=12'} pify@2.3.0: @@ -4826,11 +4809,8 @@ packages: resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} engines: {node: ^10 || ^12 || >=14} - preact@10.24.2: - resolution: {integrity: sha512-1cSoF0aCC8uaARATfrlz4VCBqE8LwZwRfLgkxJOQwAlQt6ayTmi0D9OF7nXid1POI5SZidFuG9CnlXbDfLqY/Q==} - - preact@10.27.1: - resolution: {integrity: sha512-V79raXEWch/rbqoNc7nT9E4ep7lu+mI3+sBmfRD4i1M73R3WLYcCtdI0ibxGVf4eQL8ZIz2nFacqEC+rmnOORQ==} + preact@10.26.9: + resolution: {integrity: sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} @@ -4906,16 +4886,16 @@ packages: resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} engines: {node: '>= 0.8'} - react-dom@19.1.1: - resolution: {integrity: sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==} + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} peerDependencies: - react: ^19.1.1 + react: ^19.1.0 react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react@19.1.1: - resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==} + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -4997,13 +4977,13 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.50.0: - resolution: {integrity: sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==} + rollup@4.44.0: + resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rpc-websockets@9.1.3: - resolution: {integrity: sha512-I+kNjW0udB4Fetr3vvtRuYZJS0PcSPyyvBcH5sDdoV8DFs5E4W2pTr7aiMlKfPxANTClP9RlqCPolj9dd5MsEA==} + rpc-websockets@9.1.1: + resolution: {integrity: sha512-1IXGM/TfPT6nfYMIXkJdzn+L4JEsmb0FL1O2OBjaH03V3yuUDdKFulGLMFG6ErV+8pZ5HVC0limve01RyO+saA==} rrweb-cssom@0.8.0: resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} @@ -5078,13 +5058,12 @@ packages: setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} - sha.js@2.4.12: - resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==} - engines: {node: '>= 0.10'} + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} hasBin: true - sharp@0.34.3: - resolution: {integrity: sha512-eX2IQ6nFohW4DbvHIOLRB3MHFpYqaqvXd3Tp5e/T/dSH83fxaNJQRvDMhASmkNTsNTVF2/OOopzRCt7xokgPfg==} + sharp@0.34.2: + resolution: {integrity: sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} shebang-command@2.0.0: @@ -5142,7 +5121,6 @@ packages: source-map@0.8.0-beta.0: resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions spdx-exceptions@2.5.0: resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} @@ -5150,8 +5128,8 @@ packages: spdx-expression-parse@4.0.0: resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} - spdx-license-ids@3.0.22: - resolution: {integrity: sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==} + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} split-on-first@1.1.0: resolution: {integrity: sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==} @@ -5187,6 +5165,10 @@ packages: stream-shift@1.0.3: resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} + streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + strict-uri-encode@2.0.0: resolution: {integrity: sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==} engines: {node: '>=4'} @@ -5292,8 +5274,8 @@ packages: symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} - synckit@0.11.11: - resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + synckit@0.11.8: + resolution: {integrity: sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==} engines: {node: ^14.18.0 || >=16.0.0} tailwind-merge@2.6.0: @@ -5346,10 +5328,6 @@ packages: resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} hasBin: true - to-buffer@1.2.1: - resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==} - engines: {node: '>= 0.4'} - to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -5382,6 +5360,14 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-essentials@10.1.1: + resolution: {integrity: sha512-4aTB7KLHKmUvkjNj8V+EdnmuVTiECzn3K+zIbRthumvHu+j44x3w63xpfs0JL3NGIzGXqoQ7AV591xHO+XrOTw==} + peerDependencies: + typescript: '>=4.5.0' + peerDependenciesMeta: + typescript: + optional: true + ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} @@ -5423,43 +5409,43 @@ packages: typescript: optional: true - tsx@4.20.5: - resolution: {integrity: sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw==} + tsx@4.20.3: + resolution: {integrity: sha512-qjbnuR9Tr+FJOMBqJCW5ehvIo/buZq7vH7qD7JziU98h6l3qGy0a/yPFjwO+y0/T7GFpNgNAvEcPPVfyT8rrPQ==} engines: {node: '>=18.0.0'} hasBin: true - turbo-darwin-64@2.5.6: - resolution: {integrity: sha512-3C1xEdo4aFwMJAPvtlPqz1Sw/+cddWIOmsalHFMrsqqydcptwBfu26WW2cDm3u93bUzMbBJ8k3zNKFqxJ9ei2A==} + turbo-darwin-64@2.5.4: + resolution: {integrity: sha512-ah6YnH2dErojhFooxEzmvsoZQTMImaruZhFPfMKPBq8sb+hALRdvBNLqfc8NWlZq576FkfRZ/MSi4SHvVFT9PQ==} cpu: [x64] os: [darwin] - turbo-darwin-arm64@2.5.6: - resolution: {integrity: sha512-LyiG+rD7JhMfYwLqB6k3LZQtYn8CQQUePbpA8mF/hMLPAekXdJo1g0bUPw8RZLwQXUIU/3BU7tXENvhSGz5DPA==} + turbo-darwin-arm64@2.5.4: + resolution: {integrity: sha512-2+Nx6LAyuXw2MdXb7pxqle3MYignLvS7OwtsP9SgtSBaMlnNlxl9BovzqdYAgkUW3AsYiQMJ/wBRb7d+xemM5A==} cpu: [arm64] os: [darwin] - turbo-linux-64@2.5.6: - resolution: {integrity: sha512-GOcUTT0xiT/pSnHL4YD6Yr3HreUhU8pUcGqcI2ksIF9b2/r/kRHwGFcsHgpG3+vtZF/kwsP0MV8FTlTObxsYIA==} + turbo-linux-64@2.5.4: + resolution: {integrity: sha512-5May2kjWbc8w4XxswGAl74GZ5eM4Gr6IiroqdLhXeXyfvWEdm2mFYCSWOzz0/z5cAgqyGidF1jt1qzUR8hTmOA==} cpu: [x64] os: [linux] - turbo-linux-arm64@2.5.6: - resolution: {integrity: sha512-10Tm15bruJEA3m0V7iZcnQBpObGBcOgUcO+sY7/2vk1bweW34LMhkWi8svjV9iDF68+KJDThnYDlYE/bc7/zzQ==} + turbo-linux-arm64@2.5.4: + resolution: {integrity: sha512-/2yqFaS3TbfxV3P5yG2JUI79P7OUQKOUvAnx4MV9Bdz6jqHsHwc9WZPpO4QseQm+NvmgY6ICORnoVPODxGUiJg==} cpu: [arm64] os: [linux] - turbo-windows-64@2.5.6: - resolution: {integrity: sha512-FyRsVpgaj76It0ludwZsNN40ytHN+17E4PFJyeliBEbxrGTc5BexlXVpufB7XlAaoaZVxbS6KT8RofLfDRyEPg==} + turbo-windows-64@2.5.4: + resolution: {integrity: sha512-EQUO4SmaCDhO6zYohxIjJpOKRN3wlfU7jMAj3CgcyTPvQR/UFLEKAYHqJOnJtymbQmiiM/ihX6c6W6Uq0yC7mA==} cpu: [x64] os: [win32] - turbo-windows-arm64@2.5.6: - resolution: {integrity: sha512-j/tWu8cMeQ7HPpKri6jvKtyXg9K1gRyhdK4tKrrchH8GNHscPX/F71zax58yYtLRWTiK04zNzPcUJuoS0+v/+Q==} + turbo-windows-arm64@2.5.4: + resolution: {integrity: sha512-oQ8RrK1VS8lrxkLriotFq+PiF7iiGgkZtfLKF4DDKsmdbPo0O9R2mQxm7jHLuXraRCuIQDWMIw6dpcr7Iykf4A==} cpu: [arm64] os: [win32] - turbo@2.5.6: - resolution: {integrity: sha512-gxToHmi9oTBNB05UjUsrWf0OyN5ZXtD0apOarC1KIx232Vp3WimRNy3810QzeNSgyD5rsaIDXlxlbnOzlouo+w==} + turbo@2.5.4: + resolution: {integrity: sha512-kc8ZibdRcuWUG1pbYSBFWqmIjynlD8Lp7IB6U3vIzvOv9VG+6Sp8bzyeBWE3Oi8XV5KsQrznyRTBPvrf99E4mA==} hasBin: true type-check@0.4.0: @@ -5486,8 +5472,8 @@ packages: resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} engines: {node: '>= 0.4'} - typescript@5.9.2: - resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==} + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} hasBin: true @@ -5507,8 +5493,8 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - undici-types@7.15.0: - resolution: {integrity: sha512-Xyn5T99wU4kPhLZMm+ElE6M+IoSeG8Se7eG9xoZ82ZgVHJ07wb/IWcDZeXe2GOPkavcJ8ko5oSlXMDRl/QgY9Q==} + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} unicode-canonical-property-names-ecmascript@2.0.1: resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==} @@ -5530,11 +5516,11 @@ packages: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} engines: {node: '>= 0.8'} - unrs-resolver@1.11.1: - resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + unrs-resolver@1.9.2: + resolution: {integrity: sha512-VUyWiTNQD7itdiMuJy+EuLEErLj3uwX/EpHQF8EOf33Dq3Ju6VW1GXm+swk6+1h7a49uv9fKZ+dft9jU7esdLA==} - unstorage@1.17.0: - resolution: {integrity: sha512-l9Z7lBiwtNp8ZmcoZ/dmPkFXFdtEdZtTZafCSnEIj3YvtkXeGAtL2rN8MQFy/0cs4eOLpuRJMp9ivdug7TCvww==} + unstorage@1.16.0: + resolution: {integrity: sha512-WQ37/H5A7LcRPWfYOrDa1Ys02xAbpPJq6q5GkO88FBXVSQzHd7+BjEwfRqyaSWCv9MbsJy058GWjjPjcJ16GGA==} peerDependencies: '@azure/app-configuration': ^1.8.0 '@azure/cosmos': ^4.2.0 @@ -5544,11 +5530,10 @@ packages: '@azure/storage-blob': ^12.26.0 '@capacitor/preferences': ^6.0.3 || ^7.0.0 '@deno/kv': '>=0.9.0' - '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 '@planetscale/database': ^1.19.0 '@upstash/redis': ^1.34.3 '@vercel/blob': '>=0.27.1' - '@vercel/functions': ^2.2.12 '@vercel/kv': ^1.0.1 aws4fetch: ^1.0.20 db0: '>=0.2.1' @@ -5580,8 +5565,6 @@ packages: optional: true '@vercel/blob': optional: true - '@vercel/functions': - optional: true '@vercel/kv': optional: true aws4fetch: @@ -5660,8 +5643,8 @@ packages: typescript: optional: true - viem@2.37.3: - resolution: {integrity: sha512-hwoZqkFSy13GCFzIftgfIH8hNENvdlcHIvtLt73w91tL6rKmZjQisXWTahi1Vn5of8/JQ1FBKfwUus3YkDXwbw==} + viem@2.31.4: + resolution: {integrity: sha512-0UZ/asvzl6p44CIBRDbwEcn3HXIQQurBZcMo5qmLhQ8s27Ockk+RYohgTLlpLvkYs8/t4UUEREAbHLuek1kXcw==} peerDependencies: typescript: '>=5.0.4' peerDependenciesMeta: @@ -5721,6 +5704,12 @@ packages: yaml: optional: true + vitest-mock-extended@3.1.0: + resolution: {integrity: sha512-vCM0VkuocOUBwwqwV7JB7YStw07pqeKvEIrZnR8l3PtwYi6rAAJAyJACeC1UYNfbQWi85nz7EdiXWBFI5hll2g==} + peerDependencies: + typescript: 3.x || 4.x || 5.x + vitest: '>=3.0.0' + vitest@3.2.4: resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -5753,8 +5742,8 @@ packages: resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} engines: {node: '>=18'} - wagmi@2.16.9: - resolution: {integrity: sha512-5NbjvuNNhT0t0lQsDD5otQqZ5RZBM1UhInHoBq/Lpnr6xLLa8AWxYqHg5oZtGCdiUNltys11iBOS6z4mLepIqw==} + wagmi@2.15.6: + resolution: {integrity: sha512-tR4tm+7eE0UloQe1oi4hUIjIDyjv5ImQlzq/QcvvfJYWF/EquTfGrmht6+nTYGCIeSzeEvbK90KgWyNqa+HD7Q==} peerDependencies: '@tanstack/react-query': '>=5.0.0' react: '>=18' @@ -5879,8 +5868,8 @@ packages: utf-8-validate: optional: true - ws@8.18.3: - resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + ws@8.18.2: + resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -5915,8 +5904,8 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} - yaml@2.8.1: - resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + yaml@2.8.0: + resolution: {integrity: sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==} engines: {node: '>= 14.6'} hasBin: true @@ -5935,8 +5924,8 @@ packages: zod@3.22.4: resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==} - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod@3.25.67: + resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} zustand@5.0.0: resolution: {integrity: sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ==} @@ -5956,24 +5945,6 @@ packages: use-sync-external-store: optional: true - zustand@5.0.3: - resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} - engines: {node: '>=12.20.0'} - peerDependencies: - '@types/react': '>=18.0.0' - immer: '>=9.0.6' - react: '>=18.0.0' - use-sync-external-store: '>=1.2.0' - peerDependenciesMeta: - '@types/react': - optional: true - immer: - optional: true - react: - optional: true - use-sync-external-store: - optional: true - snapshots: '@adraffy/ens-normalize@1.11.0': {} @@ -5982,13 +5953,13 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 '@asamuzakjp/css-color@3.2.0': dependencies: '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) - '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 lru-cache: 10.4.3 @@ -5999,20 +5970,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.28.0': {} + '@babel/compat-data@7.27.5': {} - '@babel/core@7.28.3': + '@babel/core@7.27.4': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 + '@babel/generator': 7.27.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) - '@babel/helpers': 7.28.3 - '@babel/parser': 7.28.3 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helpers': 7.27.6 + '@babel/parser': 7.27.5 '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -6021,49 +5992,49 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/generator@7.28.3': + '@babel/generator@7.27.5': dependencies: - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.30 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 + '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/trace-mapping': 0.3.25 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.28.0 + '@babel/compat-data': 7.27.5 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.4 + browserslist: 4.25.0 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.3)': + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.3)': + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.2.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.3)': + '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.1 @@ -6072,59 +6043,57 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/helper-globals@7.28.0': {} - '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.3)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-wrap-function': 7.28.3 - '@babel/traverse': 7.28.3 + '@babel/helper-wrap-function': 7.27.1 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.3)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 transitivePeerDependencies: - supports-color @@ -6134,629 +6103,613 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helper-wrap-function@7.28.3': + '@babel/helper-wrap-function@7.27.1': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.28.3 - '@babel/types': 7.28.2 + '@babel/traverse': 7.27.4 + '@babel/types': 7.27.6 transitivePeerDependencies: - supports-color - '@babel/helpers@7.28.3': + '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 - '@babel/parser@7.28.3': + '@babel/parser@7.27.5': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.3)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.3)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.3)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.3) - '@babel/traverse': 7.28.3 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.3) + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-block-scoping@7.27.5(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.3)': + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.28.3(@babel/core@7.28.3)': + '@babel/plugin-transform-classes@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-globals': 7.28.0 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) - '@babel/traverse': 7.28.3 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) + '@babel/traverse': 7.27.4 + globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-destructuring@7.27.3(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.3 - transitivePeerDependencies: - - supports-color - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.3)': - dependencies: - '@babel/core': 7.28.3 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.3) - transitivePeerDependencies: - - supports-color - - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.28.3 + '@babel/traverse': 7.27.4 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-object-rest-spread@7.27.3(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.3) - '@babel/traverse': 7.28.3 - transitivePeerDependencies: - - supports-color + '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.27.4) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.3)': + '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-react-display-name@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) - '@babel/types': 7.28.2 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/types': 7.27.6 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.28.3(@babel/core@7.28.3)': + '@babel/plugin-transform-regenerator@7.27.5(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.3)': + '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.3) + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.3)': + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) '@babel/helper-plugin-utils': 7.27.1 - '@babel/preset-env@7.28.3(@babel/core@7.28.3)': + '@babel/preset-env@7.27.2(@babel/core@7.27.4)': dependencies: - '@babel/compat-data': 7.28.0 - '@babel/core': 7.28.3 + '@babel/compat-data': 7.27.5 + '@babel/core': 7.27.4 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.3) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.3) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.3) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-block-scoping': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.3) - '@babel/plugin-transform-classes': 7.28.3(@babel/core@7.28.3) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-object-rest-spread': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.3) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-regenerator': 7.28.3(@babel/core@7.28.3) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.3) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.3) - babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.3) - babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.3) - babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.3) - core-js-compat: 3.45.1 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.4) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.4) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-block-scoping': 7.27.5(@babel/core@7.27.4) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.27.4) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-object-rest-spread': 7.27.3(@babel/core@7.27.4) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-regenerator': 7.27.5(@babel/core@7.27.4) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.4) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.4) + babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.4) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.4) + babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.4) + core-js-compat: 3.43.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.3)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 esutils: 2.0.3 - '@babel/preset-react@7.27.1(@babel/core@7.28.3)': + '@babel/preset-react@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.3) - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.3) + '@babel/plugin-transform-react-display-name': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.27.1(@babel/core@7.28.3)': + '@babel/preset-typescript@7.27.1(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.3) - '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.3) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4) transitivePeerDependencies: - supports-color - '@babel/runtime@7.28.3': {} + '@babel/runtime@7.27.6': {} '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.28.3 - '@babel/types': 7.28.2 + '@babel/parser': 7.27.5 + '@babel/types': 7.27.6 - '@babel/traverse@7.28.3': + '@babel/traverse@7.27.4': dependencies: '@babel/code-frame': 7.27.1 - '@babel/generator': 7.28.3 - '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.28.3 + '@babel/generator': 7.27.5 + '@babel/parser': 7.27.5 '@babel/template': 7.27.2 - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 debug: 4.4.1 + globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.28.2': + '@babel/types@7.27.6': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@base-org/account@1.1.1(@types/react@19.1.12)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/cdp-sdk@1.22.0(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@noble/hashes': 1.4.0 - clsx: 1.2.1 - eventemitter3: 5.0.1 - idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) - preact: 10.24.2 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.3(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)) + '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.8.3)(zod@3.25.67) + axios: 1.10.0 + jose: 6.0.11 + md5: 2.3.0 + uncrypto: 0.1.3 + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + zod: 3.25.67 transitivePeerDependencies: - - '@types/react' - bufferutil - - immer - - react + - debug + - encoding + - fastestsmallesttextencoderdecoder - typescript - - use-sync-external-store - utf-8-validate - - zod - '@coinbase/cdp-sdk@1.36.1(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@coinbase/cdp-sdk@1.38.2(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/spl-token': 0.4.14(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - abitype: 1.0.6(typescript@5.9.2)(zod@3.25.76) - axios: 1.11.0 - axios-retry: 4.5.0(axios@1.11.0) - jose: 6.1.0 + '@solana/spl-token': 0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + abitype: 1.0.6(typescript@5.8.3)(zod@3.25.67) + axios: 1.12.2 + axios-retry: 4.5.0(axios@1.12.2) + jose: 6.0.11 md5: 2.3.0 uncrypto: 0.1.3 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zod: 3.25.76 + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + zod: 3.25.67 transitivePeerDependencies: - bufferutil - debug @@ -6765,21 +6718,21 @@ snapshots: - typescript - utf-8-validate - '@coinbase/onchainkit@0.38.19(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/onchainkit@0.38.15(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(bufferutil@4.0.9)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@farcaster/frame-sdk': 0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/miniapp-wagmi-connector': 1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@tanstack/react-query': 5.85.9(react@19.1.1) - '@wagmi/core': 2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@farcaster/frame-sdk': 0.0.60(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@farcaster/frame-wagmi-connector': 0.0.53(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) + '@tanstack/react-query': 5.81.2(react@19.1.0) + '@wagmi/core': 2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) clsx: 2.1.1 graphql: 16.11.0 graphql-request: 6.1.0(graphql@16.11.0) qrcode: 1.5.4 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) tailwind-merge: 2.6.0 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - wagmi: 2.16.9(@tanstack/query-core@5.85.9)(@tanstack/react-query@5.85.9(react@19.1.1))(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + wagmi: 2.15.6(@tanstack/query-core@5.81.2)(@tanstack/react-query@5.81.2(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -6789,7 +6742,6 @@ snapshots: - '@azure/storage-blob' - '@capacitor/preferences' - '@deno/kv' - - '@farcaster/miniapp-sdk' - '@netlify/blobs' - '@planetscale/database' - '@react-native-async-storage/async-storage' @@ -6797,7 +6749,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -6821,34 +6772,21 @@ snapshots: eth-json-rpc-filters: 6.0.1 eventemitter3: 5.0.1 keccak: 3.0.4 - preact: 10.27.1 - sha.js: 2.4.12 + preact: 10.26.9 + sha.js: 2.4.11 transitivePeerDependencies: - supports-color - '@coinbase/wallet-sdk@4.3.6(@types/react@19.1.12)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76)': + '@coinbase/wallet-sdk@4.3.3': dependencies: - '@noble/hashes': 1.4.0 + '@noble/hashes': 1.8.0 clsx: 1.2.1 eventemitter3: 5.0.1 - idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.2)(zod@3.25.76) - preact: 10.24.2 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.3(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)) - transitivePeerDependencies: - - '@types/react' - - bufferutil - - immer - - react - - typescript - - use-sync-external-store - - utf-8-validate - - zod + preact: 10.26.9 - '@craftamap/esbuild-plugin-html@0.9.0(bufferutil@4.0.9)(esbuild@0.25.9)(utf-8-validate@5.0.10)': + '@craftamap/esbuild-plugin-html@0.9.0(bufferutil@4.0.9)(esbuild@0.25.5)(utf-8-validate@5.0.10)': dependencies: - esbuild: 0.25.9 + esbuild: 0.25.5 jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) lodash: 4.17.21 transitivePeerDependencies: @@ -6857,16 +6795,16 @@ snapshots: - supports-color - utf-8-validate - '@csstools/color-helpers@5.1.0': {} + '@csstools/color-helpers@5.0.2': {} '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 - '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + '@csstools/css-color-parser@3.0.10(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': dependencies: - '@csstools/color-helpers': 5.1.0 + '@csstools/color-helpers': 5.0.2 '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) '@csstools/css-tokenizer': 3.0.4 @@ -6877,22 +6815,22 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} - '@ecies/ciphers@0.2.4(@noble/ciphers@1.3.0)': + '@ecies/ciphers@0.2.3(@noble/ciphers@1.3.0)': dependencies: '@noble/ciphers': 1.3.0 - '@emnapi/core@1.5.0': + '@emnapi/core@1.4.3': dependencies: - '@emnapi/wasi-threads': 1.1.0 + '@emnapi/wasi-threads': 1.0.2 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.5.0': + '@emnapi/runtime@1.4.3': dependencies: tslib: 2.8.1 optional: true - '@emnapi/wasi-threads@1.1.0': + '@emnapi/wasi-threads@1.0.2': dependencies: tslib: 2.8.1 optional: true @@ -6900,97 +6838,94 @@ snapshots: '@es-joy/jsdoccomment@0.50.2': dependencies: '@types/estree': 1.0.8 - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/types': 8.35.0 comment-parser: 1.4.1 esquery: 1.6.0 jsdoc-type-pratt-parser: 4.1.0 - '@esbuild/aix-ppc64@0.25.9': + '@esbuild/aix-ppc64@0.25.5': optional: true - '@esbuild/android-arm64@0.25.9': + '@esbuild/android-arm64@0.25.5': optional: true - '@esbuild/android-arm@0.25.9': + '@esbuild/android-arm@0.25.5': optional: true - '@esbuild/android-x64@0.25.9': + '@esbuild/android-x64@0.25.5': optional: true - '@esbuild/darwin-arm64@0.25.9': + '@esbuild/darwin-arm64@0.25.5': optional: true - '@esbuild/darwin-x64@0.25.9': + '@esbuild/darwin-x64@0.25.5': optional: true - '@esbuild/freebsd-arm64@0.25.9': + '@esbuild/freebsd-arm64@0.25.5': optional: true - '@esbuild/freebsd-x64@0.25.9': + '@esbuild/freebsd-x64@0.25.5': optional: true - '@esbuild/linux-arm64@0.25.9': + '@esbuild/linux-arm64@0.25.5': optional: true - '@esbuild/linux-arm@0.25.9': + '@esbuild/linux-arm@0.25.5': optional: true - '@esbuild/linux-ia32@0.25.9': + '@esbuild/linux-ia32@0.25.5': optional: true - '@esbuild/linux-loong64@0.25.9': + '@esbuild/linux-loong64@0.25.5': optional: true - '@esbuild/linux-mips64el@0.25.9': + '@esbuild/linux-mips64el@0.25.5': optional: true - '@esbuild/linux-ppc64@0.25.9': + '@esbuild/linux-ppc64@0.25.5': optional: true - '@esbuild/linux-riscv64@0.25.9': + '@esbuild/linux-riscv64@0.25.5': optional: true - '@esbuild/linux-s390x@0.25.9': + '@esbuild/linux-s390x@0.25.5': optional: true - '@esbuild/linux-x64@0.25.9': + '@esbuild/linux-x64@0.25.5': optional: true - '@esbuild/netbsd-arm64@0.25.9': + '@esbuild/netbsd-arm64@0.25.5': optional: true - '@esbuild/netbsd-x64@0.25.9': + '@esbuild/netbsd-x64@0.25.5': optional: true - '@esbuild/openbsd-arm64@0.25.9': + '@esbuild/openbsd-arm64@0.25.5': optional: true - '@esbuild/openbsd-x64@0.25.9': + '@esbuild/openbsd-x64@0.25.5': optional: true - '@esbuild/openharmony-arm64@0.25.9': + '@esbuild/sunos-x64@0.25.5': optional: true - '@esbuild/sunos-x64@0.25.9': + '@esbuild/win32-arm64@0.25.5': optional: true - '@esbuild/win32-arm64@0.25.9': + '@esbuild/win32-ia32@0.25.5': optional: true - '@esbuild/win32-ia32@0.25.9': + '@esbuild/win32-x64@0.25.5': optional: true - '@esbuild/win32-x64@0.25.9': - optional: true - - '@eslint-community/eslint-utils@4.8.0(eslint@9.34.0(jiti@1.21.7))': + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0(jiti@1.21.7))': dependencies: - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.1': {} - '@eslint/config-array@0.21.0': + '@eslint/config-array@0.20.1': dependencies: '@eslint/object-schema': 2.1.6 debug: 4.4.1 @@ -6998,9 +6933,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/config-helpers@0.3.1': {} + '@eslint/config-helpers@0.2.3': {} - '@eslint/core@0.15.2': + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.0': dependencies: '@types/json-schema': 7.0.15 @@ -7018,13 +6957,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.34.0': {} + '@eslint/js@9.29.0': {} '@eslint/object-schema@2.1.6': {} - '@eslint/plugin-kit@0.3.5': + '@eslint/plugin-kit@0.3.2': dependencies: - '@eslint/core': 0.15.2 + '@eslint/core': 0.15.0 levn: 0.4.1 '@ethereumjs/common@3.2.0': @@ -7047,38 +6986,24 @@ snapshots: ethereum-cryptography: 2.2.1 micro-ftch: 0.3.1 - '@farcaster/frame-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': - dependencies: - '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@farcaster/quick-auth': 0.0.6(typescript@5.9.2) - comlink: 4.4.2 - eventemitter3: 5.0.1 - ox: 0.4.4(typescript@5.9.2)(zod@3.25.76) - transitivePeerDependencies: - - bufferutil - - encoding - - typescript - - utf-8-validate - - zod - - '@farcaster/miniapp-core@0.3.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@farcaster/frame-core@0.1.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - ox: 0.4.4(typescript@5.9.2)(zod@3.25.76) - zod: 3.25.76 + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + ox: 0.4.4(typescript@5.8.3)(zod@3.25.67) + zod: 3.25.67 transitivePeerDependencies: - bufferutil - encoding - typescript - utf-8-validate - '@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@farcaster/miniapp-core': 0.3.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@farcaster/quick-auth': 0.0.6(typescript@5.9.2) + '@farcaster/frame-core': 0.1.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@farcaster/quick-auth': 0.0.5(typescript@5.8.3) comlink: 4.4.2 eventemitter3: 5.0.1 - ox: 0.4.4(typescript@5.9.2)(zod@3.25.76) + ox: 0.4.4(typescript@5.8.3)(zod@3.25.67) transitivePeerDependencies: - bufferutil - encoding @@ -7086,133 +7011,122 @@ snapshots: - utf-8-validate - zod - '@farcaster/miniapp-wagmi-connector@1.0.0(@farcaster/miniapp-sdk@0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@farcaster/frame-wagmi-connector@0.0.53(@farcaster/frame-sdk@0.0.60(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))': dependencies: - '@farcaster/miniapp-sdk': 0.1.9(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@farcaster/frame-sdk': 0.0.60(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@wagmi/core': 2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) - '@farcaster/quick-auth@0.0.6(typescript@5.9.2)': + '@farcaster/quick-auth@0.0.5(typescript@5.8.3)': dependencies: jose: 5.10.0 - typescript: 5.9.2 - zod: 3.25.76 - - '@gemini-wallet/core@0.2.0(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': - dependencies: - '@metamask/rpc-errors': 7.0.2 - eventemitter3: 5.0.1 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - transitivePeerDependencies: - - supports-color + typescript: 5.8.3 + zod: 3.25.67 '@graphql-typed-document-node/core@3.2.0(graphql@16.11.0)': dependencies: graphql: 16.11.0 - '@heroicons/react@2.2.0(react@19.1.1)': + '@heroicons/react@2.2.0(react@19.1.0)': dependencies: - react: 19.1.1 + react: 19.1.0 - '@hono/node-server@1.19.1(hono@4.9.6)': + '@hono/node-server@1.14.4(hono@4.8.2)': dependencies: - hono: 4.9.6 + hono: 4.8.2 '@humanfs/core@0.19.1': {} - '@humanfs/node@0.16.7': + '@humanfs/node@0.16.6': dependencies: '@humanfs/core': 0.19.1 - '@humanwhocodes/retry': 0.4.3 + '@humanwhocodes/retry': 0.3.1 '@humanwhocodes/module-importer@1.0.1': {} + '@humanwhocodes/retry@0.3.1': {} + '@humanwhocodes/retry@0.4.3': {} - '@img/sharp-darwin-arm64@0.34.3': + '@img/sharp-darwin-arm64@0.34.2': optionalDependencies: - '@img/sharp-libvips-darwin-arm64': 1.2.0 + '@img/sharp-libvips-darwin-arm64': 1.1.0 optional: true - '@img/sharp-darwin-x64@0.34.3': + '@img/sharp-darwin-x64@0.34.2': optionalDependencies: - '@img/sharp-libvips-darwin-x64': 1.2.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 optional: true - '@img/sharp-libvips-darwin-arm64@1.2.0': + '@img/sharp-libvips-darwin-arm64@1.1.0': optional: true - '@img/sharp-libvips-darwin-x64@1.2.0': + '@img/sharp-libvips-darwin-x64@1.1.0': optional: true - '@img/sharp-libvips-linux-arm64@1.2.0': + '@img/sharp-libvips-linux-arm64@1.1.0': optional: true - '@img/sharp-libvips-linux-arm@1.2.0': + '@img/sharp-libvips-linux-arm@1.1.0': optional: true - '@img/sharp-libvips-linux-ppc64@1.2.0': + '@img/sharp-libvips-linux-ppc64@1.1.0': optional: true - '@img/sharp-libvips-linux-s390x@1.2.0': + '@img/sharp-libvips-linux-s390x@1.1.0': optional: true - '@img/sharp-libvips-linux-x64@1.2.0': + '@img/sharp-libvips-linux-x64@1.1.0': optional: true - '@img/sharp-libvips-linuxmusl-arm64@1.2.0': + '@img/sharp-libvips-linuxmusl-arm64@1.1.0': optional: true - '@img/sharp-libvips-linuxmusl-x64@1.2.0': + '@img/sharp-libvips-linuxmusl-x64@1.1.0': optional: true - '@img/sharp-linux-arm64@0.34.3': + '@img/sharp-linux-arm64@0.34.2': optionalDependencies: - '@img/sharp-libvips-linux-arm64': 1.2.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 optional: true - '@img/sharp-linux-arm@0.34.3': + '@img/sharp-linux-arm@0.34.2': optionalDependencies: - '@img/sharp-libvips-linux-arm': 1.2.0 + '@img/sharp-libvips-linux-arm': 1.1.0 optional: true - '@img/sharp-linux-ppc64@0.34.3': + '@img/sharp-linux-s390x@0.34.2': optionalDependencies: - '@img/sharp-libvips-linux-ppc64': 1.2.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 optional: true - '@img/sharp-linux-s390x@0.34.3': + '@img/sharp-linux-x64@0.34.2': optionalDependencies: - '@img/sharp-libvips-linux-s390x': 1.2.0 + '@img/sharp-libvips-linux-x64': 1.1.0 optional: true - '@img/sharp-linux-x64@0.34.3': + '@img/sharp-linuxmusl-arm64@0.34.2': optionalDependencies: - '@img/sharp-libvips-linux-x64': 1.2.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 optional: true - '@img/sharp-linuxmusl-arm64@0.34.3': + '@img/sharp-linuxmusl-x64@0.34.2': optionalDependencies: - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 optional: true - '@img/sharp-linuxmusl-x64@0.34.3': - optionalDependencies: - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - optional: true - - '@img/sharp-wasm32@0.34.3': + '@img/sharp-wasm32@0.34.2': dependencies: - '@emnapi/runtime': 1.5.0 + '@emnapi/runtime': 1.4.3 optional: true - '@img/sharp-win32-arm64@0.34.3': + '@img/sharp-win32-arm64@0.34.2': optional: true - '@img/sharp-win32-ia32@0.34.3': + '@img/sharp-win32-ia32@0.34.2': optional: true - '@img/sharp-win32-x64@0.34.3': + '@img/sharp-win32-x64@0.34.2': optional: true '@isaacs/cliui@8.0.2': @@ -7224,25 +7138,28 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 - '@jridgewell/gen-mapping@0.3.13': + '@jridgewell/gen-mapping@0.3.8': dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/set-array@1.2.1': {} - '@jridgewell/trace-mapping@0.3.30': + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.5.0 - '@lit-labs/ssr-dom-shim@1.4.0': {} + '@lit-labs/ssr-dom-shim@1.3.0': {} - '@lit/reactive-element@2.1.1': + '@lit/reactive-element@2.1.0': dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 + '@lit-labs/ssr-dom-shim': 1.3.0 '@metamask/eth-json-rpc-provider@1.0.1': dependencies: @@ -7284,7 +7201,7 @@ snapshots: '@metamask/onboarding@1.0.1': dependencies: - bowser: 2.12.1 + bowser: 2.11.0 '@metamask/providers@16.1.0': dependencies: @@ -7310,13 +7227,6 @@ snapshots: transitivePeerDependencies: - supports-color - '@metamask/rpc-errors@7.0.2': - dependencies: - '@metamask/utils': 11.7.0 - fast-safe-stringify: 2.1.1 - transitivePeerDependencies: - - supports-color - '@metamask/safe-event-emitter@2.0.0': {} '@metamask/safe-event-emitter@3.1.2': {} @@ -7342,13 +7252,13 @@ snapshots: '@metamask/sdk@0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.27.6 '@metamask/onboarding': 1.0.1 '@metamask/providers': 16.1.0 '@metamask/sdk-communication-layer': 0.32.0(cross-fetch@4.1.0)(eciesjs@0.4.15)(eventemitter2@6.4.9)(readable-stream@3.6.2)(socket.io-client@4.8.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@metamask/sdk-install-modal-web': 0.32.0 '@paulmillr/qr': 0.2.1 - bowser: 2.12.1 + bowser: 2.11.0 cross-fetch: 4.1.0 debug: 4.4.1 eciesjs: 0.4.15 @@ -7369,22 +7279,6 @@ snapshots: '@metamask/superstruct@3.2.1': {} - '@metamask/utils@11.7.0': - dependencies: - '@ethereumjs/tx': 4.2.0 - '@metamask/superstruct': 3.2.1 - '@noble/hashes': 1.8.0 - '@scure/base': 1.2.6 - '@types/debug': 4.1.12 - '@types/lodash': 4.17.20 - debug: 4.4.1 - lodash: 4.17.21 - pony-cause: 2.1.11 - semver: 7.7.2 - uuid: 9.0.1 - transitivePeerDependencies: - - supports-color - '@metamask/utils@5.0.2': dependencies: '@ethereumjs/tx': 4.2.0 @@ -7423,41 +7317,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@napi-rs/wasm-runtime@0.2.12': + '@napi-rs/wasm-runtime@0.2.11': dependencies: - '@emnapi/core': 1.5.0 - '@emnapi/runtime': 1.5.0 - '@tybys/wasm-util': 0.10.0 + '@emnapi/core': 1.4.3 + '@emnapi/runtime': 1.4.3 + '@tybys/wasm-util': 0.9.0 optional: true - '@next/env@15.5.2': {} + '@next/env@15.3.4': {} '@next/eslint-plugin-next@15.1.7': dependencies: fast-glob: 3.3.1 - '@next/swc-darwin-arm64@15.5.2': + '@next/swc-darwin-arm64@15.3.4': optional: true - '@next/swc-darwin-x64@15.5.2': + '@next/swc-darwin-x64@15.3.4': optional: true - '@next/swc-linux-arm64-gnu@15.5.2': + '@next/swc-linux-arm64-gnu@15.3.4': optional: true - '@next/swc-linux-arm64-musl@15.5.2': + '@next/swc-linux-arm64-musl@15.3.4': optional: true - '@next/swc-linux-x64-gnu@15.5.2': + '@next/swc-linux-x64-gnu@15.3.4': optional: true - '@next/swc-linux-x64-musl@15.5.2': + '@next/swc-linux-x64-musl@15.3.4': optional: true - '@next/swc-win32-arm64-msvc@15.5.2': + '@next/swc-win32-arm64-msvc@15.3.4': optional: true - '@next/swc-win32-x64-msvc@15.5.2': + '@next/swc-win32-x64-msvc@15.3.4': optional: true '@noble/ciphers@1.2.1': {} @@ -7476,11 +7370,7 @@ snapshots: dependencies: '@noble/hashes': 1.7.1 - '@noble/curves@1.9.1': - dependencies: - '@noble/hashes': 1.8.0 - - '@noble/curves@1.9.7': + '@noble/curves@1.9.2': dependencies: '@noble/hashes': 1.8.0 @@ -7511,37 +7401,37 @@ snapshots: '@pkgjs/parseargs@0.11.0': optional: true - '@pkgr/core@0.2.9': {} + '@pkgr/core@0.2.7': {} - '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4)': + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-common@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: big.js: 6.2.2 dayjs: 1.11.13 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - bufferutil - typescript - utf-8-validate - zod - '@reown/appkit-controllers@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-controllers@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/universal-provider': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 1.13.2(@types/react@19.1.12)(react@19.1.1) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -7557,7 +7447,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7570,14 +7459,14 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-pay@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-pay@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67) lit: 3.3.0 - valtio: 1.13.2(@types/react@19.1.12)(react@19.1.1) + valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -7593,7 +7482,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7610,13 +7498,13 @@ snapshots: dependencies: buffer: 6.0.3 - '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76)': + '@reown/appkit-scaffold-ui@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) lit: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -7633,7 +7521,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7647,11 +7534,11 @@ snapshots: - valtio - zod - '@reown/appkit-ui@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit-ui@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) lit: 3.3.0 qrcode: 1.5.3 transitivePeerDependencies: @@ -7669,7 +7556,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7682,16 +7568,16 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-utils@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76)': + '@reown/appkit-utils@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) '@walletconnect/logger': 2.1.2 - '@walletconnect/universal-provider': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - valtio: 1.13.2(@types/react@19.1.12)(react@19.1.1) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -7707,7 +7593,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7720,9 +7605,9 @@ snapshots: - utf-8-validate - zod - '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@reown/appkit-wallet@1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4) '@reown/appkit-polyfills': 1.7.8 '@walletconnect/logger': 2.1.2 zod: 3.22.4 @@ -7731,21 +7616,21 @@ snapshots: - typescript - utf-8-validate - '@reown/appkit@1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@reown/appkit@1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-controllers': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-pay': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-common': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-controllers': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-pay': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@reown/appkit-polyfills': 1.7.8 - '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-ui': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@reown/appkit-utils': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1))(zod@3.25.76) - '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@walletconnect/types': 2.21.0(@vercel/functions@2.2.13) - '@walletconnect/universal-provider': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit-scaffold-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67) + '@reown/appkit-ui': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@reown/appkit-utils': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0))(zod@3.25.67) + '@reown/appkit-wallet': 1.7.8(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@walletconnect/types': 2.21.0 + '@walletconnect/universal-provider': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) bs58: 6.0.0 - valtio: 1.13.2(@types/react@19.1.12)(react@19.1.1) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -7761,7 +7646,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -7774,76 +7658,73 @@ snapshots: - utf-8-validate - zod - '@rollup/rollup-android-arm-eabi@4.50.0': + '@rollup/rollup-android-arm-eabi@4.44.0': optional: true - '@rollup/rollup-android-arm64@4.50.0': + '@rollup/rollup-android-arm64@4.44.0': optional: true - '@rollup/rollup-darwin-arm64@4.50.0': + '@rollup/rollup-darwin-arm64@4.44.0': optional: true - '@rollup/rollup-darwin-x64@4.50.0': + '@rollup/rollup-darwin-x64@4.44.0': optional: true - '@rollup/rollup-freebsd-arm64@4.50.0': + '@rollup/rollup-freebsd-arm64@4.44.0': optional: true - '@rollup/rollup-freebsd-x64@4.50.0': + '@rollup/rollup-freebsd-x64@4.44.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.50.0': + '@rollup/rollup-linux-arm-gnueabihf@4.44.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.50.0': + '@rollup/rollup-linux-arm-musleabihf@4.44.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.50.0': + '@rollup/rollup-linux-arm64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.50.0': + '@rollup/rollup-linux-arm64-musl@4.44.0': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.50.0': + '@rollup/rollup-linux-loongarch64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.50.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.50.0': + '@rollup/rollup-linux-riscv64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-riscv64-musl@4.50.0': + '@rollup/rollup-linux-riscv64-musl@4.44.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.50.0': + '@rollup/rollup-linux-s390x-gnu@4.44.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.50.0': + '@rollup/rollup-linux-x64-gnu@4.44.0': optional: true - '@rollup/rollup-linux-x64-musl@4.50.0': + '@rollup/rollup-linux-x64-musl@4.44.0': optional: true - '@rollup/rollup-openharmony-arm64@4.50.0': + '@rollup/rollup-win32-arm64-msvc@4.44.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.50.0': + '@rollup/rollup-win32-ia32-msvc@4.44.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.50.0': - optional: true - - '@rollup/rollup-win32-x64-msvc@4.50.0': + '@rollup/rollup-win32-x64-msvc@4.44.0': optional: true '@rtsao/scc@1.1.0': {} - '@rushstack/eslint-patch@1.12.0': {} + '@rushstack/eslint-patch@1.11.0': {} - '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-provider@0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) events: 3.3.0 transitivePeerDependencies: - bufferutil @@ -7851,10 +7732,10 @@ snapshots: - utf-8-validate - zod - '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@safe-global/safe-apps-sdk@9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: '@safe-global/safe-gateway-typescript-sdk': 3.23.1 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - bufferutil - typescript @@ -7881,7 +7762,7 @@ snapshots: '@scure/bip32@1.7.0': dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/base': 1.2.6 @@ -7902,53 +7783,53 @@ snapshots: '@socket.io/component-emitter@3.1.2': {} - '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/compute-budget@0.8.0(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2))': + '@solana-program/token-2022@0.4.2(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))(@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) - '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': + '@solana-program/token@0.5.1(@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)))': dependencies: - '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/kit': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/accounts@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/addresses@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/assertions': 2.3.0(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/assertions': 2.3.0(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/assertions@2.3.0(typescript@5.9.2)': + '@solana/assertions@2.3.0(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@solana/buffer-layout-utils@0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) bigint-buffer: 1.1.5 - bignumber.js: 9.3.1 + bignumber.js: 9.3.0 transitivePeerDependencies: - bufferutil - encoding @@ -7959,394 +7840,463 @@ snapshots: dependencies: buffer: 6.0.3 - '@solana/codecs-core@2.0.0-rc.1(typescript@5.9.2)': + '@solana/codecs-core@2.0.0-rc.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/codecs-core@2.1.1(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/codecs-core@2.3.0(typescript@5.8.3)': dependencies: - '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-core@2.3.0(typescript@5.9.2)': + '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-data-structures@2.0.0-rc.1(typescript@5.9.2)': + '@solana/codecs-data-structures@2.3.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.2) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-data-structures@2.3.0(typescript@5.9.2)': + '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-numbers@2.0.0-rc.1(typescript@5.9.2)': + '@solana/codecs-numbers@2.1.1(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.1.1(typescript@5.8.3) + '@solana/errors': 2.1.1(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-numbers@2.3.0(typescript@5.9.2)': + '@solana/codecs-numbers@2.3.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/codecs-strings@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.2) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/codecs-strings@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) fastestsmallesttextencoderdecoder: 1.0.22 - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/codecs@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/options': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/codecs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/options': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/errors@2.0.0-rc.1(typescript@5.9.2)': + '@solana/errors@2.0.0-rc.1(typescript@5.8.3)': dependencies: - chalk: 5.6.0 + chalk: 5.4.1 commander: 12.1.0 - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/errors@2.3.0(typescript@5.9.2)': + '@solana/errors@2.1.1(typescript@5.8.3)': dependencies: - chalk: 5.6.0 - commander: 14.0.0 - typescript: 5.9.2 + chalk: 5.4.1 + commander: 13.1.0 + typescript: 5.8.3 - '@solana/fast-stable-stringify@2.3.0(typescript@5.9.2)': + '@solana/errors@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + chalk: 5.4.1 + commander: 14.0.1 + typescript: 5.8.3 - '@solana/functional@2.3.0(typescript@5.9.2)': + '@solana/fast-stable-stringify@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/instructions@2.3.0(typescript@5.9.2)': + '@solana/functional@2.3.0(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/instructions@2.3.0(typescript@5.8.3)': dependencies: - '@solana/assertions': 2.3.0(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/keys@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/assertions': 2.3.0(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/nominal-types@2.3.0(typescript@5.9.2)': + '@solana/kit@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/programs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/signers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/sysvars': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-confirmation': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + + '@solana/nominal-types@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/options@2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.9.2) - '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.0.0-rc.1(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-data-structures': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-numbers': 2.0.0-rc.1(typescript@5.8.3) + '@solana/codecs-strings': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.0.0-rc.1(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/options@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/programs@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/promises@2.3.0(typescript@5.9.2)': + '@solana/promises@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/rpc-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-parsed-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/rpc-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-parsed-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-parsed-types@2.3.0(typescript@5.9.2)': + '@solana/rpc-parsed-types@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/rpc-spec-types@2.3.0(typescript@5.9.2)': + '@solana/rpc-spec-types@2.3.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@solana/rpc-spec@2.3.0(typescript@5.9.2)': + '@solana/rpc-spec@2.3.0(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/rpc-subscriptions-api@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) - - '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.9.2)': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 - - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/rpc-subscriptions-channel-websocket@2.3.0(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + + '@solana/rpc-subscriptions-spec@2.3.0(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/subscribable': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + - ws + + '@solana/rpc-subscriptions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-subscriptions-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions-channel-websocket': 2.3.0(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-subscriptions-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/subscribable': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/rpc-transformers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc-transport-http@2.3.0(typescript@5.9.2)': + '@solana/rpc-transport-http@2.3.0(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 - undici-types: 7.15.0 + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 + undici-types: 7.16.0 - '@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/rpc-types@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/fast-stable-stringify': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/rpc-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-spec': 2.3.0(typescript@5.9.2) - '@solana/rpc-spec-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-transport-http': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/rpc@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/fast-stable-stringify': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/rpc-api': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-spec': 2.3.0(typescript@5.8.3) + '@solana/rpc-spec-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-transformers': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-transport-http': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/signers@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/spl-token-group@0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/spl-token-metadata@0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@solana/codecs': 2.0.0-rc.1(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) transitivePeerDependencies: - fastestsmallesttextencoderdecoder - typescript - '@solana/spl-token@0.4.14(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@solana/spl-token@0.4.13(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(bufferutil@4.0.9)(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: '@solana/buffer-layout': 4.0.1 - '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) - '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/web3.js': 1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10) + '@solana/buffer-layout-utils': 0.2.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) + '@solana/spl-token-group': 0.0.7(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/spl-token-metadata': 0.1.6(@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10))(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/web3.js': 1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10) buffer: 6.0.3 transitivePeerDependencies: - bufferutil @@ -8355,95 +8305,112 @@ snapshots: - typescript - utf-8-validate - '@solana/subscribable@2.3.0(typescript@5.9.2)': + '@solana/subscribable@2.3.0(typescript@5.8.3)': dependencies: - '@solana/errors': 2.3.0(typescript@5.9.2) - typescript: 5.9.2 + '@solana/errors': 2.3.0(typescript@5.8.3) + typescript: 5.8.3 - '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': + '@solana/sysvars@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': dependencies: - '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/accounts': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/promises': 2.3.0(typescript@5.9.2) - '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - ws - '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/transaction-confirmation@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10))': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/promises': 2.3.0(typescript@5.8.3) + '@solana/rpc': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/rpc-subscriptions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transactions': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder + - ws - '@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2)': - dependencies: - '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/codecs-core': 2.3.0(typescript@5.9.2) - '@solana/codecs-data-structures': 2.3.0(typescript@5.9.2) - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) - '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/errors': 2.3.0(typescript@5.9.2) - '@solana/functional': 2.3.0(typescript@5.9.2) - '@solana/instructions': 2.3.0(typescript@5.9.2) - '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/nominal-types': 2.3.0(typescript@5.9.2) - '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.9.2) - typescript: 5.9.2 + '@solana/transaction-messages@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - fastestsmallesttextencoderdecoder - '@solana/web3.js@1.98.4(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)': + '@solana/transactions@2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3)': + dependencies: + '@solana/addresses': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/codecs-core': 2.3.0(typescript@5.8.3) + '@solana/codecs-data-structures': 2.3.0(typescript@5.8.3) + '@solana/codecs-numbers': 2.3.0(typescript@5.8.3) + '@solana/codecs-strings': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/errors': 2.3.0(typescript@5.8.3) + '@solana/functional': 2.3.0(typescript@5.8.3) + '@solana/instructions': 2.3.0(typescript@5.8.3) + '@solana/keys': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/nominal-types': 2.3.0(typescript@5.8.3) + '@solana/rpc-types': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + '@solana/transaction-messages': 2.3.0(fastestsmallesttextencoderdecoder@1.0.22)(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - fastestsmallesttextencoderdecoder + + '@solana/web3.js@1.98.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)': dependencies: - '@babel/runtime': 7.28.3 - '@noble/curves': 1.9.7 + '@babel/runtime': 7.27.6 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@solana/buffer-layout': 4.0.1 - '@solana/codecs-numbers': 2.3.0(typescript@5.9.2) + '@solana/codecs-numbers': 2.1.1(typescript@5.8.3) agentkeepalive: 4.6.0 bn.js: 5.2.2 borsh: 0.7.0 @@ -8452,7 +8419,7 @@ snapshots: fast-stable-stringify: 1.0.0 jayson: 4.2.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) node-fetch: 2.7.0 - rpc-websockets: 9.1.3 + rpc-websockets: 9.1.1 superstruct: 2.0.2 transitivePeerDependencies: - bufferutil @@ -8460,56 +8427,56 @@ snapshots: - typescript - utf-8-validate - '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.3)': + '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 - '@svgr/babel-preset@8.1.0(@babel/core@7.28.3)': + '@svgr/babel-preset@8.1.0(@babel/core@7.27.4)': dependencies: - '@babel/core': 7.28.3 - '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.3) - '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.3) - '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.27.4) + '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.27.4) + '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.27.4) - '@svgr/core@8.1.0(typescript@5.9.2)': + '@svgr/core@8.1.0(typescript@5.8.3)': dependencies: - '@babel/core': 7.28.3 - '@svgr/babel-preset': 8.1.0(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.27.4) camelcase: 6.3.0 - cosmiconfig: 8.3.6(typescript@5.9.2) + cosmiconfig: 8.3.6(typescript@5.8.3) snake-case: 3.0.4 transitivePeerDependencies: - supports-color @@ -8517,42 +8484,44 @@ snapshots: '@svgr/hast-util-to-babel-ast@8.0.0': dependencies: - '@babel/types': 7.28.2 + '@babel/types': 7.27.6 entities: 4.5.0 - '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))': + '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))': dependencies: - '@babel/core': 7.28.3 - '@svgr/babel-preset': 8.1.0(@babel/core@7.28.3) - '@svgr/core': 8.1.0(typescript@5.9.2) + '@babel/core': 7.27.4 + '@svgr/babel-preset': 8.1.0(@babel/core@7.27.4) + '@svgr/core': 8.1.0(typescript@5.8.3) '@svgr/hast-util-to-babel-ast': 8.0.0 svg-parser: 2.0.4 transitivePeerDependencies: - supports-color - '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)': + '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.8.3))(typescript@5.8.3)': dependencies: - '@svgr/core': 8.1.0(typescript@5.9.2) - cosmiconfig: 8.3.6(typescript@5.9.2) + '@svgr/core': 8.1.0(typescript@5.8.3) + cosmiconfig: 8.3.6(typescript@5.8.3) deepmerge: 4.3.1 svgo: 3.3.2 transitivePeerDependencies: - typescript - '@svgr/webpack@8.1.0(typescript@5.9.2)': + '@svgr/webpack@8.1.0(typescript@5.8.3)': dependencies: - '@babel/core': 7.28.3 - '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.28.3) - '@babel/preset-env': 7.28.3(@babel/core@7.28.3) - '@babel/preset-react': 7.27.1(@babel/core@7.28.3) - '@babel/preset-typescript': 7.27.1(@babel/core@7.28.3) - '@svgr/core': 8.1.0(typescript@5.9.2) - '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2)) - '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2) + '@babel/core': 7.27.4 + '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.27.4) + '@babel/preset-env': 7.27.2(@babel/core@7.27.4) + '@babel/preset-react': 7.27.1(@babel/core@7.27.4) + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.4) + '@svgr/core': 8.1.0(typescript@5.8.3) + '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3)) + '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.8.3))(typescript@5.8.3) transitivePeerDependencies: - supports-color - typescript + '@swc/counter@0.1.3': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -8561,16 +8530,16 @@ snapshots: dependencies: tslib: 2.8.1 - '@tanstack/query-core@5.85.9': {} + '@tanstack/query-core@5.81.2': {} - '@tanstack/react-query@5.85.9(react@19.1.1)': + '@tanstack/react-query@5.81.2(react@19.1.0)': dependencies: - '@tanstack/query-core': 5.85.9 - react: 19.1.1 + '@tanstack/query-core': 5.81.2 + react: 19.1.0 '@trysound/sax@0.2.0': {} - '@tybys/wasm-util@0.10.0': + '@tybys/wasm-util@0.9.0': dependencies: tslib: 2.8.1 optional: true @@ -8578,7 +8547,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/chai@5.2.2': dependencies: @@ -8586,7 +8555,7 @@ snapshots: '@types/connect@3.4.38': dependencies: - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/debug@4.1.12': dependencies: @@ -8596,9 +8565,9 @@ snapshots: '@types/estree@1.0.8': {} - '@types/express-serve-static-core@5.0.7': + '@types/express-serve-static-core@5.0.6': dependencies: - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -8606,7 +8575,7 @@ snapshots: '@types/express@5.0.3': dependencies: '@types/body-parser': 1.19.6 - '@types/express-serve-static-core': 5.0.7 + '@types/express-serve-static-core': 5.0.6 '@types/serve-static': 1.15.8 '@types/http-errors@2.0.5': {} @@ -8615,19 +8584,17 @@ snapshots: '@types/json5@0.0.29': {} - '@types/lodash@4.17.20': {} - '@types/mime@1.3.5': {} '@types/ms@2.1.0': {} '@types/node@12.20.55': {} - '@types/node@20.19.12': + '@types/node@20.19.1': dependencies: undici-types: 6.21.0 - '@types/node@22.18.0': + '@types/node@22.15.33': dependencies: undici-types: 6.21.0 @@ -8635,23 +8602,23 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-dom@19.1.9(@types/react@19.1.12)': + '@types/react-dom@19.1.6(@types/react@19.1.8)': dependencies: - '@types/react': 19.1.12 + '@types/react': 19.1.8 - '@types/react@19.1.12': + '@types/react@19.1.8': dependencies: csstype: 3.1.3 '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/send': 0.17.5 '@types/trusted-types@2.0.7': {} @@ -8660,162 +8627,161 @@ snapshots: '@types/ws@7.4.7': dependencies: - '@types/node': 22.18.0 + '@types/node': 22.15.33 '@types/ws@8.18.1': dependencies: - '@types/node': 22.18.0 + '@types/node': 22.15.33 - '@typescript-eslint/eslint-plugin@8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.1 - '@typescript-eslint/parser': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/type-utils': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/utils': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.42.0 - eslint: 9.34.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + eslint: 9.29.0(jiti@1.21.7) graphemer: 1.4.0 ignore: 7.0.5 natural-compare: 1.4.0 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 debug: 4.4.1 - eslint: 9.34.0(jiti@1.21.7) - typescript: 5.9.2 + eslint: 9.29.0(jiti@1.21.7) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.42.0(typescript@5.9.2)': + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 debug: 4.4.1 - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.42.0': + '@typescript-eslint/scope-manager@8.35.0': dependencies: - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 - '@typescript-eslint/tsconfig-utils@8.42.0(typescript@5.9.2)': + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': dependencies: - typescript: 5.9.2 + typescript: 5.8.3 - '@typescript-eslint/type-utils@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - '@typescript-eslint/utils': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) debug: 4.4.1 - eslint: 9.34.0(jiti@1.21.7) - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + eslint: 9.29.0(jiti@1.21.7) + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.42.0': {} + '@typescript-eslint/types@8.35.0': {} - '@typescript-eslint/typescript-estree@8.42.0(typescript@5.9.2)': + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': dependencies: - '@typescript-eslint/project-service': 8.42.0(typescript@5.9.2) - '@typescript-eslint/tsconfig-utils': 8.42.0(typescript@5.9.2) - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/visitor-keys': 8.42.0 + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 debug: 4.4.1 fast-glob: 3.3.3 is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.7.2 - ts-api-utils: 2.1.0(typescript@5.9.2) - typescript: 5.9.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2)': + '@typescript-eslint/utils@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.34.0(jiti@1.21.7)) - '@typescript-eslint/scope-manager': 8.42.0 - '@typescript-eslint/types': 8.42.0 - '@typescript-eslint/typescript-estree': 8.42.0(typescript@5.9.2) - eslint: 9.34.0(jiti@1.21.7) - typescript: 5.9.2 + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@1.21.7)) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 9.29.0(jiti@1.21.7) + typescript: 5.8.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.42.0': + '@typescript-eslint/visitor-keys@8.35.0': dependencies: - '@typescript-eslint/types': 8.42.0 + '@typescript-eslint/types': 8.35.0 eslint-visitor-keys: 4.2.1 - '@unrs/resolver-binding-android-arm-eabi@1.11.1': + '@unrs/resolver-binding-android-arm-eabi@1.9.2': optional: true - '@unrs/resolver-binding-android-arm64@1.11.1': + '@unrs/resolver-binding-android-arm64@1.9.2': optional: true - '@unrs/resolver-binding-darwin-arm64@1.11.1': + '@unrs/resolver-binding-darwin-arm64@1.9.2': optional: true - '@unrs/resolver-binding-darwin-x64@1.11.1': + '@unrs/resolver-binding-darwin-x64@1.9.2': optional: true - '@unrs/resolver-binding-freebsd-x64@1.11.1': + '@unrs/resolver-binding-freebsd-x64@1.9.2': optional: true - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + '@unrs/resolver-binding-linux-arm-gnueabihf@1.9.2': optional: true - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + '@unrs/resolver-binding-linux-arm-musleabihf@1.9.2': optional: true - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + '@unrs/resolver-binding-linux-arm64-gnu@1.9.2': optional: true - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + '@unrs/resolver-binding-linux-arm64-musl@1.9.2': optional: true - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + '@unrs/resolver-binding-linux-ppc64-gnu@1.9.2': optional: true - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + '@unrs/resolver-binding-linux-riscv64-gnu@1.9.2': optional: true - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + '@unrs/resolver-binding-linux-riscv64-musl@1.9.2': optional: true - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + '@unrs/resolver-binding-linux-s390x-gnu@1.9.2': optional: true - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + '@unrs/resolver-binding-linux-x64-gnu@1.9.2': optional: true - '@unrs/resolver-binding-linux-x64-musl@1.11.1': + '@unrs/resolver-binding-linux-x64-musl@1.9.2': optional: true - '@unrs/resolver-binding-wasm32-wasi@1.11.1': + '@unrs/resolver-binding-wasm32-wasi@1.9.2': dependencies: - '@napi-rs/wasm-runtime': 0.2.12 + '@napi-rs/wasm-runtime': 0.2.11 optional: true - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + '@unrs/resolver-binding-win32-arm64-msvc@1.9.2': optional: true - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + '@unrs/resolver-binding-win32-ia32-msvc@1.9.2': optional: true - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + '@unrs/resolver-binding-win32-x64-msvc@1.9.2': optional: true '@vercel/functions@2.2.13': @@ -8832,16 +8798,16 @@ snapshots: '@types/chai': 5.2.2 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.3.3 + chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1))': + '@vitest/mocker@3.2.4(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 - magic-string: 0.30.18 + magic-string: 0.30.17 optionalDependencies: - vite: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + vite: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -8856,7 +8822,7 @@ snapshots: '@vitest/snapshot@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - magic-string: 0.30.18 + magic-string: 0.30.17 pathe: 2.0.3 '@vitest/spy@3.2.4': @@ -8866,23 +8832,21 @@ snapshots: '@vitest/utils@3.2.4': dependencies: '@vitest/pretty-format': 3.2.4 - loupe: 3.2.1 + loupe: 3.1.4 tinyrainbow: 2.0.0 - '@wagmi/connectors@5.9.9(@types/react@19.1.12)(@vercel/functions@2.2.13)(@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76)': + '@wagmi/connectors@5.8.5(@types/react@19.1.8)(@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67)': dependencies: - '@base-org/account': 1.1.1(@types/react@19.1.12)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@coinbase/wallet-sdk': 4.3.6(@types/react@19.1.12)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(zod@3.25.76) - '@gemini-wallet/core': 0.2.0(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) + '@coinbase/wallet-sdk': 4.3.3 '@metamask/sdk': 0.32.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@wagmi/core': 2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@safe-global/safe-apps-provider': 0.18.6(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@safe-global/safe-apps-sdk': 9.1.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@wagmi/core': 2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) + '@walletconnect/ethereum-provider': 2.21.1(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) cbw-sdk: '@coinbase/wallet-sdk@3.9.3' - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -8898,51 +8862,48 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil - db0 - encoding - - immer - ioredis - react - supports-color - uploadthing - - use-sync-external-store - utf-8-validate - zod - '@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))': + '@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))': dependencies: eventemitter3: 5.0.1 - mipd: 0.0.7(typescript@5.9.2) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zustand: 5.0.0(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)) + mipd: 0.0.7(typescript@5.8.3) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + zustand: 5.0.0(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)) optionalDependencies: - '@tanstack/query-core': 5.85.9 - typescript: 5.9.2 + '@tanstack/query-core': 5.81.2 + typescript: 5.8.3 transitivePeerDependencies: - '@types/react' - immer - react - use-sync-external-store - '@walletconnect/core@2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@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(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -8961,7 +8922,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -8972,21 +8932,21 @@ snapshots: - utf-8-validate - zod - '@walletconnect/core@2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/core@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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.0.9)(utf-8-validate@5.0.10) - '@walletconnect/keyvaluestorage': 1.1.1(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@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(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@walletconnect/window-getters': 1.0.1 es-toolkit: 1.33.0 events: 3.3.0 @@ -9005,7 +8965,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9020,18 +8979,18 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/ethereum-provider@2.21.1(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@reown/appkit': 1.7.8(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@reown/appkit': 1.7.8(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@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(@vercel/functions@2.2.13) - '@walletconnect/sign-client': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.1(@vercel/functions@2.2.13) - '@walletconnect/universal-provider': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/utils': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/keyvaluestorage': 1.1.1 + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@walletconnect/types': 2.21.1 + '@walletconnect/universal-provider': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -9048,7 +9007,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9108,11 +9066,11 @@ snapshots: - bufferutil - utf-8-validate - '@walletconnect/keyvaluestorage@1.1.1(@vercel/functions@2.2.13)': + '@walletconnect/keyvaluestorage@1.1.1': dependencies: '@walletconnect/safe-json': 1.0.2 idb-keyval: 6.2.2 - unstorage: 1.17.0(@vercel/functions@2.2.13)(idb-keyval@6.2.2) + unstorage: 1.16.0(idb-keyval@6.2.2) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9126,7 +9084,6 @@ snapshots: - '@planetscale/database' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - db0 @@ -9154,16 +9111,16 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/sign-client@2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@walletconnect/core': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@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(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -9179,7 +9136,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9190,16 +9146,16 @@ snapshots: - utf-8-validate - zod - '@walletconnect/sign-client@2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/sign-client@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': dependencies: - '@walletconnect/core': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/core': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) '@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(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) events: 3.3.0 transitivePeerDependencies: - '@azure/app-configuration' @@ -9215,7 +9171,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9230,12 +9185,12 @@ snapshots: dependencies: tslib: 1.14.1 - '@walletconnect/types@2.21.0(@vercel/functions@2.2.13)': + '@walletconnect/types@2.21.0': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -9252,19 +9207,18 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - db0 - ioredis - uploadthing - '@walletconnect/types@2.21.1(@vercel/functions@2.2.13)': + '@walletconnect/types@2.21.1': dependencies: '@walletconnect/events': 1.0.1 '@walletconnect/heartbeat': 1.2.2 '@walletconnect/jsonrpc-types': 1.0.4 - '@walletconnect/keyvaluestorage': 1.1.1(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 events: 3.3.0 transitivePeerDependencies: @@ -9281,25 +9235,24 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - db0 - ioredis - uploadthing - '@walletconnect/universal-provider@2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.0(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@walletconnect/types': 2.21.0 + '@walletconnect/utils': 2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -9316,7 +9269,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9328,18 +9280,18 @@ snapshots: - utf-8-validate - zod - '@walletconnect/universal-provider@2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/universal-provider@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@walletconnect/logger': 2.1.2 - '@walletconnect/sign-client': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - '@walletconnect/types': 2.21.1(@vercel/functions@2.2.13) - '@walletconnect/utils': 2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@walletconnect/sign-client': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + '@walletconnect/types': 2.21.1 + '@walletconnect/utils': 2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) es-toolkit: 1.33.0 events: 3.3.0 transitivePeerDependencies: @@ -9356,7 +9308,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9368,25 +9319,25 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.0(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.0(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@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(@vercel/functions@2.2.13) + '@walletconnect/types': 2.21.0 '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9401,7 +9352,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9412,25 +9362,25 @@ snapshots: - utf-8-validate - zod - '@walletconnect/utils@2.21.1(@vercel/functions@2.2.13)(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)': + '@walletconnect/utils@2.21.1(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)': 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(@vercel/functions@2.2.13) + '@walletconnect/keyvaluestorage': 1.1.1 '@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(@vercel/functions@2.2.13) + '@walletconnect/types': 2.21.1 '@walletconnect/window-getters': 1.0.1 '@walletconnect/window-metadata': 1.0.1 bs58: 6.0.0 detect-browser: 5.3.0 query-string: 7.1.3 uint8arrays: 3.1.0 - viem: 2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + viem: 2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -9445,7 +9395,6 @@ snapshots: - '@react-native-async-storage/async-storage' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -9465,25 +9414,20 @@ snapshots: '@walletconnect/window-getters': 1.0.1 tslib: 1.14.1 - abitype@1.0.6(typescript@5.9.2)(zod@3.25.76): - optionalDependencies: - typescript: 5.9.2 - zod: 3.25.76 - - abitype@1.0.8(typescript@5.9.2)(zod@3.25.76): + abitype@1.0.6(typescript@5.8.3)(zod@3.25.67): optionalDependencies: - typescript: 5.9.2 - zod: 3.25.76 + typescript: 5.8.3 + zod: 3.25.67 - abitype@1.1.0(typescript@5.9.2)(zod@3.22.4): + abitype@1.0.8(typescript@5.8.3)(zod@3.22.4): optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 zod: 3.22.4 - abitype@1.1.0(typescript@5.9.2)(zod@3.25.76): + abitype@1.0.8(typescript@5.8.3)(zod@3.25.67): optionalDependencies: - typescript: 5.9.2 - zod: 3.25.76 + typescript: 5.8.3 + zod: 3.25.67 accepts@1.3.8: dependencies: @@ -9496,7 +9440,7 @@ snapshots: acorn@8.15.0: {} - agent-base@7.1.4: {} + agent-base@7.1.3: {} agentkeepalive@4.6.0: dependencies: @@ -9511,7 +9455,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.2.0: {} + ansi-regex@6.1.0: {} ansi-styles@4.3.0: dependencies: @@ -9623,14 +9567,22 @@ snapshots: axe-core@4.10.3: {} - axios-retry@4.5.0(axios@1.11.0): + axios-retry@4.5.0(axios@1.12.2): dependencies: - axios: 1.11.0 + axios: 1.12.2 is-retry-allowed: 2.2.0 - axios@1.11.0: + axios@1.10.0: + dependencies: + follow-redirects: 1.15.9 + form-data: 4.0.3 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axios@1.12.2: dependencies: - follow-redirects: 1.15.11 + follow-redirects: 1.15.9 form-data: 4.0.4 proxy-from-env: 1.1.0 transitivePeerDependencies: @@ -9638,27 +9590,27 @@ snapshots: axobject-query@4.1.0: {} - babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.3): + babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.4): dependencies: - '@babel/compat-data': 7.28.0 - '@babel/core': 7.28.3 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.3) + '@babel/compat-data': 7.27.5 + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.3): + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.4): dependencies: - '@babel/core': 7.28.3 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.3) - core-js-compat: 3.45.1 + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + core-js-compat: 3.43.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.3): + babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.27.4): dependencies: - '@babel/core': 7.28.3 - '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.3) + '@babel/core': 7.27.4 + '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) transitivePeerDependencies: - supports-color @@ -9678,7 +9630,7 @@ snapshots: dependencies: bindings: 1.5.0 - bignumber.js@9.3.1: {} + bignumber.js@9.3.0: {} binary-extensions@2.3.0: {} @@ -9713,7 +9665,7 @@ snapshots: bs58: 4.0.1 text-encoding-utf-8: 1.0.2 - bowser@2.12.1: {} + bowser@2.11.0: {} brace-expansion@1.1.12: dependencies: @@ -9728,12 +9680,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.4: + browserslist@4.25.0: dependencies: - caniuse-lite: 1.0.30001739 - electron-to-chromium: 1.5.214 + caniuse-lite: 1.0.30001724 + electron-to-chromium: 1.5.173 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.4) + update-browserslist-db: 1.1.3(browserslist@4.25.0) bs58@4.0.1: dependencies: @@ -9752,11 +9704,15 @@ snapshots: dependencies: node-gyp-build: 4.8.4 - bundle-require@5.1.0(esbuild@0.25.9): + bundle-require@5.1.0(esbuild@0.25.5): dependencies: - esbuild: 0.25.9 + esbuild: 0.25.5 load-tsconfig: 0.2.5 + busboy@1.6.0: + dependencies: + streamsearch: 1.1.0 + bytes@3.1.2: {} cac@6.7.14: {} @@ -9786,22 +9742,22 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001739: {} + caniuse-lite@1.0.30001724: {} - chai@5.3.3: + chai@5.2.0: dependencies: assertion-error: 2.0.1 check-error: 2.1.1 deep-eql: 5.0.2 - loupe: 3.2.1 - pathval: 2.0.1 + loupe: 3.1.4 + pathval: 2.0.0 chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - chalk@5.6.0: {} + chalk@5.4.1: {} charenc@0.0.2: {} @@ -9861,7 +9817,9 @@ snapshots: commander@12.1.0: {} - commander@14.0.0: {} + commander@13.1.0: {} + + commander@14.0.1: {} commander@2.20.3: {} @@ -9891,20 +9849,20 @@ snapshots: cookie@0.7.1: {} - core-js-compat@3.45.1: + core-js-compat@3.43.0: dependencies: - browserslist: 4.25.4 + browserslist: 4.25.0 core-util-is@1.0.3: {} - cosmiconfig@8.3.6(typescript@5.9.2): + cosmiconfig@8.3.6(typescript@5.8.3): dependencies: import-fresh: 3.3.1 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 crc-32@1.2.2: {} @@ -9932,10 +9890,10 @@ snapshots: crypt@0.0.2: {} - css-select@5.2.2: + css-select@5.1.0: dependencies: boolbase: 1.0.0 - css-what: 6.2.2 + css-what: 6.1.0 domhandler: 5.0.3 domutils: 3.2.2 nth-check: 2.1.1 @@ -9950,7 +9908,7 @@ snapshots: mdn-data: 2.0.30 source-map-js: 1.2.1 - css-what@6.2.2: {} + css-what@6.1.0: {} cssesc@3.0.0: {} @@ -9958,7 +9916,7 @@ snapshots: dependencies: css-tree: 2.2.1 - cssstyle@4.6.0: + cssstyle@4.5.0: dependencies: '@asamuzakjp/css-color': 3.2.0 rrweb-cssom: 0.8.0 @@ -9992,7 +9950,7 @@ snapshots: date-fns@2.30.0: dependencies: - '@babel/runtime': 7.28.3 + '@babel/runtime': 7.27.6 dayjs@1.11.13: {} @@ -10014,7 +9972,7 @@ snapshots: decamelize@1.2.0: {} - decimal.js@10.6.0: {} + decimal.js@10.5.0: {} decode-uri-component@0.2.2: {} @@ -10044,9 +10002,9 @@ snapshots: depd@2.0.0: {} - derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1)): + derive-valtio@0.1.0(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0)): dependencies: - valtio: 1.13.2(@types/react@19.1.12)(react@19.1.1) + valtio: 1.13.2(@types/react@19.1.8)(react@19.1.0) destr@2.0.5: {} @@ -10107,14 +10065,14 @@ snapshots: eciesjs@0.4.15: dependencies: - '@ecies/ciphers': 0.2.4(@noble/ciphers@1.3.0) + '@ecies/ciphers': 0.2.3(@noble/ciphers@1.3.0) '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.7 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 ee-first@1.1.1: {} - electron-to-chromium@1.5.214: {} + electron-to-chromium@1.5.173: {} emoji-regex@8.0.0: {} @@ -10263,34 +10221,33 @@ snapshots: dependencies: es6-promise: 4.2.8 - esbuild@0.25.9: + esbuild@0.25.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.9 - '@esbuild/android-arm': 0.25.9 - '@esbuild/android-arm64': 0.25.9 - '@esbuild/android-x64': 0.25.9 - '@esbuild/darwin-arm64': 0.25.9 - '@esbuild/darwin-x64': 0.25.9 - '@esbuild/freebsd-arm64': 0.25.9 - '@esbuild/freebsd-x64': 0.25.9 - '@esbuild/linux-arm': 0.25.9 - '@esbuild/linux-arm64': 0.25.9 - '@esbuild/linux-ia32': 0.25.9 - '@esbuild/linux-loong64': 0.25.9 - '@esbuild/linux-mips64el': 0.25.9 - '@esbuild/linux-ppc64': 0.25.9 - '@esbuild/linux-riscv64': 0.25.9 - '@esbuild/linux-s390x': 0.25.9 - '@esbuild/linux-x64': 0.25.9 - '@esbuild/netbsd-arm64': 0.25.9 - '@esbuild/netbsd-x64': 0.25.9 - '@esbuild/openbsd-arm64': 0.25.9 - '@esbuild/openbsd-x64': 0.25.9 - '@esbuild/openharmony-arm64': 0.25.9 - '@esbuild/sunos-x64': 0.25.9 - '@esbuild/win32-arm64': 0.25.9 - '@esbuild/win32-ia32': 0.25.9 - '@esbuild/win32-x64': 0.25.9 + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 escalade@3.2.0: {} @@ -10298,21 +10255,21 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-next@15.1.7(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2): + eslint-config-next@15.1.7(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3): dependencies: '@next/eslint-plugin-next': 15.1.7 - '@rushstack/eslint-patch': 1.12.0 - '@typescript-eslint/eslint-plugin': 8.42.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - '@typescript-eslint/parser': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - eslint: 9.34.0(jiti@1.21.7) + '@rushstack/eslint-patch': 1.11.0 + '@typescript-eslint/eslint-plugin': 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + eslint: 9.29.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.34.0(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.34.0(jiti@1.21.7)) - eslint-plugin-react: 7.37.5(eslint@9.34.0(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.2.0(eslint@9.34.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.29.0(jiti@1.21.7)) + eslint-plugin-react: 7.37.5(eslint@9.29.0(jiti@1.21.7)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.29.0(jiti@1.21.7)) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - eslint-import-resolver-webpack - eslint-plugin-import-x @@ -10326,33 +10283,33 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.34.0(jiti@1.21.7)): + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@1.21.7)): dependencies: '@nolyfill/is-core-module': 1.0.39 debug: 4.4.1 - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) get-tsconfig: 4.10.1 is-bun-module: 2.0.0 stable-hash: 0.0.5 tinyglobby: 0.2.14 - unrs-resolver: 1.11.1 + unrs-resolver: 1.9.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)): + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) - eslint: 9.34.0(jiti@1.21.7) + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) + eslint: 9.29.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.34.0(jiti@1.21.7)) + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.29.0(jiti@1.21.7)) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -10361,9 +10318,9 @@ snapshots: array.prototype.flatmap: 1.3.3 debug: 3.2.7 doctrine: 2.1.0 - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.34.0(jiti@1.21.7)) + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.29.0(jiti@1.21.7)) hasown: 2.0.2 is-core-module: 2.16.1 is-glob: 4.0.3 @@ -10375,20 +10332,20 @@ snapshots: string.prototype.trimend: 1.0.9 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.42.0(eslint@9.34.0(jiti@1.21.7))(typescript@5.9.2) + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0(jiti@1.21.7))(typescript@5.8.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-jsdoc@50.8.0(eslint@9.34.0(jiti@1.21.7)): + eslint-plugin-jsdoc@50.8.0(eslint@9.29.0(jiti@1.21.7)): dependencies: '@es-joy/jsdoccomment': 0.50.2 are-docs-informative: 0.0.2 comment-parser: 1.4.1 debug: 4.4.1 escape-string-regexp: 4.0.0 - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) espree: 10.4.0 esquery: 1.6.0 parse-imports-exports: 0.2.4 @@ -10397,7 +10354,7 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-jsx-a11y@6.10.2(eslint@9.34.0(jiti@1.21.7)): + eslint-plugin-jsx-a11y@6.10.2(eslint@9.29.0(jiti@1.21.7)): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 @@ -10407,7 +10364,7 @@ snapshots: axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) hasown: 2.0.2 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 @@ -10416,18 +10373,18 @@ snapshots: safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 - eslint-plugin-prettier@5.5.4(eslint@9.34.0(jiti@1.21.7))(prettier@3.5.2): + eslint-plugin-prettier@5.5.0(eslint@9.29.0(jiti@1.21.7))(prettier@3.5.2): dependencies: - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) prettier: 3.5.2 prettier-linter-helpers: 1.0.0 - synckit: 0.11.11 + synckit: 0.11.8 - eslint-plugin-react-hooks@5.2.0(eslint@9.34.0(jiti@1.21.7)): + eslint-plugin-react-hooks@5.2.0(eslint@9.29.0(jiti@1.21.7)): dependencies: - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) - eslint-plugin-react@7.37.5(eslint@9.34.0(jiti@1.21.7)): + eslint-plugin-react@7.37.5(eslint@9.29.0(jiti@1.21.7)): dependencies: array-includes: 3.1.9 array.prototype.findlast: 1.2.5 @@ -10435,7 +10392,7 @@ snapshots: array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 es-iterator-helpers: 1.2.1 - eslint: 9.34.0(jiti@1.21.7) + eslint: 9.29.0(jiti@1.21.7) estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 @@ -10458,17 +10415,17 @@ snapshots: eslint-visitor-keys@4.2.1: {} - eslint@9.34.0(jiti@1.21.7): + eslint@9.29.0(jiti@1.21.7): dependencies: - '@eslint-community/eslint-utils': 4.8.0(eslint@9.34.0(jiti@1.21.7)) + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0(jiti@1.21.7)) '@eslint-community/regexpp': 4.12.1 - '@eslint/config-array': 0.21.0 - '@eslint/config-helpers': 0.3.1 - '@eslint/core': 0.15.2 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 '@eslint/eslintrc': 3.3.1 - '@eslint/js': 9.34.0 - '@eslint/plugin-kit': 0.3.5 - '@humanfs/node': 0.16.7 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.2 + '@humanfs/node': 0.16.6 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.8 @@ -10564,7 +10521,7 @@ snapshots: events@3.3.0: {} - expect-type@1.2.2: {} + expect-type@1.2.1: {} express@4.21.2: dependencies: @@ -10645,9 +10602,9 @@ snapshots: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.4.6(picomatch@4.0.2): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.2 file-entry-cache@8.0.0: dependencies: @@ -10685,9 +10642,9 @@ snapshots: fix-dts-default-cjs-exports@1.0.1: dependencies: - magic-string: 0.30.18 - mlly: 1.8.0 - rollup: 4.50.0 + magic-string: 0.30.17 + mlly: 1.7.4 + rollup: 4.44.0 flat-cache@4.0.1: dependencies: @@ -10696,7 +10653,7 @@ snapshots: flatted@3.3.3: {} - follow-redirects@1.15.11: {} + follow-redirects@1.15.9: {} for-each@0.3.5: dependencies: @@ -10707,6 +10664,14 @@ snapshots: cross-spawn: 7.0.6 signal-exit: 4.1.0 + form-data@4.0.3: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + form-data@4.0.4: dependencies: asynckit: 0.4.0 @@ -10784,6 +10749,8 @@ snapshots: package-json-from-dist: 1.0.1 path-scurry: 1.11.1 + globals@11.12.0: {} + globals@14.0.0: {} globalthis@1.0.4: @@ -10807,14 +10774,14 @@ snapshots: graphql@16.11.0: {} - h3@1.15.4: + h3@1.15.3: dependencies: cookie-es: 1.2.2 crossws: 0.3.5 defu: 6.1.4 destr: 2.0.5 iron-webcrypto: 1.2.1 - node-mock-http: 1.0.2 + node-mock-http: 1.0.0 radix3: 1.1.2 ufo: 1.6.1 uncrypto: 0.1.3 @@ -10841,7 +10808,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.9.6: {} + hono@4.8.2: {} html-encoding-sniffer@4.0.0: dependencies: @@ -10857,14 +10824,14 @@ snapshots: http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.4 + agent-base: 7.1.3 debug: 4.4.1 transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.4 + agent-base: 7.1.3 debug: 4.4.1 transitivePeerDependencies: - supports-color @@ -10881,8 +10848,6 @@ snapshots: dependencies: safer-buffer: 2.1.2 - idb-keyval@6.2.1: {} - idb-keyval@6.2.2: {} ieee754@1.2.1: {} @@ -11059,9 +11024,9 @@ snapshots: dependencies: ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) - isows@1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)): + isows@1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)): dependencies: - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) iterator.prototype@1.1.5: dependencies: @@ -11100,7 +11065,7 @@ snapshots: jose@5.10.0: {} - jose@6.1.0: {} + jose@6.0.11: {} joycon@3.1.1: {} @@ -11116,14 +11081,14 @@ snapshots: jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10): dependencies: - cssstyle: 4.6.0 + cssstyle: 4.5.0 data-urls: 5.0.0 - decimal.js: 10.6.0 + decimal.js: 10.5.0 html-encoding-sniffer: 4.0.0 http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.21 + nwsapi: 2.2.20 parse5: 7.3.0 rrweb-cssom: 0.8.0 saxes: 6.0.0 @@ -11134,7 +11099,7 @@ snapshots: whatwg-encoding: 3.1.1 whatwg-mimetype: 4.0.0 whatwg-url: 14.2.0 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil @@ -11202,21 +11167,21 @@ snapshots: lines-and-columns@1.2.4: {} - lit-element@4.2.1: + lit-element@4.2.0: dependencies: - '@lit-labs/ssr-dom-shim': 1.4.0 - '@lit/reactive-element': 2.1.1 - lit-html: 3.3.1 + '@lit-labs/ssr-dom-shim': 1.3.0 + '@lit/reactive-element': 2.1.0 + lit-html: 3.3.0 - lit-html@3.3.1: + lit-html@3.3.0: dependencies: '@types/trusted-types': 2.0.7 lit@3.3.0: dependencies: - '@lit/reactive-element': 2.1.1 - lit-element: 4.2.1 - lit-html: 3.3.1 + '@lit/reactive-element': 2.1.0 + lit-element: 4.2.0 + lit-html: 3.3.0 load-tsconfig@0.2.5: {} @@ -11240,7 +11205,7 @@ snapshots: dependencies: js-tokens: 4.0.0 - loupe@3.2.1: {} + loupe@3.1.4: {} lower-case@2.0.2: dependencies: @@ -11252,9 +11217,9 @@ snapshots: dependencies: yallist: 3.1.1 - magic-string@0.30.18: + magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/sourcemap-codec': 1.5.0 math-intrinsics@1.1.0: {} @@ -11303,11 +11268,11 @@ snapshots: minipass@7.1.2: {} - mipd@0.0.7(typescript@5.9.2): + mipd@0.0.7(typescript@5.8.3): optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 - mlly@1.8.0: + mlly@1.7.4: dependencies: acorn: 8.15.0 pathe: 2.0.3 @@ -11328,31 +11293,33 @@ snapshots: nanoid@3.3.11: {} - napi-postinstall@0.3.3: {} + napi-postinstall@0.2.4: {} natural-compare@1.4.0: {} negotiator@0.6.3: {} - next@15.5.2(@babel/core@7.28.3)(react-dom@19.1.1(react@19.1.1))(react@19.1.1): + next@15.3.4(@babel/core@7.27.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: - '@next/env': 15.5.2 + '@next/env': 15.3.4 + '@swc/counter': 0.1.3 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001739 + busboy: 1.6.0 + caniuse-lite: 1.0.30001724 postcss: 8.4.31 - react: 19.1.1 - react-dom: 19.1.1(react@19.1.1) - styled-jsx: 5.1.6(@babel/core@7.28.3)(react@19.1.1) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(@babel/core@7.27.4)(react@19.1.0) optionalDependencies: - '@next/swc-darwin-arm64': 15.5.2 - '@next/swc-darwin-x64': 15.5.2 - '@next/swc-linux-arm64-gnu': 15.5.2 - '@next/swc-linux-arm64-musl': 15.5.2 - '@next/swc-linux-x64-gnu': 15.5.2 - '@next/swc-linux-x64-musl': 15.5.2 - '@next/swc-win32-arm64-msvc': 15.5.2 - '@next/swc-win32-x64-msvc': 15.5.2 - sharp: 0.34.3 + '@next/swc-darwin-arm64': 15.3.4 + '@next/swc-darwin-x64': 15.3.4 + '@next/swc-linux-arm64-gnu': 15.3.4 + '@next/swc-linux-arm64-musl': 15.3.4 + '@next/swc-linux-x64-gnu': 15.3.4 + '@next/swc-linux-x64-musl': 15.3.4 + '@next/swc-win32-arm64-msvc': 15.3.4 + '@next/swc-win32-x64-msvc': 15.3.4 + sharp: 0.34.2 transitivePeerDependencies: - '@babel/core' - babel-plugin-macros @@ -11364,7 +11331,7 @@ snapshots: node-addon-api@2.0.2: {} - node-fetch-native@1.6.7: {} + node-fetch-native@1.6.6: {} node-fetch@2.7.0: dependencies: @@ -11372,7 +11339,7 @@ snapshots: node-gyp-build@4.8.4: {} - node-mock-http@1.0.2: {} + node-mock-http@1.0.0: {} node-releases@2.0.19: {} @@ -11382,7 +11349,7 @@ snapshots: dependencies: boolbase: 1.0.0 - nwsapi@2.2.21: {} + nwsapi@2.2.20: {} obj-multiplex@1.0.0: dependencies: @@ -11437,7 +11404,7 @@ snapshots: ofetch@1.4.1: dependencies: destr: 2.0.5 - node-fetch-native: 1.6.7 + node-fetch-native: 1.6.6 ufo: 1.6.1 on-exit-leak-free@0.2.0: {} @@ -11465,75 +11432,61 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.4.4(typescript@5.9.2)(zod@3.25.76): + ox@0.4.4(typescript@5.8.3)(zod@3.25.67): dependencies: '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.9.7 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - zod - ox@0.6.7(typescript@5.9.2)(zod@3.25.76): + ox@0.6.7(typescript@5.8.3)(zod@3.25.67): dependencies: '@adraffy/ens-normalize': 1.11.0 '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.2)(zod@3.25.76) - eventemitter3: 5.0.1 - optionalDependencies: - typescript: 5.9.2 - transitivePeerDependencies: - - zod - - ox@0.6.9(typescript@5.9.2)(zod@3.25.76): - dependencies: - '@adraffy/ens-normalize': 1.11.0 - '@noble/curves': 1.9.7 - '@noble/hashes': 1.8.0 - '@scure/bip32': 1.7.0 - '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - zod - ox@0.9.3(typescript@5.9.2)(zod@3.22.4): + ox@0.8.1(typescript@5.8.3)(zod@3.22.4): dependencies: '@adraffy/ens-normalize': 1.11.0 '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.22.4) + abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - zod - ox@0.9.3(typescript@5.9.2)(zod@3.25.76): + ox@0.8.1(typescript@5.8.3)(zod@3.25.67): dependencies: '@adraffy/ens-normalize': 1.11.0 '@noble/ciphers': 1.3.0 - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) + abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67) eventemitter3: 5.0.1 optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - zod @@ -11597,13 +11550,13 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.1: {} + pathval@2.0.0: {} picocolors@1.1.1: {} picomatch@2.3.1: {} - picomatch@4.0.3: {} + picomatch@4.0.2: {} pify@2.3.0: {} @@ -11637,7 +11590,7 @@ snapshots: pkg-types@1.3.1: dependencies: confbox: 0.1.8 - mlly: 1.8.0 + mlly: 1.7.4 pathe: 2.0.3 pngjs@5.0.0: {} @@ -11661,18 +11614,18 @@ snapshots: postcss-load-config@4.0.2(postcss@8.5.6): dependencies: lilconfig: 3.1.3 - yaml: 2.8.1 + yaml: 2.8.0 optionalDependencies: postcss: 8.5.6 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(yaml@2.8.1): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(yaml@2.8.0): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 postcss: 8.5.6 - tsx: 4.20.5 - yaml: 2.8.1 + tsx: 4.20.3 + yaml: 2.8.0 postcss-nested@6.2.0(postcss@8.5.6): dependencies: @@ -11698,9 +11651,7 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 - preact@10.24.2: {} - - preact@10.27.1: {} + preact@10.26.9: {} prelude-ls@1.2.1: {} @@ -11775,14 +11726,14 @@ snapshots: iconv-lite: 0.4.24 unpipe: 1.0.0 - react-dom@19.1.1(react@19.1.1): + react-dom@19.1.0(react@19.1.0): dependencies: - react: 19.1.1 + react: 19.1.0 scheduler: 0.26.0 react-is@16.13.1: {} - react@19.1.1: {} + react@19.1.0: {} read-cache@1.0.0: dependencies: @@ -11877,34 +11828,33 @@ snapshots: reusify@1.1.0: {} - rollup@4.50.0: + rollup@4.44.0: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.50.0 - '@rollup/rollup-android-arm64': 4.50.0 - '@rollup/rollup-darwin-arm64': 4.50.0 - '@rollup/rollup-darwin-x64': 4.50.0 - '@rollup/rollup-freebsd-arm64': 4.50.0 - '@rollup/rollup-freebsd-x64': 4.50.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.50.0 - '@rollup/rollup-linux-arm-musleabihf': 4.50.0 - '@rollup/rollup-linux-arm64-gnu': 4.50.0 - '@rollup/rollup-linux-arm64-musl': 4.50.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.50.0 - '@rollup/rollup-linux-ppc64-gnu': 4.50.0 - '@rollup/rollup-linux-riscv64-gnu': 4.50.0 - '@rollup/rollup-linux-riscv64-musl': 4.50.0 - '@rollup/rollup-linux-s390x-gnu': 4.50.0 - '@rollup/rollup-linux-x64-gnu': 4.50.0 - '@rollup/rollup-linux-x64-musl': 4.50.0 - '@rollup/rollup-openharmony-arm64': 4.50.0 - '@rollup/rollup-win32-arm64-msvc': 4.50.0 - '@rollup/rollup-win32-ia32-msvc': 4.50.0 - '@rollup/rollup-win32-x64-msvc': 4.50.0 + '@rollup/rollup-android-arm-eabi': 4.44.0 + '@rollup/rollup-android-arm64': 4.44.0 + '@rollup/rollup-darwin-arm64': 4.44.0 + '@rollup/rollup-darwin-x64': 4.44.0 + '@rollup/rollup-freebsd-arm64': 4.44.0 + '@rollup/rollup-freebsd-x64': 4.44.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.0 + '@rollup/rollup-linux-arm-musleabihf': 4.44.0 + '@rollup/rollup-linux-arm64-gnu': 4.44.0 + '@rollup/rollup-linux-arm64-musl': 4.44.0 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-gnu': 4.44.0 + '@rollup/rollup-linux-riscv64-musl': 4.44.0 + '@rollup/rollup-linux-s390x-gnu': 4.44.0 + '@rollup/rollup-linux-x64-gnu': 4.44.0 + '@rollup/rollup-linux-x64-musl': 4.44.0 + '@rollup/rollup-win32-arm64-msvc': 4.44.0 + '@rollup/rollup-win32-ia32-msvc': 4.44.0 + '@rollup/rollup-win32-x64-msvc': 4.44.0 fsevents: 2.3.3 - rpc-websockets@9.1.3: + rpc-websockets@9.1.1: dependencies: '@swc/helpers': 0.5.17 '@types/uuid': 8.3.4 @@ -11912,7 +11862,7 @@ snapshots: buffer: 6.0.3 eventemitter3: 5.0.1 uuid: 8.3.2 - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 @@ -12013,40 +11963,38 @@ snapshots: setprototypeof@1.2.0: {} - sha.js@2.4.12: + sha.js@2.4.11: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - to-buffer: 1.2.1 - sharp@0.34.3: + sharp@0.34.2: dependencies: color: 4.2.3 detect-libc: 2.0.4 semver: 7.7.2 optionalDependencies: - '@img/sharp-darwin-arm64': 0.34.3 - '@img/sharp-darwin-x64': 0.34.3 - '@img/sharp-libvips-darwin-arm64': 1.2.0 - '@img/sharp-libvips-darwin-x64': 1.2.0 - '@img/sharp-libvips-linux-arm': 1.2.0 - '@img/sharp-libvips-linux-arm64': 1.2.0 - '@img/sharp-libvips-linux-ppc64': 1.2.0 - '@img/sharp-libvips-linux-s390x': 1.2.0 - '@img/sharp-libvips-linux-x64': 1.2.0 - '@img/sharp-libvips-linuxmusl-arm64': 1.2.0 - '@img/sharp-libvips-linuxmusl-x64': 1.2.0 - '@img/sharp-linux-arm': 0.34.3 - '@img/sharp-linux-arm64': 0.34.3 - '@img/sharp-linux-ppc64': 0.34.3 - '@img/sharp-linux-s390x': 0.34.3 - '@img/sharp-linux-x64': 0.34.3 - '@img/sharp-linuxmusl-arm64': 0.34.3 - '@img/sharp-linuxmusl-x64': 0.34.3 - '@img/sharp-wasm32': 0.34.3 - '@img/sharp-win32-arm64': 0.34.3 - '@img/sharp-win32-ia32': 0.34.3 - '@img/sharp-win32-x64': 0.34.3 + '@img/sharp-darwin-arm64': 0.34.2 + '@img/sharp-darwin-x64': 0.34.2 + '@img/sharp-libvips-darwin-arm64': 1.1.0 + '@img/sharp-libvips-darwin-x64': 1.1.0 + '@img/sharp-libvips-linux-arm': 1.1.0 + '@img/sharp-libvips-linux-arm64': 1.1.0 + '@img/sharp-libvips-linux-ppc64': 1.1.0 + '@img/sharp-libvips-linux-s390x': 1.1.0 + '@img/sharp-libvips-linux-x64': 1.1.0 + '@img/sharp-libvips-linuxmusl-arm64': 1.1.0 + '@img/sharp-libvips-linuxmusl-x64': 1.1.0 + '@img/sharp-linux-arm': 0.34.2 + '@img/sharp-linux-arm64': 0.34.2 + '@img/sharp-linux-s390x': 0.34.2 + '@img/sharp-linux-x64': 0.34.2 + '@img/sharp-linuxmusl-arm64': 0.34.2 + '@img/sharp-linuxmusl-x64': 0.34.2 + '@img/sharp-wasm32': 0.34.2 + '@img/sharp-win32-arm64': 0.34.2 + '@img/sharp-win32-ia32': 0.34.2 + '@img/sharp-win32-x64': 0.34.2 optional: true shebang-command@2.0.0: @@ -12130,9 +12078,9 @@ snapshots: spdx-expression-parse@4.0.0: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.22 + spdx-license-ids: 3.0.21 - spdx-license-ids@3.0.22: {} + spdx-license-ids@3.0.21: {} split-on-first@1.1.0: {} @@ -12159,6 +12107,8 @@ snapshots: stream-shift@1.0.3: {} + streamsearch@1.1.0: {} + strict-uri-encode@2.0.0: {} string-width@4.2.3: @@ -12237,7 +12187,7 @@ snapshots: strip-ansi@7.1.0: dependencies: - ansi-regex: 6.2.0 + ansi-regex: 6.1.0 strip-bom@3.0.0: {} @@ -12247,16 +12197,16 @@ snapshots: dependencies: js-tokens: 9.0.1 - styled-jsx@5.1.6(@babel/core@7.28.3)(react@19.1.1): + styled-jsx@5.1.6(@babel/core@7.27.4)(react@19.1.0): dependencies: client-only: 0.0.1 - react: 19.1.1 + react: 19.1.0 optionalDependencies: - '@babel/core': 7.28.3 + '@babel/core': 7.27.4 sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/gen-mapping': 0.3.8 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -12280,17 +12230,17 @@ snapshots: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 - css-select: 5.2.2 + css-select: 5.1.0 css-tree: 2.3.1 - css-what: 6.2.2 + css-what: 6.1.0 csso: 5.0.5 picocolors: 1.1.1 symbol-tree@3.2.4: {} - synckit@0.11.11: + synckit@0.11.8: dependencies: - '@pkgr/core': 0.2.9 + '@pkgr/core': 0.2.7 tailwind-merge@2.6.0: {} @@ -12341,8 +12291,8 @@ snapshots: tinyglobby@0.2.14: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 tinypool@1.1.1: {} @@ -12356,12 +12306,6 @@ snapshots: dependencies: tldts-core: 6.1.86 - to-buffer@1.2.1: - dependencies: - isarray: 2.0.5 - safe-buffer: 5.2.1 - typed-array-buffer: 1.0.3 - to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -12384,15 +12328,19 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@2.1.0(typescript@5.9.2): + ts-api-utils@2.1.0(typescript@5.8.3): dependencies: - typescript: 5.9.2 + typescript: 5.8.3 + + ts-essentials@10.1.1(typescript@5.8.3): + optionalDependencies: + typescript: 5.8.3 ts-interface-checker@0.1.13: {} - tsconfck@3.1.6(typescript@5.9.2): + tsconfck@3.1.6(typescript@5.8.3): optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 tsconfig-paths@3.15.0: dependencies: @@ -12405,20 +12353,20 @@ snapshots: tslib@2.8.1: {} - tsup@8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(typescript@5.9.2)(yaml@2.8.1): + tsup@8.5.0(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(typescript@5.8.3)(yaml@2.8.0): dependencies: - bundle-require: 5.1.0(esbuild@0.25.9) + bundle-require: 5.1.0(esbuild@0.25.5) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 debug: 4.4.1 - esbuild: 0.25.9 + esbuild: 0.25.5 fix-dts-default-cjs-exports: 1.0.1 joycon: 3.1.1 picocolors: 1.1.1 - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.5)(yaml@2.8.1) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.6)(tsx@4.20.3)(yaml@2.8.0) resolve-from: 5.0.0 - rollup: 4.50.0 + rollup: 4.44.0 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tinyexec: 0.3.2 @@ -12426,46 +12374,46 @@ snapshots: tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.6 - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - jiti - supports-color - tsx - yaml - tsx@4.20.5: + tsx@4.20.3: dependencies: - esbuild: 0.25.9 + esbuild: 0.25.5 get-tsconfig: 4.10.1 optionalDependencies: fsevents: 2.3.3 - turbo-darwin-64@2.5.6: + turbo-darwin-64@2.5.4: optional: true - turbo-darwin-arm64@2.5.6: + turbo-darwin-arm64@2.5.4: optional: true - turbo-linux-64@2.5.6: + turbo-linux-64@2.5.4: optional: true - turbo-linux-arm64@2.5.6: + turbo-linux-arm64@2.5.4: optional: true - turbo-windows-64@2.5.6: + turbo-windows-64@2.5.4: optional: true - turbo-windows-arm64@2.5.6: + turbo-windows-arm64@2.5.4: optional: true - turbo@2.5.6: + turbo@2.5.4: optionalDependencies: - turbo-darwin-64: 2.5.6 - turbo-darwin-arm64: 2.5.6 - turbo-linux-64: 2.5.6 - turbo-linux-arm64: 2.5.6 - turbo-windows-64: 2.5.6 - turbo-windows-arm64: 2.5.6 + turbo-darwin-64: 2.5.4 + turbo-darwin-arm64: 2.5.4 + turbo-linux-64: 2.5.4 + turbo-linux-arm64: 2.5.4 + turbo-windows-64: 2.5.4 + turbo-windows-arm64: 2.5.4 type-check@0.4.0: dependencies: @@ -12509,7 +12457,7 @@ snapshots: possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 - typescript@5.9.2: {} + typescript@5.8.3: {} ufo@1.6.1: {} @@ -12528,7 +12476,7 @@ snapshots: undici-types@6.21.0: {} - undici-types@7.15.0: {} + undici-types@7.16.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -12543,47 +12491,46 @@ snapshots: unpipe@1.0.0: {} - unrs-resolver@1.11.1: + unrs-resolver@1.9.2: dependencies: - napi-postinstall: 0.3.3 + napi-postinstall: 0.2.4 optionalDependencies: - '@unrs/resolver-binding-android-arm-eabi': 1.11.1 - '@unrs/resolver-binding-android-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-x64': 1.11.1 - '@unrs/resolver-binding-freebsd-x64': 1.11.1 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 - '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-musl': 1.11.1 - '@unrs/resolver-binding-wasm32-wasi': 1.11.1 - '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 - '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 - '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 - - unstorage@1.17.0(@vercel/functions@2.2.13)(idb-keyval@6.2.2): + '@unrs/resolver-binding-android-arm-eabi': 1.9.2 + '@unrs/resolver-binding-android-arm64': 1.9.2 + '@unrs/resolver-binding-darwin-arm64': 1.9.2 + '@unrs/resolver-binding-darwin-x64': 1.9.2 + '@unrs/resolver-binding-freebsd-x64': 1.9.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.9.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.9.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.9.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.9.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.9.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.9.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.9.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.9.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.9.2 + '@unrs/resolver-binding-linux-x64-musl': 1.9.2 + '@unrs/resolver-binding-wasm32-wasi': 1.9.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.9.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.9.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.9.2 + + unstorage@1.16.0(idb-keyval@6.2.2): dependencies: anymatch: 3.1.3 chokidar: 4.0.3 destr: 2.0.5 - h3: 1.15.4 + h3: 1.15.3 lru-cache: 10.4.3 - node-fetch-native: 1.6.7 + node-fetch-native: 1.6.6 ofetch: 1.4.1 ufo: 1.6.1 optionalDependencies: - '@vercel/functions': 2.2.13 idb-keyval: 6.2.2 - update-browserslist-db@1.1.3(browserslist@4.25.4): + update-browserslist-db@1.1.3(browserslist@4.25.0): dependencies: - browserslist: 4.25.4 + browserslist: 4.25.0 escalade: 3.2.0 picocolors: 1.1.1 @@ -12591,13 +12538,13 @@ snapshots: dependencies: punycode: 2.3.1 - use-sync-external-store@1.2.0(react@19.1.1): + use-sync-external-store@1.2.0(react@19.1.0): dependencies: - react: 19.1.1 + react: 19.1.0 - use-sync-external-store@1.4.0(react@19.1.1): + use-sync-external-store@1.4.0(react@19.1.0): dependencies: - react: 19.1.1 + react: 19.1.0 utf-8-validate@5.0.10: dependencies: @@ -12619,75 +12566,75 @@ snapshots: uuid@9.0.1: {} - valtio@1.13.2(@types/react@19.1.12)(react@19.1.1): + valtio@1.13.2(@types/react@19.1.8)(react@19.1.0): dependencies: - derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.12)(react@19.1.1)) + derive-valtio: 0.1.0(valtio@1.13.2(@types/react@19.1.8)(react@19.1.0)) proxy-compare: 2.6.0 - use-sync-external-store: 1.2.0(react@19.1.1) + use-sync-external-store: 1.2.0(react@19.1.0) optionalDependencies: - '@types/react': 19.1.12 - react: 19.1.1 + '@types/react': 19.1.8 + react: 19.1.0 vary@1.1.2: {} - viem@2.23.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.23.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67): dependencies: '@noble/curves': 1.8.1 '@noble/hashes': 1.7.1 '@scure/bip32': 1.6.2 '@scure/bip39': 1.5.4 - abitype: 1.0.8(typescript@5.9.2)(zod@3.25.76) + abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67) isows: 1.0.6(ws@8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.6.7(typescript@5.9.2)(zod@3.25.76) + ox: 0.6.7(typescript@5.8.3)(zod@3.25.67) ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.22.4): + viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.22.4): dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.22.4) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.3(typescript@5.9.2)(zod@3.22.4) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.22.4) + isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.8.1(typescript@5.8.3)(zod@3.22.4) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76): + viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67): dependencies: - '@noble/curves': 1.9.1 + '@noble/curves': 1.9.2 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.2)(zod@3.25.76) - isows: 1.0.7(ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10)) - ox: 0.9.3(typescript@5.9.2)(zod@3.25.76) - ws: 8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10) + abitype: 1.0.8(typescript@5.8.3)(zod@3.25.67) + isows: 1.0.7(ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.8.1(typescript@5.8.3)(zod@3.25.67) + ws: 8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - bufferutil - utf-8-validate - zod - vite-node@3.2.4(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1): + vite-node@3.2.4(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + vite: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -12702,60 +12649,66 @@ snapshots: - tsx - yaml - vite-tsconfig-paths@5.1.4(typescript@5.9.2)(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)): + vite-tsconfig-paths@5.1.4(typescript@5.8.3)(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)): dependencies: debug: 4.4.1 globrex: 0.1.2 - tsconfck: 3.1.6(typescript@5.9.2) + tsconfck: 3.1.6(typescript@5.8.3) optionalDependencies: - vite: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + vite: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color - typescript - vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1): + vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): dependencies: - esbuild: 0.25.9 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + esbuild: 0.25.5 + fdir: 6.4.6(picomatch@4.0.2) + picomatch: 4.0.2 postcss: 8.5.6 - rollup: 4.50.0 + rollup: 4.44.0 tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 22.18.0 + '@types/node': 22.15.33 fsevents: 2.3.3 jiti: 1.21.7 - tsx: 4.20.5 - yaml: 2.8.1 + tsx: 4.20.3 + yaml: 2.8.0 + + vitest-mock-extended@3.1.0(typescript@5.8.3)(vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0)): + dependencies: + ts-essentials: 10.1.1(typescript@5.8.3) + typescript: 5.8.3 + vitest: 3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0) - vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.18.0)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.5)(yaml@2.8.1): + vitest@3.2.4(@types/debug@4.1.12)(@types/node@22.15.33)(jiti@1.21.7)(jsdom@26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10))(tsx@4.20.3)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1)) + '@vitest/mocker': 3.2.4(vite@6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 '@vitest/spy': 3.2.4 '@vitest/utils': 3.2.4 - chai: 5.3.3 + chai: 5.2.0 debug: 4.4.1 - expect-type: 1.2.2 - magic-string: 0.30.18 + expect-type: 1.2.1 + magic-string: 0.30.17 pathe: 2.0.3 - picomatch: 4.0.3 + picomatch: 4.0.2 std-env: 3.9.0 tinybench: 2.9.0 tinyexec: 0.3.2 tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 6.3.5(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) - vite-node: 3.2.4(@types/node@22.18.0)(jiti@1.21.7)(tsx@4.20.5)(yaml@2.8.1) + vite: 6.3.5(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@22.15.33)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/debug': 4.1.12 - '@types/node': 22.18.0 + '@types/node': 22.15.33 jsdom: 26.1.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) transitivePeerDependencies: - jiti @@ -12775,16 +12728,16 @@ snapshots: dependencies: xml-name-validator: 5.0.0 - wagmi@2.16.9(@tanstack/query-core@5.85.9)(@tanstack/react-query@5.85.9(react@19.1.1))(@types/react@19.1.12)(@vercel/functions@2.2.13)(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76): + wagmi@2.15.6(@tanstack/query-core@5.81.2)(@tanstack/react-query@5.81.2(react@19.1.0))(@types/react@19.1.8)(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67): dependencies: - '@tanstack/react-query': 5.85.9(react@19.1.1) - '@wagmi/connectors': 5.9.9(@types/react@19.1.12)(@vercel/functions@2.2.13)(@wagmi/core@2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)))(bufferutil@4.0.9)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(utf-8-validate@5.0.10)(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76))(zod@3.25.76) - '@wagmi/core': 2.20.3(@tanstack/query-core@5.85.9)(@types/react@19.1.12)(react@19.1.1)(typescript@5.9.2)(use-sync-external-store@1.4.0(react@19.1.1))(viem@2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76)) - react: 19.1.1 - use-sync-external-store: 1.4.0(react@19.1.1) - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) + '@tanstack/react-query': 5.81.2(react@19.1.0) + '@wagmi/connectors': 5.8.5(@types/react@19.1.8)(@wagmi/core@2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)))(bufferutil@4.0.9)(react@19.1.0)(typescript@5.8.3)(utf-8-validate@5.0.10)(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67))(zod@3.25.67) + '@wagmi/core': 2.17.3(@tanstack/query-core@5.81.2)(@types/react@19.1.8)(react@19.1.0)(typescript@5.8.3)(use-sync-external-store@1.4.0(react@19.1.0))(viem@2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67)) + react: 19.1.0 + use-sync-external-store: 1.4.0(react@19.1.0) + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) optionalDependencies: - typescript: 5.9.2 + typescript: 5.8.3 transitivePeerDependencies: - '@azure/app-configuration' - '@azure/cosmos' @@ -12801,7 +12754,6 @@ snapshots: - '@types/react' - '@upstash/redis' - '@vercel/blob' - - '@vercel/functions' - '@vercel/kv' - aws4fetch - bufferutil @@ -12933,19 +12885,19 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - ws@8.18.3(bufferutil@4.0.9)(utf-8-validate@5.0.10): + ws@8.18.2(bufferutil@4.0.9)(utf-8-validate@5.0.10): optionalDependencies: bufferutil: 4.0.9 utf-8-validate: 5.0.10 - x402@0.1.2(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10): + x402@0.1.2(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10): dependencies: - '@hono/node-server': 1.19.1(hono@4.9.6) - axios: 1.11.0 + '@hono/node-server': 1.14.4(hono@4.8.2) + axios: 1.10.0 express: 4.21.2 - hono: 4.9.6 - viem: 2.37.3(bufferutil@4.0.9)(typescript@5.9.2)(utf-8-validate@5.0.10)(zod@3.25.76) - zod: 3.25.76 + hono: 4.8.2 + viem: 2.31.4(bufferutil@4.0.9)(typescript@5.8.3)(utf-8-validate@5.0.10)(zod@3.25.67) + zod: 3.25.67 transitivePeerDependencies: - bufferutil - debug @@ -12965,7 +12917,7 @@ snapshots: yallist@3.1.1: {} - yaml@2.8.1: {} + yaml@2.8.0: {} yargs-parser@18.1.3: dependencies: @@ -12990,16 +12942,10 @@ snapshots: zod@3.22.4: {} - zod@3.25.76: {} - - zustand@5.0.0(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)): - optionalDependencies: - '@types/react': 19.1.12 - react: 19.1.1 - use-sync-external-store: 1.4.0(react@19.1.1) + zod@3.25.67: {} - zustand@5.0.3(@types/react@19.1.12)(react@19.1.1)(use-sync-external-store@1.4.0(react@19.1.1)): + zustand@5.0.0(@types/react@19.1.8)(react@19.1.0)(use-sync-external-store@1.4.0(react@19.1.0)): optionalDependencies: - '@types/react': 19.1.12 - react: 19.1.1 - use-sync-external-store: 1.4.0(react@19.1.1) + '@types/react': 19.1.8 + react: 19.1.0 + use-sync-external-store: 1.4.0(react@19.1.0) diff --git a/typescript/pnpm-workspace.yaml b/typescript/pnpm-workspace.yaml index 6aff8b61f3..e3e083b0f4 100644 --- a/typescript/pnpm-workspace.yaml +++ b/typescript/pnpm-workspace.yaml @@ -5,3 +5,10 @@ packages: - site ignoredBuiltDependencies: - esbuild +onlyBuiltDependencies: + - bigint-buffer + - bufferutil + - keccak + - sharp + - unrs-resolver + - utf-8-validate diff --git a/typescript/site/app/facilitator/verify/route.ts b/typescript/site/app/facilitator/verify/route.ts index e825819ba4..524ab5153f 100644 --- a/typescript/site/app/facilitator/verify/route.ts +++ b/typescript/site/app/facilitator/verify/route.ts @@ -1,6 +1,6 @@ import { - PaymentPayload, - PaymentPayloadSchema, + ExactPaymentPayload as PaymentPayload, + ExactPaymentPayloadSchema as PaymentPayloadSchema, PaymentRequirements, PaymentRequirementsSchema, SupportedEVMNetworks,