@coinbase/agentkit
Version:
Coinbase AgentKit core primitives
289 lines (280 loc) • 13.1 kB
JavaScript
"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.x402ActionProvider = exports.X402ActionProvider = void 0;
const zod_1 = require("zod");
const actionProvider_1 = require("../actionProvider");
const actionDecorator_1 = require("../actionDecorator");
const schemas_1 = require("./schemas");
const wallet_providers_1 = require("../../wallet-providers");
const axios_1 = __importDefault(require("axios"));
const x402_axios_1 = require("x402-axios");
const SUPPORTED_NETWORKS = ["base-mainnet", "base-sepolia"];
/**
* X402ActionProvider provides actions for making HTTP requests, with optional x402 payment handling.
*/
class X402ActionProvider extends actionProvider_1.ActionProvider {
/**
* Creates a new instance of X402ActionProvider.
* Initializes the provider with x402 capabilities.
*/
constructor() {
super("x402", []);
/**
* Checks if the action provider supports the given network.
*
* @param network - The network to check support for
* @returns True if the network is supported, false otherwise
*/
this.supportsNetwork = (network) => network.protocolFamily === "evm" && SUPPORTED_NETWORKS.includes(network.networkId);
}
/**
* Makes a basic HTTP request to an API endpoint.
*
* @param walletProvider - The wallet provider to use for potential payments
* @param args - The request parameters including URL, method, headers, and body
* @returns A JSON string containing the response or error details
*/
async makeHttpRequest(walletProvider, args) {
try {
const response = await axios_1.default.request({
url: args.url,
method: args.method ?? "GET",
headers: args.headers ?? undefined,
data: args.body,
validateStatus: status => status === 402 || (status >= 200 && status < 300),
});
if (response.status !== 402) {
return JSON.stringify({
success: true,
url: args.url,
method: args.method,
status: response.status,
data: response.data,
}, null, 2);
}
return JSON.stringify({
status: "error_402_payment_required",
acceptablePaymentOptions: response.data.accepts,
nextSteps: [
"Inform the user that the requested server replied with a 402 Payment Required response.",
`The payment options are: ${response.data.accepts.map(option => `${option.asset} ${option.maxAmountRequired} ${option.network}`).join(", ")}`,
"Ask the user if they want to retry the request with payment.",
`Use retry_http_request_with_x402 to retry the request with payment.`,
],
});
}
catch (error) {
return this.handleHttpError(error, args.url);
}
}
/**
* Retries a request with x402 payment after receiving a 402 response.
*
* @param walletProvider - The wallet provider to use for making the payment
* @param args - The request parameters including URL, method, headers, body, and payment option
* @returns A JSON string containing the response with payment details or error information
*/
async retryWithX402(walletProvider, args) {
try {
// Make the request with payment handling
const account = walletProvider.toSigner();
const paymentSelector = (accepts) => {
const { scheme, network, maxAmountRequired, asset } = args.selectedPaymentOption;
let paymentRequirements = accepts.find(accept => accept.scheme === scheme &&
accept.network === network &&
accept.maxAmountRequired <= maxAmountRequired &&
accept.asset === asset);
if (paymentRequirements) {
return paymentRequirements;
}
paymentRequirements = accepts.find(accept => accept.scheme === scheme &&
accept.network === network &&
accept.maxAmountRequired <= maxAmountRequired &&
accept.asset === asset);
if (paymentRequirements) {
return paymentRequirements;
}
return accepts[0];
};
const api = (0, x402_axios_1.withPaymentInterceptor)(axios_1.default.create({}), account, paymentSelector);
const response = await api.request({
url: args.url,
method: args.method ?? "GET",
headers: args.headers ?? undefined,
data: args.body,
});
// Check for payment proof
const paymentProof = response.headers["x-payment-response"]
? (0, x402_axios_1.decodeXPaymentResponse)(response.headers["x-payment-response"])
: null;
return JSON.stringify({
status: "success",
data: response.data,
message: "Request completed successfully with payment",
details: {
url: args.url,
method: args.method,
paymentUsed: {
network: args.selectedPaymentOption.network,
asset: args.selectedPaymentOption.asset,
amount: args.selectedPaymentOption.maxAmountRequired,
},
paymentProof: paymentProof
? {
transaction: paymentProof.transaction,
network: paymentProof.network,
payer: paymentProof.payer,
}
: null,
},
});
}
catch (error) {
return this.handleHttpError(error, args.url);
}
}
/**
* Makes an HTTP request with automatic x402 payment handling.
*
* @param walletProvider - The wallet provider to use for automatic payments
* @param args - The request parameters including URL, method, headers, and body
* @returns A JSON string containing the response with optional payment details or error information
*/
async makeHttpRequestWithX402(walletProvider, args) {
try {
const account = walletProvider.toSigner();
const api = (0, x402_axios_1.withPaymentInterceptor)(axios_1.default.create({}), account);
const response = await api.request({
url: args.url,
method: args.method ?? "GET",
headers: args.headers ?? undefined,
data: args.body,
});
// Check for payment proof
const paymentProof = response.headers["x-payment-response"]
? (0, x402_axios_1.decodeXPaymentResponse)(response.headers["x-payment-response"])
: null;
return JSON.stringify({
success: true,
message: "Request completed successfully (payment handled automatically if required)",
url: args.url,
method: args.method,
status: response.status,
data: response.data,
paymentProof: paymentProof
? {
transaction: paymentProof.transaction,
network: paymentProof.network,
payer: paymentProof.payer,
}
: null,
}, null, 2);
}
catch (error) {
return this.handleHttpError(error, args.url);
}
}
/**
* Helper method to handle HTTP errors consistently.
*
* @param error - The axios error to handle
* @param url - The URL that was being accessed when the error occurred
* @returns A JSON string containing formatted error details
*/
handleHttpError(error, url) {
if (error.response) {
return JSON.stringify({
error: true,
message: `HTTP ${error.response.status} error when accessing ${url}`,
details: error.response.data?.error || error.response.statusText,
suggestion: "Check if the URL is correct and the API is available.",
}, null, 2);
}
if (error.request) {
return JSON.stringify({
error: true,
message: `Network error when accessing ${url}`,
details: error.message,
suggestion: "Check your internet connection and verify the API endpoint is accessible.",
}, null, 2);
}
return JSON.stringify({
error: true,
message: `Error making request to ${url}`,
details: error.message,
suggestion: "Please check the request parameters and try again.",
}, null, 2);
}
}
exports.X402ActionProvider = X402ActionProvider;
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "make_http_request",
description: `
Makes a basic HTTP request to an API endpoint. If the endpoint requires payment (returns 402),
it will return payment details that can be used with retry_http_request_with_x402.
EXAMPLES:
- Production API: make_http_request("https://api.example.com/weather")
- Local development: make_http_request("http://localhost:3000/api/data")
- Testing x402: make_http_request("http://localhost:3000/protected")
If you receive a 402 Payment Required response, use retry_http_request_with_x402 to handle the payment.`,
schema: schemas_1.HttpRequestSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], X402ActionProvider.prototype, "makeHttpRequest", null);
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "retry_http_request_with_x402",
description: `
Retries an HTTP request with x402 payment after receiving a 402 Payment Required response.
This should be used after make_http_request returns a 402 response.
EXAMPLE WORKFLOW:
1. First call make_http_request("http://localhost:3000/protected")
2. If you get a 402 response, use this action to retry with payment
3. Pass the entire original response to this action
DO NOT use this action directly without first trying make_http_request!`,
schema: schemas_1.RetryWithX402Schema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], X402ActionProvider.prototype, "retryWithX402", null);
__decorate([
(0, actionDecorator_1.CreateAction)({
name: "make_http_request_with_x402",
description: `
⚠️ WARNING: This action automatically handles payments without asking for confirmation!
Only use this when explicitly told to skip the confirmation flow.
For most cases, you should:
1. First try make_http_request
2. Then use retry_http_request_with_x402 if payment is required
This action combines both steps into one, which means:
- No chance to review payment details before paying
- No confirmation step
- Automatic payment processing
EXAMPLES:
- Production: make_http_request_with_x402("https://api.example.com/data")
- Local dev: make_http_request_with_x402("http://localhost:3000/protected")
Unless specifically instructed otherwise, prefer the two-step approach with make_http_request first.`,
schema: schemas_1.DirectX402RequestSchema,
}),
__metadata("design:type", Function),
__metadata("design:paramtypes", [wallet_providers_1.EvmWalletProvider, void 0]),
__metadata("design:returntype", Promise)
], X402ActionProvider.prototype, "makeHttpRequestWithX402", null);
const x402ActionProvider = () => new X402ActionProvider();
exports.x402ActionProvider = x402ActionProvider;