agentwallet-sdk 1.0.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +92 -0
- package/dist/index.d.ts +323 -250
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/x402/__tests__/budget.test.d.ts +2 -0
- package/dist/x402/__tests__/budget.test.d.ts.map +1 -0
- package/dist/x402/__tests__/budget.test.js +114 -0
- package/dist/x402/__tests__/budget.test.js.map +1 -0
- package/dist/x402/__tests__/client.test.d.ts +2 -0
- package/dist/x402/__tests__/client.test.d.ts.map +1 -0
- package/dist/x402/__tests__/client.test.js +107 -0
- package/dist/x402/__tests__/client.test.js.map +1 -0
- package/dist/x402/budget.d.ts +52 -0
- package/dist/x402/budget.d.ts.map +1 -0
- package/dist/x402/budget.js +113 -0
- package/dist/x402/budget.js.map +1 -0
- package/dist/x402/client.d.ts +60 -0
- package/dist/x402/client.d.ts.map +1 -0
- package/dist/x402/client.js +205 -0
- package/dist/x402/client.js.map +1 -0
- package/dist/x402/index.d.ts +6 -0
- package/dist/x402/index.d.ts.map +1 -0
- package/dist/x402/index.js +6 -0
- package/dist/x402/index.js.map +1 -0
- package/dist/x402/middleware.d.ts +37 -0
- package/dist/x402/middleware.d.ts.map +1 -0
- package/dist/x402/middleware.js +65 -0
- package/dist/x402/middleware.js.map +1 -0
- package/dist/x402/types.d.ts +91 -0
- package/dist/x402/types.d.ts.map +1 -0
- package/dist/x402/types.js +9 -0
- package/dist/x402/types.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* [MAX-ADDED] In-memory budget tracker for x402 payments.
|
|
3
|
+
* Enforces per-service caps, daily limits, and per-request maximums.
|
|
4
|
+
* On-chain spend limits are ALSO enforced by the AgentWallet contract —
|
|
5
|
+
* this is an additional client-side layer for granular service-level control.
|
|
6
|
+
*/
|
|
7
|
+
export class X402BudgetTracker {
|
|
8
|
+
constructor(config = {}) {
|
|
9
|
+
this.serviceBudgets = new Map();
|
|
10
|
+
this.dailySpend = new Map(); // service -> today's total
|
|
11
|
+
this.globalDailySpend = 0n;
|
|
12
|
+
this.transactionLog = [];
|
|
13
|
+
this.globalDailyLimit = config.globalDailyLimit ?? BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFF'); // effectively unlimited
|
|
14
|
+
this.globalPerRequestMax = config.globalPerRequestMax ?? BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFF');
|
|
15
|
+
this.dailyResetTimestamp = this.startOfDay();
|
|
16
|
+
if (config.serviceBudgets) {
|
|
17
|
+
for (const budget of config.serviceBudgets) {
|
|
18
|
+
this.serviceBudgets.set(budget.service, budget);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Check if a payment is within budget limits.
|
|
24
|
+
* Returns { allowed: true } or { allowed: false, reason: string }.
|
|
25
|
+
*/
|
|
26
|
+
checkBudget(service, amount) {
|
|
27
|
+
this.maybeResetDaily();
|
|
28
|
+
// Global per-request check
|
|
29
|
+
if (amount > this.globalPerRequestMax) {
|
|
30
|
+
return { allowed: false, reason: `Amount ${amount} exceeds global per-request max ${this.globalPerRequestMax}` };
|
|
31
|
+
}
|
|
32
|
+
// Global daily check
|
|
33
|
+
if (this.globalDailySpend + amount > this.globalDailyLimit) {
|
|
34
|
+
return { allowed: false, reason: `Would exceed global daily limit ${this.globalDailyLimit}` };
|
|
35
|
+
}
|
|
36
|
+
// Service-specific checks
|
|
37
|
+
const budget = this.findServiceBudget(service);
|
|
38
|
+
if (budget) {
|
|
39
|
+
if (amount > budget.maxPerRequest) {
|
|
40
|
+
return { allowed: false, reason: `Amount ${amount} exceeds service per-request max ${budget.maxPerRequest} for ${service}` };
|
|
41
|
+
}
|
|
42
|
+
const serviceDailySpend = this.dailySpend.get(service) ?? 0n;
|
|
43
|
+
if (serviceDailySpend + amount > budget.dailyLimit) {
|
|
44
|
+
return { allowed: false, reason: `Would exceed daily limit ${budget.dailyLimit} for ${service}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { allowed: true };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Record a completed payment.
|
|
51
|
+
*/
|
|
52
|
+
recordPayment(log) {
|
|
53
|
+
this.maybeResetDaily();
|
|
54
|
+
this.transactionLog.push(log);
|
|
55
|
+
if (log.success) {
|
|
56
|
+
const service = log.service;
|
|
57
|
+
this.dailySpend.set(service, (this.dailySpend.get(service) ?? 0n) + log.amount);
|
|
58
|
+
this.globalDailySpend += log.amount;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get transaction history, optionally filtered.
|
|
63
|
+
*/
|
|
64
|
+
getTransactionLog(filter) {
|
|
65
|
+
let logs = this.transactionLog;
|
|
66
|
+
if (filter?.service) {
|
|
67
|
+
logs = logs.filter(l => l.service === filter.service);
|
|
68
|
+
}
|
|
69
|
+
if (filter?.since) {
|
|
70
|
+
logs = logs.filter(l => l.timestamp >= filter.since);
|
|
71
|
+
}
|
|
72
|
+
return logs;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get current daily spend summary.
|
|
76
|
+
*/
|
|
77
|
+
getDailySpendSummary() {
|
|
78
|
+
this.maybeResetDaily();
|
|
79
|
+
const byService = {};
|
|
80
|
+
for (const [service, amount] of this.dailySpend) {
|
|
81
|
+
byService[service] = amount;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
global: this.globalDailySpend,
|
|
85
|
+
byService,
|
|
86
|
+
resetsAt: this.dailyResetTimestamp + 86400,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Add or update a service budget at runtime.
|
|
91
|
+
*/
|
|
92
|
+
setServiceBudget(budget) {
|
|
93
|
+
this.serviceBudgets.set(budget.service, budget);
|
|
94
|
+
}
|
|
95
|
+
// ─── Internals ───
|
|
96
|
+
findServiceBudget(service) {
|
|
97
|
+
// Exact match first, then wildcard
|
|
98
|
+
return this.serviceBudgets.get(service) ?? this.serviceBudgets.get('*');
|
|
99
|
+
}
|
|
100
|
+
maybeResetDaily() {
|
|
101
|
+
const now = this.startOfDay();
|
|
102
|
+
if (now > this.dailyResetTimestamp) {
|
|
103
|
+
this.dailySpend.clear();
|
|
104
|
+
this.globalDailySpend = 0n;
|
|
105
|
+
this.dailyResetTimestamp = now;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
startOfDay() {
|
|
109
|
+
const now = Math.floor(Date.now() / 1000);
|
|
110
|
+
return now - (now % 86400);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=budget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"budget.js","sourceRoot":"","sources":["../../src/x402/budget.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { X402PaymentRequired, X402PaymentRequirements, X402ClientConfig, X402TransactionLog } from './types.js';
|
|
2
|
+
import { X402BudgetTracker } from './budget.js';
|
|
3
|
+
/**
|
|
4
|
+
* [MAX-ADDED] x402 Payment Client for AgentWallet.
|
|
5
|
+
*
|
|
6
|
+
* Handles the full x402 payment flow:
|
|
7
|
+
* 1. Detects 402 Payment Required responses
|
|
8
|
+
* 2. Parses payment instructions from PAYMENT-REQUIRED header
|
|
9
|
+
* 3. Validates against budget controls
|
|
10
|
+
* 4. Executes USDC payment via AgentWallet contract
|
|
11
|
+
* 5. Retries original request with payment proof
|
|
12
|
+
*/
|
|
13
|
+
export declare class X402Client {
|
|
14
|
+
private wallet;
|
|
15
|
+
private config;
|
|
16
|
+
private budget;
|
|
17
|
+
private supportedNetworks;
|
|
18
|
+
constructor(wallet: any, config?: X402ClientConfig);
|
|
19
|
+
/**
|
|
20
|
+
* Make an x402-aware fetch request. Automatically handles 402 responses.
|
|
21
|
+
*/
|
|
22
|
+
fetch(url: string | URL, init?: RequestInit): Promise<Response>;
|
|
23
|
+
/**
|
|
24
|
+
* Parse a 402 response to extract payment requirements.
|
|
25
|
+
*/
|
|
26
|
+
parse402Response(response: Response): Promise<X402PaymentRequired | null>;
|
|
27
|
+
/**
|
|
28
|
+
* Select the best compatible payment option from offered requirements.
|
|
29
|
+
* Prefers: Base network, USDC, exact scheme.
|
|
30
|
+
*/
|
|
31
|
+
selectPaymentOption(accepts: X402PaymentRequirements[]): X402PaymentRequirements | null;
|
|
32
|
+
/**
|
|
33
|
+
* Execute the payment via AgentWallet's agentTransferToken.
|
|
34
|
+
*/
|
|
35
|
+
private executePayment;
|
|
36
|
+
/** Get the budget tracker for direct inspection */
|
|
37
|
+
get budgetTracker(): X402BudgetTracker;
|
|
38
|
+
/** Get transaction log */
|
|
39
|
+
getTransactionLog(filter?: {
|
|
40
|
+
service?: string;
|
|
41
|
+
since?: number;
|
|
42
|
+
}): X402TransactionLog[];
|
|
43
|
+
/** Get daily spend summary */
|
|
44
|
+
getDailySpendSummary(): {
|
|
45
|
+
global: bigint;
|
|
46
|
+
byService: Record<string, bigint>;
|
|
47
|
+
resetsAt: number;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export declare class X402PaymentError extends Error {
|
|
51
|
+
readonly paymentRequirements: X402PaymentRequirements;
|
|
52
|
+
constructor(message: string, paymentRequirements: X402PaymentRequirements);
|
|
53
|
+
}
|
|
54
|
+
export declare class X402BudgetExceededError extends Error {
|
|
55
|
+
readonly reason: string;
|
|
56
|
+
readonly url: string;
|
|
57
|
+
readonly paymentRequirements: X402PaymentRequirements;
|
|
58
|
+
constructor(reason: string, url: string, paymentRequirements: X402PaymentRequirements);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/x402/client.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EAGvB,gBAAgB,EAChB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAQhD;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAM;IACpB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,iBAAiB,CAAc;gBAE3B,MAAM,EAAE,GAAG,EAAE,MAAM,GAAE,gBAAqB;IAYtD;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAmFrE;;OAEG;IACG,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IA2B/E;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,uBAAuB,EAAE,GAAG,uBAAuB,GAAG,IAAI;IAmBvF;;OAEG;YACW,cAAc;IA+B5B,mDAAmD;IACnD,IAAI,aAAa,IAAI,iBAAiB,CAErC;IAED,0BAA0B;IAC1B,iBAAiB,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB,EAAE;IAItF,8BAA8B;IAC9B,oBAAoB;;;;;CAGrB;AAID,qBAAa,gBAAiB,SAAQ,KAAK;aAGvB,mBAAmB,EAAE,uBAAuB;gBAD5D,OAAO,EAAE,MAAM,EACC,mBAAmB,EAAE,uBAAuB;CAK/D;AAED,qBAAa,uBAAwB,SAAQ,KAAK;aAE9B,MAAM,EAAE,MAAM;aACd,GAAG,EAAE,MAAM;aACX,mBAAmB,EAAE,uBAAuB;gBAF5C,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,EACX,mBAAmB,EAAE,uBAAuB;CAK/D"}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { parseAbi } from 'viem';
|
|
2
|
+
import { USDC_ADDRESSES, DEFAULT_SUPPORTED_NETWORKS } from './types.js';
|
|
3
|
+
import { X402BudgetTracker } from './budget.js';
|
|
4
|
+
import { agentTransferToken, checkBudget } from '../index.js';
|
|
5
|
+
// ERC20 transfer ABI for encoding
|
|
6
|
+
const ERC20_TRANSFER_ABI = parseAbi([
|
|
7
|
+
'function transfer(address to, uint256 amount) returns (bool)',
|
|
8
|
+
]);
|
|
9
|
+
/**
|
|
10
|
+
* [MAX-ADDED] x402 Payment Client for AgentWallet.
|
|
11
|
+
*
|
|
12
|
+
* Handles the full x402 payment flow:
|
|
13
|
+
* 1. Detects 402 Payment Required responses
|
|
14
|
+
* 2. Parses payment instructions from PAYMENT-REQUIRED header
|
|
15
|
+
* 3. Validates against budget controls
|
|
16
|
+
* 4. Executes USDC payment via AgentWallet contract
|
|
17
|
+
* 5. Retries original request with payment proof
|
|
18
|
+
*/
|
|
19
|
+
export class X402Client {
|
|
20
|
+
constructor(wallet, config = {}) {
|
|
21
|
+
this.wallet = wallet;
|
|
22
|
+
this.config = {
|
|
23
|
+
autoPay: true,
|
|
24
|
+
maxRetries: 1,
|
|
25
|
+
supportedNetworks: [...DEFAULT_SUPPORTED_NETWORKS],
|
|
26
|
+
...config,
|
|
27
|
+
};
|
|
28
|
+
this.budget = new X402BudgetTracker(config);
|
|
29
|
+
this.supportedNetworks = new Set(this.config.supportedNetworks);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Make an x402-aware fetch request. Automatically handles 402 responses.
|
|
33
|
+
*/
|
|
34
|
+
async fetch(url, init) {
|
|
35
|
+
const urlStr = url.toString();
|
|
36
|
+
const response = await globalThis.fetch(url, init);
|
|
37
|
+
if (response.status !== 402) {
|
|
38
|
+
return response;
|
|
39
|
+
}
|
|
40
|
+
if (!this.config.autoPay) {
|
|
41
|
+
return response;
|
|
42
|
+
}
|
|
43
|
+
// Parse the 402 response
|
|
44
|
+
const paymentRequired = await this.parse402Response(response);
|
|
45
|
+
if (!paymentRequired) {
|
|
46
|
+
return response; // Couldn't parse — return original 402
|
|
47
|
+
}
|
|
48
|
+
// Find a compatible payment option
|
|
49
|
+
const selected = this.selectPaymentOption(paymentRequired.accepts);
|
|
50
|
+
if (!selected) {
|
|
51
|
+
return response; // No compatible payment option
|
|
52
|
+
}
|
|
53
|
+
// Check budget
|
|
54
|
+
const amount = BigInt(selected.amount);
|
|
55
|
+
const service = new URL(urlStr).hostname;
|
|
56
|
+
const budgetCheck = this.budget.checkBudget(service, amount);
|
|
57
|
+
if (!budgetCheck.allowed) {
|
|
58
|
+
throw new X402BudgetExceededError(budgetCheck.reason, urlStr, selected);
|
|
59
|
+
}
|
|
60
|
+
// Callback check
|
|
61
|
+
if (this.config.onBeforePayment) {
|
|
62
|
+
const proceed = await this.config.onBeforePayment(selected, urlStr);
|
|
63
|
+
if (!proceed) {
|
|
64
|
+
return response;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Execute payment
|
|
68
|
+
const paymentResult = await this.executePayment(selected);
|
|
69
|
+
// Build payment payload
|
|
70
|
+
const paymentPayload = {
|
|
71
|
+
x402Version: paymentRequired.x402Version,
|
|
72
|
+
resource: paymentRequired.resource,
|
|
73
|
+
accepted: selected,
|
|
74
|
+
payload: {
|
|
75
|
+
txHash: paymentResult.txHash,
|
|
76
|
+
network: selected.network,
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
// Log the transaction
|
|
80
|
+
const log = {
|
|
81
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
82
|
+
service,
|
|
83
|
+
url: urlStr,
|
|
84
|
+
amount,
|
|
85
|
+
token: selected.asset,
|
|
86
|
+
recipient: selected.payTo,
|
|
87
|
+
txHash: paymentResult.txHash,
|
|
88
|
+
network: selected.network,
|
|
89
|
+
scheme: selected.scheme,
|
|
90
|
+
success: true,
|
|
91
|
+
};
|
|
92
|
+
this.budget.recordPayment(log);
|
|
93
|
+
this.config.onPaymentComplete?.(log);
|
|
94
|
+
// Retry request with payment proof
|
|
95
|
+
const retryHeaders = new Headers(init?.headers);
|
|
96
|
+
const payloadB64 = btoa(JSON.stringify(paymentPayload));
|
|
97
|
+
retryHeaders.set('X-PAYMENT', payloadB64);
|
|
98
|
+
const retryResponse = await globalThis.fetch(url, {
|
|
99
|
+
...init,
|
|
100
|
+
headers: retryHeaders,
|
|
101
|
+
});
|
|
102
|
+
return retryResponse;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Parse a 402 response to extract payment requirements.
|
|
106
|
+
*/
|
|
107
|
+
async parse402Response(response) {
|
|
108
|
+
// Try PAYMENT-REQUIRED header first (standard x402)
|
|
109
|
+
const headerValue = response.headers.get('payment-required')
|
|
110
|
+
?? response.headers.get('x-payment-required');
|
|
111
|
+
if (headerValue) {
|
|
112
|
+
try {
|
|
113
|
+
const decoded = JSON.parse(atob(headerValue));
|
|
114
|
+
return decoded;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
// Fall through to body parsing
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Try JSON body as fallback
|
|
121
|
+
try {
|
|
122
|
+
const body = await response.clone().json();
|
|
123
|
+
if (body.x402Version && body.accepts) {
|
|
124
|
+
return body;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// Not parseable
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Select the best compatible payment option from offered requirements.
|
|
134
|
+
* Prefers: Base network, USDC, exact scheme.
|
|
135
|
+
*/
|
|
136
|
+
selectPaymentOption(accepts) {
|
|
137
|
+
// Filter to supported networks and known assets
|
|
138
|
+
const compatible = accepts.filter(req => {
|
|
139
|
+
if (!this.supportedNetworks.has(req.network))
|
|
140
|
+
return false;
|
|
141
|
+
const networkAssets = this.config.supportedAssets?.[req.network]
|
|
142
|
+
?? [USDC_ADDRESSES[req.network]].filter(Boolean);
|
|
143
|
+
return networkAssets.some(a => a.toLowerCase() === req.asset.toLowerCase());
|
|
144
|
+
});
|
|
145
|
+
if (compatible.length === 0)
|
|
146
|
+
return null;
|
|
147
|
+
// Prefer "exact" scheme, then lowest amount
|
|
148
|
+
const exact = compatible.filter(r => r.scheme === 'exact');
|
|
149
|
+
const candidates = exact.length > 0 ? exact : compatible;
|
|
150
|
+
candidates.sort((a, b) => Number(BigInt(a.amount) - BigInt(b.amount)));
|
|
151
|
+
return candidates[0];
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Execute the payment via AgentWallet's agentTransferToken.
|
|
155
|
+
*/
|
|
156
|
+
async executePayment(req) {
|
|
157
|
+
// First check on-chain budget
|
|
158
|
+
const onChainBudget = await checkBudget(this.wallet, req.asset);
|
|
159
|
+
const amount = BigInt(req.amount);
|
|
160
|
+
if (amount > onChainBudget.perTxLimit) {
|
|
161
|
+
throw new X402PaymentError(`Amount ${amount} exceeds on-chain per-tx limit ${onChainBudget.perTxLimit}`, req);
|
|
162
|
+
}
|
|
163
|
+
if (amount > onChainBudget.remainingInPeriod) {
|
|
164
|
+
throw new X402PaymentError(`Amount ${amount} exceeds remaining period budget ${onChainBudget.remainingInPeriod}`, req);
|
|
165
|
+
}
|
|
166
|
+
// Execute the ERC20 transfer via AgentWallet
|
|
167
|
+
const txHash = await agentTransferToken(this.wallet, {
|
|
168
|
+
token: req.asset,
|
|
169
|
+
to: req.payTo,
|
|
170
|
+
amount,
|
|
171
|
+
});
|
|
172
|
+
return { txHash };
|
|
173
|
+
}
|
|
174
|
+
// ─── Budget Access ───
|
|
175
|
+
/** Get the budget tracker for direct inspection */
|
|
176
|
+
get budgetTracker() {
|
|
177
|
+
return this.budget;
|
|
178
|
+
}
|
|
179
|
+
/** Get transaction log */
|
|
180
|
+
getTransactionLog(filter) {
|
|
181
|
+
return this.budget.getTransactionLog(filter);
|
|
182
|
+
}
|
|
183
|
+
/** Get daily spend summary */
|
|
184
|
+
getDailySpendSummary() {
|
|
185
|
+
return this.budget.getDailySpendSummary();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// ─── Error Types ───
|
|
189
|
+
export class X402PaymentError extends Error {
|
|
190
|
+
constructor(message, paymentRequirements) {
|
|
191
|
+
super(`x402 payment error: ${message}`);
|
|
192
|
+
this.paymentRequirements = paymentRequirements;
|
|
193
|
+
this.name = 'X402PaymentError';
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
export class X402BudgetExceededError extends Error {
|
|
197
|
+
constructor(reason, url, paymentRequirements) {
|
|
198
|
+
super(`x402 budget exceeded: ${reason}`);
|
|
199
|
+
this.reason = reason;
|
|
200
|
+
this.url = url;
|
|
201
|
+
this.paymentRequirements = paymentRequirements;
|
|
202
|
+
this.name = 'X402BudgetExceededError';
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/x402/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { X402Client, X402PaymentError, X402BudgetExceededError } from './client.js';
|
|
2
|
+
export { X402BudgetTracker } from './budget.js';
|
|
3
|
+
export { createX402Client, createX402Fetch, wrapWithX402 } from './middleware.js';
|
|
4
|
+
export type { X402PaymentRequired, X402PaymentRequirements, X402PaymentPayload, X402SettlementResponse, X402ResourceInfo, X402ServiceBudget, X402TransactionLog, X402ClientConfig, } from './types.js';
|
|
5
|
+
export { USDC_ADDRESSES, DEFAULT_SUPPORTED_NETWORKS } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/x402/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAClF,YAAY,EACV,mBAAmB,EACnB,uBAAuB,EACvB,kBAAkB,EAClB,sBAAsB,EACtB,gBAAgB,EAChB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// [MAX-ADDED] x402 Protocol Module — HTTP 402 payment support for AgentWallet
|
|
2
|
+
export { X402Client, X402PaymentError, X402BudgetExceededError } from './client.js';
|
|
3
|
+
export { X402BudgetTracker } from './budget.js';
|
|
4
|
+
export { createX402Client, createX402Fetch, wrapWithX402 } from './middleware.js';
|
|
5
|
+
export { USDC_ADDRESSES, DEFAULT_SUPPORTED_NETWORKS } from './types.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/x402/index.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACpF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAWlF,OAAO,EAAE,cAAc,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { X402ClientConfig } from './types.js';
|
|
2
|
+
import { X402Client } from './client.js';
|
|
3
|
+
/**
|
|
4
|
+
* [MAX-ADDED] Create an x402-aware HTTP client.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const client = createX402Client(wallet, { globalDailyLimit: 10_000_000n });
|
|
8
|
+
* const response = await client.fetch('https://api.example.com/data');
|
|
9
|
+
* // If the endpoint returns 402, payment is handled automatically
|
|
10
|
+
*
|
|
11
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
12
|
+
* @param config - Optional x402 client configuration
|
|
13
|
+
* @returns X402Client with .fetch() method and budget controls
|
|
14
|
+
*/
|
|
15
|
+
export declare function createX402Client(wallet: any, config?: X402ClientConfig): X402Client;
|
|
16
|
+
/**
|
|
17
|
+
* [MAX-ADDED] Create an x402-aware fetch function (drop-in replacement).
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* const x402Fetch = createX402Fetch(wallet);
|
|
21
|
+
* const response = await x402Fetch('https://api.example.com/data');
|
|
22
|
+
*
|
|
23
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
24
|
+
* @param config - Optional x402 client configuration
|
|
25
|
+
* @returns A fetch-compatible function that handles 402 payments
|
|
26
|
+
*/
|
|
27
|
+
export declare function createX402Fetch(wallet: any, config?: X402ClientConfig): typeof globalThis.fetch;
|
|
28
|
+
/**
|
|
29
|
+
* [MAX-ADDED] Wrap an existing fetch-like function to be x402-aware.
|
|
30
|
+
* Useful for wrapping custom HTTP clients or test mocks.
|
|
31
|
+
*
|
|
32
|
+
* @param fetchFn - The original fetch function to wrap
|
|
33
|
+
* @param wallet - AgentWallet instance
|
|
34
|
+
* @param config - Optional x402 client configuration
|
|
35
|
+
*/
|
|
36
|
+
export declare function wrapWithX402(fetchFn: typeof globalThis.fetch, wallet: any, config?: X402ClientConfig): typeof globalThis.fetch;
|
|
37
|
+
//# sourceMappingURL=middleware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/x402/middleware.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,gBAAgB,GAAG,UAAU,CAEnF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,UAAU,CAAC,KAAK,CAMzB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,UAAU,CAAC,KAAK,EAChC,MAAM,EAAE,GAAG,EACX,MAAM,CAAC,EAAE,gBAAgB,GACxB,OAAO,UAAU,CAAC,KAAK,CAyBzB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { X402Client } from './client.js';
|
|
2
|
+
/**
|
|
3
|
+
* [MAX-ADDED] Create an x402-aware HTTP client.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* const client = createX402Client(wallet, { globalDailyLimit: 10_000_000n });
|
|
7
|
+
* const response = await client.fetch('https://api.example.com/data');
|
|
8
|
+
* // If the endpoint returns 402, payment is handled automatically
|
|
9
|
+
*
|
|
10
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
11
|
+
* @param config - Optional x402 client configuration
|
|
12
|
+
* @returns X402Client with .fetch() method and budget controls
|
|
13
|
+
*/
|
|
14
|
+
export function createX402Client(wallet, config) {
|
|
15
|
+
return new X402Client(wallet, config);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* [MAX-ADDED] Create an x402-aware fetch function (drop-in replacement).
|
|
19
|
+
*
|
|
20
|
+
* Usage:
|
|
21
|
+
* const x402Fetch = createX402Fetch(wallet);
|
|
22
|
+
* const response = await x402Fetch('https://api.example.com/data');
|
|
23
|
+
*
|
|
24
|
+
* @param wallet - AgentWallet instance from createWallet()
|
|
25
|
+
* @param config - Optional x402 client configuration
|
|
26
|
+
* @returns A fetch-compatible function that handles 402 payments
|
|
27
|
+
*/
|
|
28
|
+
export function createX402Fetch(wallet, config) {
|
|
29
|
+
const client = new X402Client(wallet, config);
|
|
30
|
+
return (input, init) => {
|
|
31
|
+
const url = input instanceof Request ? input.url : input.toString();
|
|
32
|
+
return client.fetch(url, init);
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* [MAX-ADDED] Wrap an existing fetch-like function to be x402-aware.
|
|
37
|
+
* Useful for wrapping custom HTTP clients or test mocks.
|
|
38
|
+
*
|
|
39
|
+
* @param fetchFn - The original fetch function to wrap
|
|
40
|
+
* @param wallet - AgentWallet instance
|
|
41
|
+
* @param config - Optional x402 client configuration
|
|
42
|
+
*/
|
|
43
|
+
export function wrapWithX402(fetchFn, wallet, config) {
|
|
44
|
+
const client = new X402Client(wallet, config);
|
|
45
|
+
// Override the global fetch used by X402Client
|
|
46
|
+
const originalGlobalFetch = globalThis.fetch;
|
|
47
|
+
const wrappedClient = new X402Client(wallet, config);
|
|
48
|
+
return async (input, init) => {
|
|
49
|
+
const url = input instanceof Request ? input.url : input.toString();
|
|
50
|
+
const response = await fetchFn(input, init);
|
|
51
|
+
if (response.status !== 402) {
|
|
52
|
+
return response;
|
|
53
|
+
}
|
|
54
|
+
// Parse and handle 402 via the client
|
|
55
|
+
const paymentRequired = await wrappedClient.parse402Response(response);
|
|
56
|
+
if (!paymentRequired)
|
|
57
|
+
return response;
|
|
58
|
+
const selected = wrappedClient.selectPaymentOption(paymentRequired.accepts);
|
|
59
|
+
if (!selected)
|
|
60
|
+
return response;
|
|
61
|
+
// Use the client's fetch for retry (which calls globalThis.fetch)
|
|
62
|
+
return wrappedClient.fetch(url, init);
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=middleware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/x402/middleware.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAAW,EAAE,MAAyB;IACrE,OAAO,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,eAAe,CAC7B,MAAW,EACX,MAAyB;IAEzB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9C,OAAO,CAAC,KAA6B,EAAE,IAAkB,EAAE,EAAE;QAC3D,MAAM,GAAG,GAAG,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpE,OAAO,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC1B,OAAgC,EAChC,MAAW,EACX,MAAyB;IAEzB,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE9C,+CAA+C;IAC/C,MAAM,mBAAmB,GAAG,UAAU,CAAC,KAAK,CAAC;IAC7C,MAAM,aAAa,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAErD,OAAO,KAAK,EAAE,KAA6B,EAAE,IAAkB,EAAE,EAAE;QACjE,MAAM,GAAG,GAAG,KAAK,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpE,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAE5C,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;YAC5B,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,sCAAsC;QACtC,MAAM,eAAe,GAAG,MAAM,aAAa,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;QACvE,IAAI,CAAC,eAAe;YAAE,OAAO,QAAQ,CAAC;QAEtC,MAAM,QAAQ,GAAG,aAAa,CAAC,mBAAmB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC;QAC5E,IAAI,CAAC,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAE/B,kEAAkE;QAClE,OAAO,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACxC,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { Address, Hash } from 'viem';
|
|
2
|
+
/** Resource information included in x402 responses */
|
|
3
|
+
export interface X402ResourceInfo {
|
|
4
|
+
url: string;
|
|
5
|
+
description: string;
|
|
6
|
+
mimeType: string;
|
|
7
|
+
}
|
|
8
|
+
/** Payment requirements returned in 402 response PAYMENT-REQUIRED header */
|
|
9
|
+
export interface X402PaymentRequirements {
|
|
10
|
+
scheme: string;
|
|
11
|
+
network: string;
|
|
12
|
+
asset: string;
|
|
13
|
+
amount: string;
|
|
14
|
+
payTo: string;
|
|
15
|
+
maxTimeoutSeconds: number;
|
|
16
|
+
extra: Record<string, unknown>;
|
|
17
|
+
}
|
|
18
|
+
/** Full 402 response payload (base64-decoded from PAYMENT-REQUIRED header) */
|
|
19
|
+
export interface X402PaymentRequired {
|
|
20
|
+
x402Version: number;
|
|
21
|
+
error?: string;
|
|
22
|
+
resource: X402ResourceInfo;
|
|
23
|
+
accepts: X402PaymentRequirements[];
|
|
24
|
+
extensions?: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
/** Payment payload sent back with PAYMENT-SIGNATURE header */
|
|
27
|
+
export interface X402PaymentPayload {
|
|
28
|
+
x402Version: number;
|
|
29
|
+
resource: X402ResourceInfo;
|
|
30
|
+
accepted: X402PaymentRequirements;
|
|
31
|
+
payload: Record<string, unknown>;
|
|
32
|
+
extensions?: Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/** Settlement response returned in PAYMENT-RESPONSE header */
|
|
35
|
+
export interface X402SettlementResponse {
|
|
36
|
+
success: boolean;
|
|
37
|
+
txHash?: string;
|
|
38
|
+
network?: string;
|
|
39
|
+
error?: string;
|
|
40
|
+
}
|
|
41
|
+
/** Per-service spending cap configuration */
|
|
42
|
+
export interface X402ServiceBudget {
|
|
43
|
+
/** Domain or URL pattern (e.g. "api.example.com" or "*") */
|
|
44
|
+
service: string;
|
|
45
|
+
/** Max spend per single request in token base units */
|
|
46
|
+
maxPerRequest: bigint;
|
|
47
|
+
/** Max total spend per day in token base units */
|
|
48
|
+
dailyLimit: bigint;
|
|
49
|
+
}
|
|
50
|
+
/** Transaction log entry for x402 payments */
|
|
51
|
+
export interface X402TransactionLog {
|
|
52
|
+
timestamp: number;
|
|
53
|
+
service: string;
|
|
54
|
+
url: string;
|
|
55
|
+
amount: bigint;
|
|
56
|
+
token: Address;
|
|
57
|
+
recipient: Address;
|
|
58
|
+
txHash: Hash;
|
|
59
|
+
network: string;
|
|
60
|
+
scheme: string;
|
|
61
|
+
success: boolean;
|
|
62
|
+
error?: string;
|
|
63
|
+
}
|
|
64
|
+
/** Configuration for the x402 client */
|
|
65
|
+
export interface X402ClientConfig {
|
|
66
|
+
/** Service-level budget controls */
|
|
67
|
+
serviceBudgets?: X402ServiceBudget[];
|
|
68
|
+
/** Global daily spending limit (token base units, default: unlimited) */
|
|
69
|
+
globalDailyLimit?: bigint;
|
|
70
|
+
/** Global per-request max (token base units, default: unlimited) */
|
|
71
|
+
globalPerRequestMax?: bigint;
|
|
72
|
+
/** Supported networks (default: ["base:8453"]) */
|
|
73
|
+
supportedNetworks?: string[];
|
|
74
|
+
/** Supported assets by network (default: USDC on Base) */
|
|
75
|
+
supportedAssets?: Record<string, Address[]>;
|
|
76
|
+
/** Max retries after payment (default: 1) */
|
|
77
|
+
maxRetries?: number;
|
|
78
|
+
/** Custom facilitator URL (optional, for verify/settle) */
|
|
79
|
+
facilitatorUrl?: string;
|
|
80
|
+
/** Whether to auto-pay 402 responses (default: true) */
|
|
81
|
+
autoPay?: boolean;
|
|
82
|
+
/** Callback before payment — return false to reject */
|
|
83
|
+
onBeforePayment?: (req: X402PaymentRequirements, url: string) => Promise<boolean> | boolean;
|
|
84
|
+
/** Callback after payment */
|
|
85
|
+
onPaymentComplete?: (log: X402TransactionLog) => void;
|
|
86
|
+
}
|
|
87
|
+
/** USDC contract addresses by network */
|
|
88
|
+
export declare const USDC_ADDRESSES: Record<string, Address>;
|
|
89
|
+
/** Default supported networks */
|
|
90
|
+
export declare const DEFAULT_SUPPORTED_NETWORKS: readonly ["base:8453"];
|
|
91
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/x402/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// ─── Well-known Assets ───
|
|
2
|
+
/** USDC contract addresses by network */
|
|
3
|
+
export const USDC_ADDRESSES = {
|
|
4
|
+
'base:8453': '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913',
|
|
5
|
+
'base-sepolia:84532': '0x036CbD53842c5426634e7929541eC2318f3dCF7e',
|
|
6
|
+
};
|
|
7
|
+
/** Default supported networks */
|
|
8
|
+
export const DEFAULT_SUPPORTED_NETWORKS = ['base:8453'];
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/x402/types.ts"],"names":[],"mappings":"AAoGA,4BAA4B;AAE5B,yCAAyC;AACzC,MAAM,CAAC,MAAM,cAAc,GAA4B;IACrD,WAAW,EAAE,4CAA4C;IACzD,oBAAoB,EAAE,4CAA4C;CAC1D,CAAC;AAEX,iCAAiC;AACjC,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,WAAW,CAAU,CAAC"}
|