From 99da55ae0d8f38b65c42f4324b053e85ca896c68 Mon Sep 17 00:00:00 2001 From: Philippe d'Argent Date: Tue, 10 Jun 2025 22:24:50 +0200 Subject: [PATCH] Added a new action provider to call x402 protected api --- typescript/.changeset/loud-jars-relate.md | 5 + typescript/agentkit/README.md | 13 + typescript/agentkit/package.json | 8 +- .../agentkit/src/action-providers/index.ts | 1 + .../src/action-providers/x402/README.md | 70 +++ .../src/action-providers/x402/index.ts | 1 + .../src/action-providers/x402/schemas.ts | 26 + .../x402/x402ActionProvider.test.ts | 523 ++++++++++++++++++ .../x402/x402ActionProvider.ts | 234 ++++++++ typescript/pnpm-lock.yaml | 346 +++--------- 10 files changed, 964 insertions(+), 263 deletions(-) create mode 100644 typescript/.changeset/loud-jars-relate.md create mode 100644 typescript/agentkit/src/action-providers/x402/README.md create mode 100644 typescript/agentkit/src/action-providers/x402/index.ts create mode 100644 typescript/agentkit/src/action-providers/x402/schemas.ts create mode 100644 typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts create mode 100644 typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts diff --git a/typescript/.changeset/loud-jars-relate.md b/typescript/.changeset/loud-jars-relate.md new file mode 100644 index 000000000..f5259fd6f --- /dev/null +++ b/typescript/.changeset/loud-jars-relate.md @@ -0,0 +1,5 @@ +--- +"@coinbase/agentkit": minor +--- + +Added a new action provider to call x402 protected api diff --git a/typescript/agentkit/README.md b/typescript/agentkit/README.md index f88c80a7b..76237b746 100644 --- a/typescript/agentkit/README.md +++ b/typescript/agentkit/README.md @@ -418,6 +418,19 @@ const agent = createReactAgent({
+X402 + + + + + + + + + +
paid_requestMakes HTTP requests to x402-protected API endpoints with automatic payment handling.
fetch_payment_infoFetches payment information from x402-protected endpoints without making payments.
+
+
ZeroDev Wallet diff --git a/typescript/agentkit/package.json b/typescript/agentkit/package.json index 3e79e7a34..5977fbb7a 100644 --- a/typescript/agentkit/package.json +++ b/typescript/agentkit/package.json @@ -48,18 +48,20 @@ "@privy-io/server-auth": "1.18.4", "@solana/spl-token": "^0.4.12", "@solana/web3.js": "^1.98.1", + "@zerodev/ecdsa-validator": "^5.4.5", + "@zerodev/intent": "^0.0.24", + "@zerodev/sdk": "^5.4.28", + "axios": "^1.9.0", "bs58": "^4.0.1", "canonicalize": "^2.1.0", "decimal.js": "^10.5.0", "ethers": "^6.13.5", - "@zerodev/ecdsa-validator": "^5.4.5", - "@zerodev/intent": "^0.0.24", - "@zerodev/sdk": "^5.4.28", "md5": "^2.3.0", "opensea-js": "^7.1.18", "reflect-metadata": "^0.2.2", "twitter-api-v2": "^1.18.2", "viem": "^2.22.16", + "x402-axios": "^0.3.3", "zod": "^3.23.8" }, "devDependencies": { diff --git a/typescript/agentkit/src/action-providers/index.ts b/typescript/agentkit/src/action-providers/index.ts index 13856ca53..2ea47ab83 100644 --- a/typescript/agentkit/src/action-providers/index.ts +++ b/typescript/agentkit/src/action-providers/index.ts @@ -28,4 +28,5 @@ export * from "./allora"; export * from "./flaunch"; export * from "./onramp"; export * from "./vaultsfyi"; +export * from "./x402"; export * from "./zerodev"; diff --git a/typescript/agentkit/src/action-providers/x402/README.md b/typescript/agentkit/src/action-providers/x402/README.md new file mode 100644 index 000000000..0d540b73a --- /dev/null +++ b/typescript/agentkit/src/action-providers/x402/README.md @@ -0,0 +1,70 @@ +# X402 Action Provider + +This directory contains the **X402ActionProvider** implementation, which provides actions to interact with **x402-protected APIs** that require payment to access. + +## Directory Structure + +``` +x402/ +├── x402ActionProvider.ts # Main provider with x402 payment functionality +├── schemas.ts # x402 action schemas +├── index.ts # Main exports +└── README.md # This file +``` + +## Actions + +- `paid_request`: Make HTTP requests to x402-protected API endpoints with automatic payment handling +- `fetch_payment_info`: Get payment information from x402-protected endpoints without making payments + +## Overview + +The x402 protocol enables APIs to require micropayments for access. When a client makes a request to a protected endpoint, the server responds with a `402 Payment Required` status code along with payment instructions. This action provider automatically handles the entire payment flow: + +1. Makes the initial request to the protected API +2. If a 402 response is received, automatically processes the payment using the wallet +3. Retries the request with payment proof +4. Returns the API response data + +## Usage + +### `paid_request` Action + +The `paid_request` action accepts the following parameters: + +- **url**: The full URL of the x402-protected API endpoint +- **method**: HTTP method (GET, POST, PUT, DELETE, PATCH) - defaults to GET +- **headers**: Optional additional headers to include in the request +- **body**: Optional request body for POST/PUT/PATCH requests + +### `fetch_payment_info` Action + +The `fetch_payment_info` action accepts the following parameters: + +- **url**: The full URL of the x402-protected API endpoint +- **method**: HTTP method (GET, POST, PUT, DELETE, PATCH) - defaults to GET +- **headers**: Optional additional headers to include in the request + +This action is useful for: +- Checking payment requirements before committing to a paid request +- Understanding the cost structure of an API +- Getting details about accepted payment tokens and amounts +- Debugging x402 payment configurations + +## Network Support + +The x402 provider currently supports the following networks: +- `base-mainnet` +- `base-sepolia` + +The provider requires EVM-compatible networks where the wallet can sign payment transactions. + +## Dependencies + +This action provider requires: +- `axios` - For making HTTP requests +- `x402-axios` - For handling x402 payment flows + +## Notes + +For more information on the **x402 protocol**, visit the [x402 documentation](https://x402.gitbook.io/x402/). \ No newline at end of file diff --git a/typescript/agentkit/src/action-providers/x402/index.ts b/typescript/agentkit/src/action-providers/x402/index.ts new file mode 100644 index 000000000..670411a1e --- /dev/null +++ b/typescript/agentkit/src/action-providers/x402/index.ts @@ -0,0 +1 @@ +export * from "./x402ActionProvider"; diff --git a/typescript/agentkit/src/action-providers/x402/schemas.ts b/typescript/agentkit/src/action-providers/x402/schemas.ts new file mode 100644 index 000000000..a21d84dc2 --- /dev/null +++ b/typescript/agentkit/src/action-providers/x402/schemas.ts @@ -0,0 +1,26 @@ +import { z } from "zod"; + +export const PaidRequestSchema = z + .object({ + url: z.string().url().describe("The URL of the x402-protected API endpoint"), + method: z + .enum(["GET", "POST", "PUT", "DELETE", "PATCH"]) + .default("GET") + .describe("The HTTP method to use for the request"), + headers: z.record(z.string()).optional().describe("Optional headers to include in the request"), + body: z.any().optional().describe("Optional request body for POST/PUT/PATCH requests"), + }) + .strip() + .describe("Instructions for making a paid request to an x402-protected API"); + +export const FetchPaymentInfoSchema = z + .object({ + url: z.string().url().describe("The URL of the x402-protected API endpoint"), + method: z + .enum(["GET", "POST", "PUT", "DELETE", "PATCH"]) + .default("GET") + .describe("The HTTP method to use for the request"), + headers: z.record(z.string()).optional().describe("Optional headers to include in the request"), + }) + .strip() + .describe("Instructions for fetching payment information from an x402-protected API endpoint"); diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts new file mode 100644 index 000000000..ba4c3a4d6 --- /dev/null +++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.test.ts @@ -0,0 +1,523 @@ +import { X402ActionProvider } from "./x402ActionProvider"; +import { EvmWalletProvider } from "../../wallet-providers"; +import { Network } from "../../network"; +import { AxiosError, AxiosResponse, AxiosRequestConfig, AxiosInstance } from "axios"; +import axios from "axios"; +import * as x402axios from "x402-axios"; + +// Mock modules +jest.mock("axios"); +jest.mock("x402-axios"); + +// Create mock functions +const mockRequest = jest.fn(); + +// Create a complete mock axios instance +const mockAxiosInstance = { + request: mockRequest, + get: jest.fn(), + delete: jest.fn(), + head: jest.fn(), + options: jest.fn(), + post: jest.fn(), + put: jest.fn(), + patch: jest.fn(), + getUri: jest.fn(), + defaults: {}, + interceptors: { + request: { use: jest.fn(), eject: jest.fn(), clear: jest.fn() }, + response: { use: jest.fn(), eject: jest.fn(), clear: jest.fn() }, + }, +} as unknown as AxiosInstance; + +// Create a complete mock axios static +const mockAxios = { + create: jest.fn().mockReturnValue(mockAxiosInstance), + request: mockRequest, + get: jest.fn(), + delete: jest.fn(), + head: jest.fn(), + options: jest.fn(), + post: jest.fn(), + put: jest.fn(), + patch: jest.fn(), + all: jest.fn(), + spread: jest.fn(), + isAxiosError: jest.fn(), + isCancel: jest.fn(), + CancelToken: { + source: jest.fn(), + }, + VERSION: "1.x", +} as unknown as jest.Mocked; + +const mockWithPaymentInterceptor = jest.fn().mockReturnValue(mockAxiosInstance); +const mockDecodeXPaymentResponse = jest.fn(); + +// Override the mocked modules +(axios as jest.Mocked).create = mockAxios.create; +(axios as jest.Mocked).request = mockRequest; +(axios as jest.Mocked).isAxiosError = mockAxios.isAxiosError; + +// Mock x402-axios functions +jest.mocked(x402axios.withPaymentInterceptor).mockImplementation(mockWithPaymentInterceptor); +jest.mocked(x402axios.decodeXPaymentResponse).mockImplementation(mockDecodeXPaymentResponse); + +// Mock wallet provider +const mockWalletProvider = { + toSigner: jest.fn().mockReturnValue("mock-signer"), +} as unknown as EvmWalletProvider; + +// Sample responses based on real examples +const MOCK_PAYMENT_INFO_RESPONSE = { + paymentRequired: true, + url: "https://www.x402.org/protected", + status: 402, + data: { + x402Version: 1, + error: "X-PAYMENT header is required", + accepts: [ + { + scheme: "exact", + network: "base-sepolia", + maxAmountRequired: "10000", + resource: "https://www.x402.org/protected", + description: "Access to protected content", + mimeType: "application/json", + payTo: "0x209693Bc6afc0C5328bA36FaF03C514EF312287C", + maxTimeoutSeconds: 300, + asset: "0x036CbD53842c5426634e7929541eC2318f3dCF7e", + extra: { + name: "USDC", + version: "2", + }, + }, + ], + }, +}; + +const MOCK_PAYMENT_RESPONSE = { + success: true, + transaction: + "0xcbc385789d3744b52af5106c32809534f64adcbe097e050ec03d6b53fed5d305" as `0x${string}`, + network: "base-sepolia" as const, + payer: "0xa8c1a5D3C372C65c04f91f87a43F549619A9483f" as `0x${string}`, +}; + +const MOCK_PAID_REQUEST_RESPONSE = { + success: true, + url: "https://www.x402.org/protected", + method: "GET", + status: 200, + data: "...", + paymentResponse: MOCK_PAYMENT_RESPONSE, +}; + +describe("X402ActionProvider", () => { + let provider: X402ActionProvider; + + beforeEach(() => { + provider = new X402ActionProvider(); + jest.clearAllMocks(); + + // Setup mocks + mockAxios.create.mockReturnValue(mockAxiosInstance); + mockWithPaymentInterceptor.mockReturnValue(mockAxiosInstance); + + // Setup axios.isAxiosError mock + jest + .mocked(axios.isAxiosError) + .mockImplementation((error: unknown): boolean => + Boolean( + error && + typeof error === "object" && + ("isAxiosError" in error || "response" in error || "request" in error), + ), + ); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe("supportsNetwork", () => { + it("should support base-mainnet", () => { + const network: Network = { protocolFamily: "evm", networkId: "base-mainnet" }; + expect(provider.supportsNetwork(network)).toBe(true); + }); + + it("should support base-sepolia", () => { + const network: Network = { protocolFamily: "evm", networkId: "base-sepolia" }; + expect(provider.supportsNetwork(network)).toBe(true); + }); + + it("should not support unsupported EVM networks", () => { + const network: Network = { protocolFamily: "evm", networkId: "ethereum" }; + expect(provider.supportsNetwork(network)).toBe(false); + }); + + it("should not support non-EVM networks", () => { + const network: Network = { protocolFamily: "solana", networkId: "mainnet" }; + expect(provider.supportsNetwork(network)).toBe(false); + }); + }); + + describe("fetchPaymentInfo", () => { + it("should successfully fetch payment info for 402 response", async () => { + mockRequest.mockResolvedValue({ + status: 402, + statusText: "Payment Required", + data: MOCK_PAYMENT_INFO_RESPONSE.data, + headers: {}, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + expect(mockRequest).toHaveBeenCalledWith({ + url: "https://www.x402.org/protected", + method: "GET", + headers: undefined, + validateStatus: expect.any(Function), + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentRequired).toBe(true); + expect(parsedResult.status).toBe(402); + expect(parsedResult.data).toEqual(MOCK_PAYMENT_INFO_RESPONSE.data); + }); + + it("should handle non-payment-protected endpoints", async () => { + mockRequest.mockResolvedValue({ + status: 200, + statusText: "OK", + data: { message: "No payment required" }, + headers: {}, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://api.example.com/free", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentRequired).toBe(false); + expect(parsedResult.status).toBe(200); + expect(parsedResult.data).toEqual({ message: "No payment required" }); + }); + + it("should handle 402 errors with payment details in headers", async () => { + mockDecodeXPaymentResponse.mockReturnValue(MOCK_PAYMENT_RESPONSE); + + const error = new Error("Payment required") as AxiosError; + error.isAxiosError = true; + error.response = { + status: 402, + statusText: "Payment Required", + headers: { + "x-payment-response": "encoded-payment-data", + }, + data: MOCK_PAYMENT_INFO_RESPONSE.data, + config: {} as AxiosRequestConfig, + } as AxiosResponse; + + mockRequest.mockRejectedValue(error); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + expect(mockDecodeXPaymentResponse).toHaveBeenCalledWith("encoded-payment-data"); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentRequired).toBe(true); + expect(parsedResult.status).toBe(402); + expect(parsedResult.paymentDetails).toEqual(MOCK_PAYMENT_RESPONSE); + }); + + it("should fallback to JSON.parse when decodeXPaymentResponse fails", async () => { + const paymentDetailsJson = '{"amount": "10000"}'; + mockDecodeXPaymentResponse.mockImplementation(() => { + throw new Error("Decode failed"); + }); + + const error = new Error("Payment required") as AxiosError; + error.isAxiosError = true; + error.response = { + status: 402, + statusText: "Payment Required", + headers: { + "x-payment-response": paymentDetailsJson, + }, + data: MOCK_PAYMENT_INFO_RESPONSE.data, + config: {} as AxiosRequestConfig, + } as AxiosResponse; + + mockRequest.mockRejectedValue(error); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentDetails).toEqual({ amount: "10000" }); + }); + + it("should handle payment header parsing failures", async () => { + mockDecodeXPaymentResponse.mockImplementation(() => { + throw new Error("Decode failed"); + }); + + const error = new Error("Payment required") as AxiosError; + error.isAxiosError = true; + error.response = { + status: 402, + statusText: "Payment Required", + headers: { + "x-payment-response": "invalid-json", + }, + data: MOCK_PAYMENT_INFO_RESPONSE.data, + config: {} as AxiosRequestConfig, + } as AxiosResponse; + + mockRequest.mockRejectedValue(error); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentDetails.error).toBe("Failed to decode payment response"); + expect(parsedResult.paymentDetails.rawHeader).toBe("invalid-json"); + }); + + it("should handle non-402 HTTP errors", async () => { + const error = new Error("Server error") as AxiosError; + error.isAxiosError = true; + error.response = { + status: 500, + statusText: "Internal Server Error", + headers: {}, + data: { error: "Internal server error" }, + config: {} as AxiosRequestConfig, + } as AxiosResponse; + + mockRequest.mockRejectedValue(error); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "GET", + }); + + expect(result).toContain("Error fetching payment info"); + expect(result).toContain("HTTP 500"); + expect(result).toContain("Internal server error"); + }); + + it("should handle network errors", async () => { + const error = new Error("Network error") as AxiosError; + error.isAxiosError = true; + error.request = {}; + + mockRequest.mockRejectedValue(error); + + const result = await provider.fetchPaymentInfo(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "GET", + }); + + expect(result).toContain("Error fetching payment info"); + expect(result).toContain("Network error"); + }); + }); + + describe("paidRequest", () => { + it("should successfully make a paid request with payment response", async () => { + mockDecodeXPaymentResponse.mockReturnValue(MOCK_PAYMENT_RESPONSE); + + mockRequest.mockResolvedValue({ + status: 200, + statusText: "OK", + data: MOCK_PAID_REQUEST_RESPONSE.data, + headers: { + "x-payment-response": "encoded-payment-response", + }, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + expect(mockWithPaymentInterceptor).toHaveBeenCalledWith(mockAxiosInstance, "mock-signer"); + + expect(mockRequest).toHaveBeenCalledWith({ + url: "https://www.x402.org/protected", + method: "GET", + headers: undefined, + data: undefined, + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.success).toBe(true); + expect(parsedResult.status).toBe(200); + expect(parsedResult.paymentResponse).toEqual(MOCK_PAYMENT_RESPONSE); + }); + + it("should handle successful request without payment", async () => { + mockRequest.mockResolvedValue({ + status: 200, + statusText: "OK", + data: { message: "Success" }, + headers: {}, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://api.example.com/free", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.success).toBe(true); + expect(parsedResult.status).toBe(200); + expect(parsedResult.paymentResponse).toBe(null); + }); + + it("should fallback to JSON.parse when decodeXPaymentResponse fails", async () => { + const paymentResponseJson = '{"transaction": "0x123"}'; + + mockDecodeXPaymentResponse.mockImplementation(() => { + throw new Error("Decode failed"); + }); + + mockRequest.mockResolvedValue({ + status: 200, + statusText: "OK", + data: "Success", + headers: { + "x-payment-response": paymentResponseJson, + }, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentResponse).toEqual({ transaction: "0x123" }); + }); + + it("should handle payment response parsing failures", async () => { + mockDecodeXPaymentResponse.mockImplementation(() => { + throw new Error("Decode failed"); + }); + + mockRequest.mockResolvedValue({ + status: 200, + statusText: "OK", + data: "Success", + headers: { + "x-payment-response": "invalid-json", + }, + config: {} as AxiosRequestConfig, + } as AxiosResponse); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://www.x402.org/protected", + method: "GET", + }); + + const parsedResult = JSON.parse(result); + expect(parsedResult.paymentResponse.error).toBe("Failed to decode payment response"); + expect(parsedResult.paymentResponse.rawHeader).toBe("invalid-json"); + }); + + it("should handle HTTP errors", async () => { + const error = new Error("Bad request") as AxiosError; + error.isAxiosError = true; + error.response = { + status: 400, + statusText: "Bad Request", + headers: {}, + data: { error: "Invalid parameters" }, + config: {} as AxiosRequestConfig, + } as AxiosResponse; + + mockRequest.mockRejectedValue(error); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "POST", + body: { test: "data" }, + }); + + expect(result).toContain("Error making paid request"); + expect(result).toContain("HTTP 400"); + expect(result).toContain("Invalid parameters"); + }); + + it("should handle network errors", async () => { + const error = new Error("Connection timeout") as AxiosError; + error.isAxiosError = true; + error.request = {}; + + mockRequest.mockRejectedValue(error); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "GET", + }); + + expect(result).toContain("Error making paid request"); + expect(result).toContain("Network error"); + expect(result).toContain("Connection timeout"); + }); + + it("should handle generic errors", async () => { + const error = new Error("Something went wrong"); + + mockRequest.mockRejectedValue(error); + + const result = await provider.paidRequest(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "GET", + }); + + expect(result).toContain("Error making paid request"); + expect(result).toContain("Something went wrong"); + }); + + it("should pass through all request parameters", async () => { + mockRequest.mockResolvedValue({ + status: 200, + data: "Success", + headers: {}, + } as AxiosResponse); + + await provider.paidRequest(mockWalletProvider, { + url: "https://api.example.com/endpoint", + method: "POST", + headers: { "Content-Type": "application/json" }, + body: { key: "value" }, + }); + + expect(mockRequest).toHaveBeenCalledWith({ + url: "https://api.example.com/endpoint", + method: "POST", + headers: { "Content-Type": "application/json" }, + data: { key: "value" }, + }); + }); + }); +}); diff --git a/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts new file mode 100644 index 000000000..19d3cf459 --- /dev/null +++ b/typescript/agentkit/src/action-providers/x402/x402ActionProvider.ts @@ -0,0 +1,234 @@ +import { z } from "zod"; +import { ActionProvider } from "../actionProvider"; +import { Network } from "../../network"; +import { CreateAction } from "../actionDecorator"; +import { PaidRequestSchema, FetchPaymentInfoSchema } from "./schemas"; +import { EvmWalletProvider } from "../../wallet-providers"; +import axios, { AxiosError } from "axios"; +import { withPaymentInterceptor, decodeXPaymentResponse } from "x402-axios"; + +const SUPPORTED_NETWORKS = ["base-mainnet", "base-sepolia"]; + +/** + * X402ActionProvider is an action provider for making paid requests to x402-protected APIs. + */ +export class X402ActionProvider extends ActionProvider { + /** + * Constructor for the X402ActionProvider. + */ + constructor() { + super("x402", []); + } + + /** + * Makes a paid request to an x402-protected API endpoint. + * + * @param walletProvider - The wallet provider to use for payment signing. + * @param args - The input arguments for the action. + * @returns A message containing the API response data. + */ + @CreateAction({ + name: "paid_request", + description: ` +This tool makes HTTP requests to APIs that are protected by x402 paywalls. It automatically handles the payment flow when a 402 Payment Required response is received. + +Inputs: +- url: The full URL of the x402-protected API endpoint +- method: The HTTP method (GET, POST, PUT, DELETE, PATCH) - defaults to GET +- headers: Optional additional headers to include in the request +- body: Optional request body for POST/PUT/PATCH requests + +The tool will: +1. Make the initial request to the protected endpoint +2. If a 402 Payment Required response is received, automatically handle the payment using the wallet +3. Retry the request with payment proof +4. Return the API response data + +Supported on EVM networks where the wallet can sign payment transactions. +`, + schema: PaidRequestSchema, + }) + async paidRequest( + walletProvider: EvmWalletProvider, + args: z.infer, + ): Promise { + try { + // Get the viem account from the wallet provider for x402-axios + const account = walletProvider.toSigner(); + + // Create an axios instance with the payment interceptor + const api = withPaymentInterceptor(axios.create({}), account); + + // Make the request + const response = await api.request({ + url: args.url, + method: args.method, + headers: args.headers, + data: args.body, + }); + + // Extract payment information if available + const paymentResponseHeader = response.headers["x-payment-response"]; + let paymentResponse: Record | null = null; + + if (paymentResponseHeader) { + try { + paymentResponse = decodeXPaymentResponse(paymentResponseHeader); + } catch { + // Fall back to JSON parsing if decodeXPaymentResponse fails + try { + paymentResponse = JSON.parse(paymentResponseHeader); + } catch { + paymentResponse = { + error: "Failed to decode payment response", + rawHeader: paymentResponseHeader, + }; + } + } + } + + // Structure the response to clearly separate API response and payment details + const result = { + success: true, + url: args.url, + method: args.method, + status: response.status, + data: response.data, + paymentResponse: paymentResponse, + }; + + return JSON.stringify(result, null, 2); + } catch (error) { + const axiosError = error as AxiosError<{ error?: string }>; + if (axiosError.response) { + return `Error making paid request to ${args.url}: HTTP ${axiosError.response.status} - ${axiosError.response.data?.error || axiosError.response.statusText}`; + } else if (axiosError.request) { + return `Error making paid request to ${args.url}: Network error - ${axiosError.message}`; + } else { + return `Error making paid request to ${args.url}: ${axiosError.message}`; + } + } + } + + /** + * Fetches payment information from an x402-protected API endpoint without making the payment. + * + * @param walletProvider - The wallet provider (not used for this action but required by interface). + * @param args - The input arguments for the action. + * @returns A message containing the payment requirements and endpoint information. + */ + @CreateAction({ + name: "fetch_payment_info", + description: ` +This tool fetches payment information from x402-protected API endpoints without actually making any payments. It's useful for checking payment requirements before deciding whether to proceed with a paid request. + +Inputs: +- url: The full URL of the x402-protected API endpoint +- method: The HTTP method (GET, POST, PUT, DELETE, PATCH) - defaults to GET +- headers: Optional additional headers to include in the request + +The tool will: +1. Make a request to the protected endpoint +2. Receive the 402 Payment Required response with payment details +3. Return information about the payment requirements (amount, token, etc.) + +Note: Payment amounts are returned in the smallest unit of the token. For example, for USDC (which has 6 decimal places) maxAmountRequired "10000" corresponds to 0.01 USDC. + +This is useful for understanding what payment will be required before using the paid_request action. +`, + schema: FetchPaymentInfoSchema, + }) + async fetchPaymentInfo( + walletProvider: EvmWalletProvider, + args: z.infer, + ): Promise { + try { + // Make a simple axios request without payment interceptor to get the 402 response + const response = await axios.request({ + url: args.url, + method: args.method, + headers: args.headers, + validateStatus: status => status === 402 || (status >= 200 && status < 300), // Accept 402 responses + }); + + if (response.status === 402) { + return JSON.stringify( + { + paymentRequired: true, + url: args.url, + status: response.status, + data: response.data, + }, + null, + 2, + ); + } else { + // Endpoint is not payment-protected or request succeeded without payment + return JSON.stringify( + { + paymentRequired: false, + url: args.url, + status: response.status, + data: response.data, + }, + null, + 2, + ); + } + } catch (error) { + const axiosError = error as AxiosError<{ error?: string }>; + if (axiosError.response) { + if (axiosError.response.status === 402) { + // Handle 402 responses that axios might treat as errors + const paymentResponseHeader = axiosError.response.headers["x-payment-response"]; + let paymentDetails: Record | null = null; + + if (paymentResponseHeader) { + try { + paymentDetails = decodeXPaymentResponse(paymentResponseHeader); + } catch { + // Fall back to JSON parsing if decodeXPaymentResponse fails + try { + paymentDetails = JSON.parse(paymentResponseHeader); + } catch { + paymentDetails = { + error: "Failed to decode payment response", + rawHeader: paymentResponseHeader, + }; + } + } + } + + return JSON.stringify( + { + paymentRequired: true, + url: args.url, + status: 402, + paymentDetails: paymentDetails, + data: axiosError.response.data, + }, + null, + 2, + ); + } else { + return `Error fetching payment info from ${args.url}: HTTP ${axiosError.response.status} - ${axiosError.response.data?.error || axiosError.response.statusText}`; + } + } else if (axiosError.request) { + return `Error fetching payment info from ${args.url}: Network error - ${axiosError.message}`; + } else { + return `Error fetching payment info from ${args.url}: ${axiosError.message}`; + } + } + } + + /** + * Checks if the X402 action provider supports the given network. + * + * @param network - The network to check. + * @returns True if the X402 action provider supports the network, false otherwise. + */ + supportsNetwork = (network: Network) => + network.protocolFamily === "evm" && SUPPORTED_NETWORKS.includes(network.networkId!); +} + +export const x402ActionProvider = () => new X402ActionProvider(); diff --git a/typescript/pnpm-lock.yaml b/typescript/pnpm-lock.yaml index 6d28c41c6..50d2ff5e0 100644 --- a/typescript/pnpm-lock.yaml +++ b/typescript/pnpm-lock.yaml @@ -98,6 +98,9 @@ importers: '@zerodev/sdk': specifier: ^5.4.28 version: 5.4.28(viem@2.23.15(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2)) + axios: + specifier: ^1.9.0 + version: 1.9.0 bs58: specifier: ^4.0.1 version: 4.0.1 @@ -125,6 +128,9 @@ importers: viem: specifier: ^2.22.16 version: 2.23.15(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2) + x402-axios: + specifier: ^0.3.3 + version: 0.3.3(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10) zod: specifier: ^3.23.8 version: 3.24.2 @@ -152,7 +158,7 @@ importers: version: 14.1.1 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) + version: 29.7.0(@types/node@20.17.27)(ts-node@10.9.2(@types/node@20.17.27)(typescript@5.8.2)) mock-fs: specifier: ^5.2.0 version: 5.5.0 @@ -170,7 +176,7 @@ importers: version: 2.4.2 ts-jest: specifier: ^29.2.5 - version: 29.3.0(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)))(typescript@5.8.2) + version: 29.3.0(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@20.17.27)(ts-node@10.9.2(@types/node@20.17.27)(typescript@5.8.2)))(typescript@5.8.2) tsd: specifier: ^0.31.2 version: 0.31.2 @@ -386,13 +392,13 @@ importers: version: link:../../framework-extensions/langchain '@langchain/core': specifier: ^0.3.19 - version: 0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) + version: 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) '@langchain/langgraph': specifier: ^0.2.21 - version: 0.2.59(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)(zod-to-json-schema@3.24.5(zod@3.24.2)) + version: 0.2.59(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)(zod-to-json-schema@3.24.5(zod@3.24.2)) '@langchain/openai': specifier: ^0.3.14 - version: 0.3.17(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + version: 0.3.17(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) '@solana/web3.js': specifier: ^1.98.0 version: 1.98.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) @@ -2014,8 +2020,8 @@ packages: peerDependencies: axios: 0.x || 1.x - axios@1.8.4: - resolution: {integrity: sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==} + axios@1.9.0: + resolution: {integrity: sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==} babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} @@ -3775,6 +3781,7 @@ packages: node-domexception@1.0.0: resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead node-fetch-native@1.6.6: resolution: {integrity: sha512-8Mc2HhqPdlIfedsuZoc3yioPuzp6b+L5jRCRY1QzuWZh2EGJVQrGppC6V6cF0bLdbW0+O2YpqCA25aF/1lvipQ==} @@ -4968,6 +4975,12 @@ packages: utf-8-validate: optional: true + x402-axios@0.3.3: + resolution: {integrity: sha512-WaqYBO6QCLtON6YYHoN1vCxVaWXXTDOltZnwo7VjvWc9gYoQRismONVu2VC6PK5SHad+Ha9wsJJrgL9NBF83Bg==} + + x402@0.3.7: + resolution: {integrity: sha512-8g2sXjWX7UbUNg9wJqgSBoYP7QV3/7qYYssdfPiQM5XDDThuVy7+MnH4cCuQ4UGGn2SVz1hpzWpwxMC3nwp+zA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -5422,7 +5435,7 @@ snapshots: dependencies: '@solana/web3.js': 1.98.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10) abitype: 1.0.6(typescript@5.8.2)(zod@3.24.2) - axios: 1.8.4 + axios: 1.9.0 jose: 6.0.10 md5: 2.3.0 viem: 2.24.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.24.2) @@ -5438,9 +5451,9 @@ snapshots: dependencies: '@scure/bip32': 1.6.2 abitype: 1.0.8(typescript@5.8.2)(zod@3.24.2) - axios: 1.8.4 - axios-mock-adapter: 1.22.0(axios@1.8.4) - axios-retry: 4.5.0(axios@1.8.4) + axios: 1.9.0 + axios-mock-adapter: 1.22.0(axios@1.9.0) + axios-retry: 4.5.0(axios@1.9.0) bip32: 4.0.0 bip39: 3.1.0 decimal.js: 10.5.0 @@ -5902,41 +5915,6 @@ snapshots: - supports-color - ts-node - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2))': - dependencies: - '@jest/console': 29.7.0 - '@jest/reporters': 29.7.0 - '@jest/test-result': 29.7.0 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - '@types/node': 20.17.27 - ansi-escapes: 4.3.2 - chalk: 4.1.2 - ci-info: 3.9.0 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.17.27)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - jest-haste-map: 29.7.0 - jest-message-util: 29.7.0 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-resolve-dependencies: 29.7.0 - jest-runner: 29.7.0 - jest-runtime: 29.7.0 - jest-snapshot: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - jest-watcher: 29.7.0 - micromatch: 4.0.8 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-ansi: 6.0.1 - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - ts-node - '@jest/environment@29.7.0': dependencies: '@jest/fake-timers': 29.7.0 @@ -6079,23 +6057,6 @@ snapshots: '@jup-ag/api@6.0.40': {} - '@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2))': - dependencies: - '@cfworker/json-schema': 4.1.1 - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.19 - langsmith: 0.2.15(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) - mustache: 4.2.0 - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 10.0.0 - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) - transitivePeerDependencies: - - openai - '@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2))': dependencies: '@cfworker/json-schema': 4.1.1 @@ -6113,26 +6074,11 @@ snapshots: transitivePeerDependencies: - openai - '@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))': - dependencies: - '@langchain/core': 0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) - uuid: 10.0.0 - '@langchain/langgraph-checkpoint@0.0.16(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))': dependencies: '@langchain/core': 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) uuid: 10.0.0 - '@langchain/langgraph-sdk@0.0.60(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.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.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) - react: 18.3.1 - '@langchain/langgraph-sdk@0.0.60(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)': dependencies: '@types/json-schema': 7.0.15 @@ -6143,18 +6089,6 @@ snapshots: '@langchain/core': 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) react: 18.3.1 - '@langchain/langgraph@0.2.59(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)(zod-to-json-schema@3.24.5(zod@3.24.2))': - dependencies: - '@langchain/core': 0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) - '@langchain/langgraph-checkpoint': 0.0.16(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2))) - '@langchain/langgraph-sdk': 0.0.60(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1) - uuid: 10.0.0 - zod: 3.24.2 - optionalDependencies: - zod-to-json-schema: 3.24.5(zod@3.24.2) - transitivePeerDependencies: - - react - '@langchain/langgraph@0.2.59(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(react@18.3.1)(zod-to-json-schema@3.24.5(zod@3.24.2))': dependencies: '@langchain/core': 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) @@ -6167,17 +6101,6 @@ snapshots: transitivePeerDependencies: - react - '@langchain/openai@0.3.17(@langchain/core@0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))': - dependencies: - '@langchain/core': 0.3.30(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) - js-tiktoken: 1.0.19 - openai: 4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2) - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) - transitivePeerDependencies: - - encoding - - ws - '@langchain/openai@0.3.17(@langchain/core@0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)))(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))': dependencies: '@langchain/core': 0.3.30(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)) @@ -6862,6 +6785,11 @@ snapshots: typescript: 5.8.2 zod: 3.24.2 + abitype@1.0.8(typescript@5.8.2)(zod@3.25.56): + optionalDependencies: + typescript: 5.8.2 + zod: 3.25.56 + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -7003,18 +6931,18 @@ snapshots: dependencies: possible-typed-array-names: 1.1.0 - axios-mock-adapter@1.22.0(axios@1.8.4): + axios-mock-adapter@1.22.0(axios@1.9.0): dependencies: - axios: 1.8.4 + axios: 1.9.0 fast-deep-equal: 3.1.3 is-buffer: 2.0.5 - axios-retry@4.5.0(axios@1.8.4): + axios-retry@4.5.0(axios@1.9.0): dependencies: - axios: 1.8.4 + axios: 1.9.0 is-retry-allowed: 2.2.0 - axios@1.8.4: + axios@1.9.0: dependencies: follow-redirects: 1.15.9 form-data: 4.0.2 @@ -7367,21 +7295,6 @@ snapshots: - supports-color - ts-node - create-jest@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)): - dependencies: - '@jest/types': 29.6.3 - chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - jest-util: 29.7.0 - prompts: 2.4.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - create-require@1.1.1: {} cross-spawn@7.0.6: @@ -8616,25 +8529,6 @@ snapshots: - supports-color - ts-node - jest-cli@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - '@jest/test-result': 29.7.0 - '@jest/types': 29.6.3 - chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - exit: 0.1.2 - import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - jest-util: 29.7.0 - jest-validate: 29.7.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jest-config@29.7.0(@types/node@20.17.27)(ts-node@10.9.2(@types/node@20.17.27)(typescript@5.8.2)): dependencies: '@babel/core': 7.26.10 @@ -8666,68 +8560,6 @@ snapshots: - babel-plugin-macros - supports-color - jest-config@29.7.0(@types/node@20.17.27)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)): - dependencies: - '@babel/core': 7.26.10 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.10) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 20.17.27 - ts-node: 10.9.2(@types/node@22.13.14)(typescript@5.8.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - - jest-config@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)): - dependencies: - '@babel/core': 7.26.10 - '@jest/test-sequencer': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.10) - chalk: 4.1.2 - ci-info: 3.9.0 - deepmerge: 4.3.1 - glob: 7.2.3 - graceful-fs: 4.2.11 - jest-circus: 29.7.0 - jest-environment-node: 29.7.0 - jest-get-type: 29.6.3 - jest-regex-util: 29.6.3 - jest-resolve: 29.7.0 - jest-runner: 29.7.0 - jest-util: 29.7.0 - jest-validate: 29.7.0 - micromatch: 4.0.8 - parse-json: 5.2.0 - pretty-format: 29.7.0 - slash: 3.0.0 - strip-json-comments: 3.1.1 - optionalDependencies: - '@types/node': 22.13.14 - ts-node: 10.9.2(@types/node@22.13.14)(typescript@5.8.2) - transitivePeerDependencies: - - babel-plugin-macros - - supports-color - jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -8955,18 +8787,6 @@ snapshots: - supports-color - ts-node - jest@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)): - dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - '@jest/types': 29.6.3 - import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - transitivePeerDependencies: - - '@types/node' - - babel-plugin-macros - - supports-color - - ts-node - jose@4.15.9: {} jose@5.10.0: {} @@ -9032,17 +8852,6 @@ snapshots: kleur@3.0.3: {} - langsmith@0.2.15(openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)): - dependencies: - '@types/uuid': 10.0.0 - commander: 10.0.1 - p-queue: 6.6.2 - p-retry: 4.6.2 - semver: 7.7.1 - uuid: 10.0.0 - optionalDependencies: - openai: 4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2) - langsmith@0.2.15(openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2)): dependencies: '@types/uuid': 10.0.0 @@ -9374,21 +9183,6 @@ snapshots: dependencies: mimic-function: 5.0.1 - openai@4.89.1(ws@7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2): - dependencies: - '@types/node': 18.19.83 - '@types/node-fetch': 2.6.12 - abort-controller: 3.0.0 - agentkeepalive: 4.6.0 - form-data-encoder: 1.7.2 - formdata-node: 4.4.1 - node-fetch: 2.7.0 - optionalDependencies: - ws: 7.5.10(bufferutil@4.0.9)(utf-8-validate@5.0.10) - zod: 3.24.2 - transitivePeerDependencies: - - encoding - openai@4.89.1(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10))(zod@3.24.2): dependencies: '@types/node': 18.19.83 @@ -9471,6 +9265,20 @@ snapshots: transitivePeerDependencies: - zod + ox@0.6.9(typescript@5.8.2)(zod@3.25.56): + 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.8.2)(zod@3.25.56) + eventemitter3: 5.0.1 + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - zod + p-filter@2.1.0: dependencies: p-map: 2.1.0 @@ -10173,26 +9981,6 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.26.10) - ts-jest@29.3.0(@babel/core@7.26.10)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.10))(jest@29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)))(typescript@5.8.2): - dependencies: - bs-logger: 0.2.6 - ejs: 3.1.10 - fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.13.14)(ts-node@10.9.2(@types/node@22.13.14)(typescript@5.8.2)) - jest-util: 29.7.0 - json5: 2.2.3 - lodash.memoize: 4.1.2 - make-error: 1.3.6 - semver: 7.7.1 - type-fest: 4.38.0 - typescript: 5.8.2 - yargs-parser: 21.1.1 - optionalDependencies: - '@babel/core': 7.26.10 - '@jest/transform': 29.7.0 - '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.10) - ts-node@10.9.2(@types/node@18.19.83)(typescript@5.8.2): dependencies: '@cspotcode/source-map-support': 0.8.1 @@ -10499,6 +10287,23 @@ snapshots: - utf-8-validate - zod + viem@2.24.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.56): + 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.8.2)(zod@3.25.56) + isows: 1.0.6(ws@8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10)) + ox: 0.6.9(typescript@5.8.2)(zod@3.25.56) + ws: 8.18.1(bufferutil@4.0.9)(utf-8-validate@5.0.10) + optionalDependencies: + typescript: 5.8.2 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + - zod + walker@1.0.8: dependencies: makeerror: 1.0.12 @@ -10613,6 +10418,27 @@ snapshots: bufferutil: 4.0.9 utf-8-validate: 5.0.10 + x402-axios@0.3.3(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10): + dependencies: + axios: 1.9.0 + viem: 2.24.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.56) + x402: 0.3.7(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10) + zod: 3.25.56 + transitivePeerDependencies: + - bufferutil + - debug + - typescript + - utf-8-validate + + x402@0.3.7(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10): + dependencies: + viem: 2.24.1(bufferutil@4.0.9)(typescript@5.8.2)(utf-8-validate@5.0.10)(zod@3.25.56) + zod: 3.25.56 + transitivePeerDependencies: + - bufferutil + - typescript + - utf-8-validate + y18n@5.0.8: {} yallist@3.1.1: {}