@agether/sdk 1.5.0 → 1.5.2

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.
@@ -0,0 +1,257 @@
1
+ // src/clients/X402Client.ts
2
+ import { ethers } from "ethers";
3
+ var USDC_DOMAINS = {
4
+ "eip155:1": { name: "USD Coin", version: "2", address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48" },
5
+ "eip155:8453": { name: "USD Coin", version: "2", address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913" },
6
+ "eip155:84532": { name: "USD Coin", version: "2", address: "0x036CbD53842c5426634e7929541eC2318f3dCF7e" },
7
+ "eip155:42161": { name: "USD Coin", version: "2", address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831" },
8
+ "eip155:10": { name: "USD Coin", version: "2", address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85" }
9
+ };
10
+ function chainIdFromNetwork(network) {
11
+ const m = network.match(/^eip155:(\d+)$/);
12
+ return m ? Number(m[1]) : 1;
13
+ }
14
+ var X402Client = class {
15
+ constructor(config) {
16
+ this.config = config;
17
+ const provider = new ethers.JsonRpcProvider(config.rpcUrl);
18
+ this.wallet = new ethers.Wallet(config.privateKey, provider);
19
+ }
20
+ async get(url, opts) {
21
+ return this.request(url, { ...opts, method: "GET" });
22
+ }
23
+ async post(url, body, opts) {
24
+ return this.request(url, {
25
+ ...opts,
26
+ method: "POST",
27
+ body: body ? JSON.stringify(body) : void 0,
28
+ headers: { "Content-Type": "application/json", ...opts?.headers }
29
+ });
30
+ }
31
+ getAddress() {
32
+ return this.wallet.address;
33
+ }
34
+ // ──────────── Core request / 402-retry loop ────────────
35
+ async request(url, options) {
36
+ try {
37
+ console.log(" [1/4] Calling resource server\u2026");
38
+ const response = await fetch(url, {
39
+ ...options,
40
+ headers: {
41
+ ...options?.headers,
42
+ "X-Agent-Id": this.config.agentId || ""
43
+ }
44
+ });
45
+ if (response.ok) {
46
+ const data = await response.json();
47
+ return { success: true, data };
48
+ }
49
+ if (response.status !== 402) {
50
+ return { success: false, error: `HTTP ${response.status}: ${await response.text()}` };
51
+ }
52
+ console.log(" [2/4] 402 received \u2014 parsing payment requirements\u2026");
53
+ const parsed = await this.parsePaymentRequired(response);
54
+ if (!parsed) {
55
+ return { success: false, error: "Could not parse payment requirements from 402 response" };
56
+ }
57
+ const { requirements, resource } = parsed;
58
+ console.log(` scheme : ${requirements.scheme}`);
59
+ console.log(` network : ${requirements.network}`);
60
+ console.log(` amount : ${requirements.amount} (atomic)`);
61
+ console.log(` asset : ${requirements.asset}`);
62
+ console.log(` payTo : ${requirements.payTo}`);
63
+ console.log(" [3/4] Signing EIP-3009 transferWithAuthorization\u2026");
64
+ const paymentPayload = await this.buildPaymentPayload(requirements, resource, url);
65
+ const paymentB64 = Buffer.from(JSON.stringify(paymentPayload)).toString("base64");
66
+ await this.riskCheck(paymentPayload, requirements);
67
+ console.log(" [4/4] Retrying with PAYMENT-SIGNATURE header\u2026");
68
+ const paidResponse = await fetch(url, {
69
+ ...options,
70
+ headers: {
71
+ ...options?.headers,
72
+ "X-Agent-Id": this.config.agentId || "",
73
+ // v2 header
74
+ "PAYMENT-SIGNATURE": paymentB64,
75
+ // v1 compat header (some servers still use this)
76
+ "X-PAYMENT": paymentB64
77
+ }
78
+ });
79
+ if (paidResponse.ok) {
80
+ const data = await paidResponse.json();
81
+ const settlementHeader = paidResponse.headers.get("PAYMENT-RESPONSE") || paidResponse.headers.get("X-PAYMENT-RESPONSE");
82
+ let txHash;
83
+ if (settlementHeader) {
84
+ try {
85
+ const settlement = JSON.parse(Buffer.from(settlementHeader, "base64").toString("utf-8"));
86
+ txHash = settlement.transaction;
87
+ } catch {
88
+ }
89
+ }
90
+ return {
91
+ success: true,
92
+ data,
93
+ paymentInfo: {
94
+ amount: requirements.amount,
95
+ asset: requirements.extra?.name || "USDC",
96
+ network: requirements.network,
97
+ txHash
98
+ }
99
+ };
100
+ }
101
+ const errBody = await paidResponse.text();
102
+ return { success: false, error: `Payment rejected (HTTP ${paidResponse.status}): ${errBody}` };
103
+ } catch (error) {
104
+ return { success: false, error: `Request failed: ${error instanceof Error ? error.message : String(error)}` };
105
+ }
106
+ }
107
+ // ──────────── Parse 402 response ────────────
108
+ async parsePaymentRequired(response) {
109
+ const prHeader = response.headers.get("PAYMENT-REQUIRED") || response.headers.get("x-payment-required");
110
+ if (prHeader) {
111
+ try {
112
+ const decoded = JSON.parse(Buffer.from(prHeader, "base64").toString("utf-8"));
113
+ if (decoded.accepts?.length) {
114
+ return { requirements: decoded.accepts[0], resource: decoded.resource };
115
+ }
116
+ } catch {
117
+ }
118
+ }
119
+ try {
120
+ const body = await response.json();
121
+ if (body.accepts && Array.isArray(body.accepts) && body.accepts.length > 0) {
122
+ return { requirements: body.accepts[0], resource: body.resource };
123
+ }
124
+ if (body.paymentRequirements) {
125
+ const pr = Array.isArray(body.paymentRequirements) ? body.paymentRequirements[0] : body.paymentRequirements;
126
+ return { requirements: pr, resource: body.resource };
127
+ }
128
+ if (body.scheme && body.network) {
129
+ return { requirements: body, resource: body.resource };
130
+ }
131
+ } catch {
132
+ }
133
+ const wwwAuth = response.headers.get("WWW-Authenticate");
134
+ if (wwwAuth) {
135
+ const m = wwwAuth.match(/x402[^"]*"([^"]+)"/);
136
+ if (m) {
137
+ try {
138
+ const decoded = JSON.parse(Buffer.from(m[1], "base64").toString("utf-8"));
139
+ const reqs = Array.isArray(decoded) ? decoded[0] : decoded;
140
+ return { requirements: reqs };
141
+ } catch {
142
+ }
143
+ }
144
+ }
145
+ return null;
146
+ }
147
+ // ──────────── Build x402 v2 PaymentPayload with EIP-3009 ────────────
148
+ //
149
+ // If an AgentAccount is configured, we use it as the `from` address
150
+ // (smart wallet pays directly). The AgentAccount implements EIP-1271
151
+ // so USDC's transferWithAuthorization will call isValidSignature()
152
+ // to verify the owner's ECDSA signature. The facilitator detects
153
+ // the >65-byte or smart-wallet case and uses the bytes overload.
154
+ async buildPaymentPayload(reqs, resource, url) {
155
+ const now = Math.floor(Date.now() / 1e3);
156
+ const validAfter = String(now - 60);
157
+ const validBefore = String(now + (reqs.maxTimeoutSeconds || 300));
158
+ const nonce = ethers.hexlify(ethers.randomBytes(32));
159
+ const chainId = chainIdFromNetwork(reqs.network);
160
+ const usdcDomain = USDC_DOMAINS[reqs.network] || USDC_DOMAINS["eip155:1"];
161
+ const payerAddress = this.config.accountAddress || this.wallet.address;
162
+ const isSmartWallet = !!this.config.accountAddress;
163
+ const domain = {
164
+ name: usdcDomain.name,
165
+ version: usdcDomain.version,
166
+ chainId,
167
+ verifyingContract: reqs.asset || usdcDomain.address
168
+ };
169
+ const types = {
170
+ TransferWithAuthorization: [
171
+ { name: "from", type: "address" },
172
+ { name: "to", type: "address" },
173
+ { name: "value", type: "uint256" },
174
+ { name: "validAfter", type: "uint256" },
175
+ { name: "validBefore", type: "uint256" },
176
+ { name: "nonce", type: "bytes32" }
177
+ ]
178
+ };
179
+ const value = {
180
+ from: payerAddress,
181
+ // AgentAccount or EOA
182
+ to: reqs.payTo,
183
+ value: reqs.amount,
184
+ validAfter,
185
+ validBefore,
186
+ nonce
187
+ };
188
+ let signature = await this.wallet.signTypedData(domain, types, value);
189
+ if (isSmartWallet) {
190
+ signature = signature + "00";
191
+ }
192
+ if (isSmartWallet) {
193
+ console.log(` \u2713 Signed for AgentAccount ${payerAddress.slice(0, 10)}\u2026 (EIP-1271, chain=${chainId})`);
194
+ } else {
195
+ console.log(` \u2713 Signed (from=${payerAddress.slice(0, 10)}\u2026, chain=${chainId})`);
196
+ }
197
+ return {
198
+ x402Version: 2,
199
+ resource: resource || { url, description: "", mimeType: "application/json" },
200
+ accepted: {
201
+ scheme: reqs.scheme,
202
+ network: reqs.network,
203
+ amount: reqs.amount,
204
+ asset: reqs.asset,
205
+ payTo: reqs.payTo,
206
+ maxTimeoutSeconds: reqs.maxTimeoutSeconds,
207
+ extra: reqs.extra || {}
208
+ },
209
+ payload: {
210
+ signature,
211
+ authorization: {
212
+ from: payerAddress,
213
+ // AgentAccount address — facilitator checks balance here
214
+ to: reqs.payTo,
215
+ value: reqs.amount,
216
+ validAfter,
217
+ validBefore,
218
+ nonce
219
+ }
220
+ }
221
+ };
222
+ }
223
+ // ──────────── Risk check via our backend ────────────
224
+ async riskCheck(paymentPayload, reqs) {
225
+ try {
226
+ const verifyUrl = `${this.config.backendUrl}/x402/verify`;
227
+ const resp = await fetch(verifyUrl, {
228
+ method: "POST",
229
+ headers: {
230
+ "Content-Type": "application/json",
231
+ "X-Agent-Id": this.config.agentId || "",
232
+ ...this.config.accountAddress ? { "X-Agent-Account": this.config.accountAddress } : {}
233
+ },
234
+ body: JSON.stringify({
235
+ x402Version: 2,
236
+ paymentPayload,
237
+ paymentRequirements: reqs
238
+ }),
239
+ signal: AbortSignal.timeout(5e3)
240
+ });
241
+ if (resp.ok) {
242
+ const result = await resp.json();
243
+ const decision = resp.headers.get("X-Risk-Decision") || (result.isValid ? "allow" : "unknown");
244
+ const score = resp.headers.get("X-Risk-Score") || "?";
245
+ console.log(` \u2713 Risk check: ${decision} (score=${score})`);
246
+ } else {
247
+ console.log(` \u26A0 Risk check failed (HTTP ${resp.status}) \u2014 continuing anyway`);
248
+ }
249
+ } catch {
250
+ console.log(" \u26A0 Risk check unavailable \u2014 continuing");
251
+ }
252
+ }
253
+ };
254
+
255
+ export {
256
+ X402Client
257
+ };
package/dist/cli.d.mts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/cli.js CHANGED
@@ -148,9 +148,9 @@ var init_config = __esm({
148
148
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
149
149
  },
150
150
  [8453 /* Base */]: {
151
- accountFactory: "0xeB72f248Ad9F4bf4024e8D9da75cf7AAD37B58f5",
152
- validationRegistry: "0x8842f2383A86134Dd80c3Ecf6Bbae2e38396A5ec",
153
- agentReputation: "0xF1bed094D4E33E47CC8C72E086FFFde09e2211b4",
151
+ accountFactory: "0x7D5D56416bAEA06a9DCBe3092eF335724C6320a0",
152
+ validationRegistry: "0xd196C32D2149270F56E209ba7aEE67CE9ceD2001",
153
+ agentReputation: "0x65c9cA1211809D3CF3A2707558198eb2b2bE623c",
154
154
  usdc: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
155
155
  identityRegistry: "0x8004A169FB4a3325136EB29fA0ceB6D2e539a432",
156
156
  morphoBlue: "0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb"
@@ -274,11 +274,6 @@ var init_MorphoClient = __esm({
274
274
  let agentId;
275
275
  if (balance > 0n && this.agentId) {
276
276
  agentId = BigInt(this.agentId);
277
- } else if (balance > 0n) {
278
- throw new AgetherError(
279
- "Wallet already has an ERC-8004 identity but agentId is unknown. Pass agentId in config.",
280
- "AGENT_ID_UNKNOWN"
281
- );
282
277
  } else {
283
278
  const regTx = await this.identityRegistry.register();
284
279
  const regReceipt = await regTx.wait();