@armory-sh/client-web3 0.2.6 → 0.2.10

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/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Web3BaseWalletAccount, Web3BaseWallet } from 'web3-types';
2
- import { PaymentPayloadV1, PaymentPayloadV2, CustomToken, NetworkConfig, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponseV1, SettlementResponseV2 } from '@armory-sh/base';
3
- export { EIP712_TYPES as CORE_EIP712_TYPES, USDC_DOMAIN as CORE_USDC_DOMAIN, NETWORKS, NetworkConfig, PaymentPayload, PaymentPayloadV1, PaymentPayloadV2, PaymentRequirements, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponse, SettlementResponseV1, SettlementResponseV2, V1_HEADERS, V2_HEADERS, createEIP712Domain as createCoreEIP712Domain, createTransferWithAuthorization as createCoreTransferWithAuthorization, decodePaymentV1, decodePaymentV2, decodeSettlementV1, decodeSettlementV2, encodePaymentV1, encodePaymentV2, encodeSettlementV1, encodeSettlementV2, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getPaymentRequiredHeaderName, getPaymentResponseHeaderName, getPaymentVersion, getRequirementsVersion, getSettlementVersion, getTestnets, getTxHash, isAddress, isCAIP2ChainId, isCAIPAssetId, isSettlementSuccessful, isV1, isV2, validateTransferWithAuthorization as validateCoreTransferWithAuthorization } from '@armory-sh/base';
2
+ import { PaymentPayloadV1, PaymentPayloadV2, CustomToken, NetworkConfig, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponseV1, SettlementResponseV2, X402PaymentRequiredV1, PaymentRequiredV2, X402PaymentPayloadV1 } from '@armory-sh/base';
3
+ export { EIP712_TYPES as CORE_EIP712_TYPES, USDC_DOMAIN as CORE_USDC_DOMAIN, NETWORKS, NetworkConfig, PaymentPayload, PaymentPayloadV1, PaymentPayloadV2, PaymentRequirements, PaymentRequirementsV1, PaymentRequirementsV2, SettlementResponse, SettlementResponseV1, SettlementResponseV2, V1_HEADERS, V2_HEADERS, combineSignatureV2, createEIP712Domain as createCoreEIP712Domain, createTransferWithAuthorization as createCoreTransferWithAuthorization, createNonce, decodePaymentV1, decodePaymentV2, decodeSettlementV1, decodeSettlementV2, encodePaymentV1, encodePaymentV2, encodeSettlementV1, encodeSettlementV2, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getPaymentRequiredHeaderName, getPaymentResponseHeaderName, getPaymentVersion, getRequirementsVersion, getSettlementVersion, getTestnets, getTxHash, isSettlementSuccessful, isV1, isV2, isX402V1Payload, isX402V1Requirements, isX402V1Settlement, isX402V2Payload, isX402V2Requirements, isX402V2Settlement, parseSignatureV2, validateTransferWithAuthorization as validateCoreTransferWithAuthorization } from '@armory-sh/base';
4
4
 
5
5
  type Web3Account = Web3BaseWalletAccount | Web3BaseWallet<Web3BaseWalletAccount>;
6
6
  /** Token configuration - can use pre-configured tokens from @armory-sh/tokens */
@@ -48,6 +48,7 @@ interface X402TransportOptions {
48
48
  maxRetries?: number;
49
49
  }
50
50
  interface Web3X402Client {
51
+ fetch(url: string | Request, init?: RequestInit): Promise<Response>;
51
52
  getAccount(): Web3Account;
52
53
  getNetwork(): NetworkConfig;
53
54
  getVersion(): 1 | 2;
@@ -82,9 +83,103 @@ declare const isV2Settlement: (response: SettlementResponseV1 | SettlementRespon
82
83
 
83
84
  declare const createX402Client: (config: Web3ClientConfig) => Web3X402Client;
84
85
 
86
+ /**
87
+ * Create an x402 transport layer for handling payment-required responses
88
+ */
85
89
  declare const createX402Transport: (options: X402TransportOptions) => X402Transport;
90
+ /**
91
+ * Create a fetch function bound to an x402 transport
92
+ */
86
93
  declare const createFetchWithX402: (transport: X402Transport) => (url: string | Request, init?: RequestInit) => Promise<Response>;
87
94
 
95
+ /**
96
+ * X402 Protocol Detection and Parsing Functions
97
+ *
98
+ * Handles both x402 V1 and V2 protocol detection and parsing from HTTP responses.
99
+ */
100
+
101
+ type X402Version = 1 | 2;
102
+ interface ParsedPaymentRequired {
103
+ version: X402Version;
104
+ requirements: PaymentRequirementsV1[] | PaymentRequirementsV2[];
105
+ raw: X402PaymentRequiredV1 | PaymentRequiredV2;
106
+ }
107
+ /**
108
+ * Detect x402 protocol version from response headers
109
+ * Returns V2 if PAYMENT-REQUIRED header exists, V1 for X-PAYMENT-REQUIRED
110
+ * Falls back to body detection if no headers present
111
+ */
112
+ declare const detectX402Version: (response: Response, fallbackVersion?: X402Version) => X402Version;
113
+ /**
114
+ * Detect version from a parsed payment required object
115
+ */
116
+ declare const detectVersionFromObject: (obj: unknown) => X402Version | null;
117
+ /**
118
+ * Parse PAYMENT-REQUIRED header or response body
119
+ * Handles both V1 (base64 encoded) and V2 (JSON) formats
120
+ */
121
+ declare const parsePaymentRequired: (response: Response, version?: X402Version) => Promise<ParsedPaymentRequired>;
122
+ /**
123
+ * Parse payment required from header value
124
+ */
125
+ declare const parsePaymentRequiredFromHeader: (header: string, version: X402Version) => ParsedPaymentRequired;
126
+ /**
127
+ * Parse payment required from response body
128
+ */
129
+ declare const parsePaymentRequiredFromBody: (body: string, version: X402Version) => ParsedPaymentRequired;
130
+ /**
131
+ * Create x402 V1 payment payload
132
+ */
133
+ declare const createX402V1Payment: (params: {
134
+ from: string;
135
+ to: string;
136
+ value: string;
137
+ nonce: `0x${string}`;
138
+ validAfter: string;
139
+ validBefore: string;
140
+ signature: `0x${string}`;
141
+ network: string;
142
+ }) => X402PaymentPayloadV1;
143
+ /**
144
+ * Create x402 V2 payment payload
145
+ */
146
+ declare const createX402V2Payment: (params: {
147
+ from: string;
148
+ to: string;
149
+ value: string;
150
+ nonce: `0x${string}`;
151
+ validAfter: string;
152
+ validBefore: string;
153
+ signature: `0x${string}`;
154
+ accepted: PaymentRequirementsV2;
155
+ resource?: {
156
+ url: string;
157
+ description?: string;
158
+ mimeType?: string;
159
+ };
160
+ }) => PaymentPayloadV2;
161
+ /**
162
+ * Create payment header for request
163
+ */
164
+ declare const createPaymentHeader: (payload: X402PaymentPayloadV1 | PaymentPayloadV2, version: X402Version) => string;
165
+ /**
166
+ * Get the payment header name for a version
167
+ */
168
+ declare const getPaymentHeader: (version: X402Version) => string;
169
+ /**
170
+ * Get the payment required header name for a version
171
+ */
172
+ declare const getPaymentRequiredHeader: (version: X402Version) => string;
173
+ /**
174
+ * Check if response indicates payment is required
175
+ */
176
+ declare const isPaymentRequiredResponse: (response: Response) => boolean;
177
+ /**
178
+ * Extract requirements for a specific scheme from accepts array
179
+ * Uses type assertion to handle union types
180
+ */
181
+ declare const selectSchemeRequirements: (requirements: PaymentRequirementsV1[] | PaymentRequirementsV2[], scheme?: string) => PaymentRequirementsV1 | PaymentRequirementsV2 | undefined;
182
+
88
183
  declare const EIP712_TYPES: {
89
184
  readonly EIP712Domain: readonly [{
90
185
  readonly name: "name";
@@ -144,4 +239,4 @@ declare const signWithPrivateKey: (_privateKey: string, _domain: Web3EIP712Domai
144
239
  s: string;
145
240
  }>;
146
241
 
147
- export { EIP712_TYPES, type PaymentSignOptions, type PaymentSignatureResult, type Token, USDC_DOMAIN, type Web3Account, type Web3ClientConfig, type Web3EIP712Domain, type Web3TransferWithAuthorization, type Web3X402Client, type X402RequestContext, type X402Transport, type X402TransportOptions, adjustVForChainId, concatenateSignature, createEIP712Domain, createFetchWithX402, createTransferWithAuthorization, createX402Client, createX402Transport, isV1Requirements, isV1Settlement, isV2Requirements, isV2Settlement, parseSignature, signTypedData, signWithPrivateKey, validateTransferWithAuthorization };
242
+ export { EIP712_TYPES, type ParsedPaymentRequired, type PaymentSignOptions, type PaymentSignatureResult, type Token, USDC_DOMAIN, type Web3Account, type Web3ClientConfig, type Web3EIP712Domain, type Web3TransferWithAuthorization, type Web3X402Client, type X402RequestContext, type X402Transport, type X402TransportOptions, type X402Version, adjustVForChainId, concatenateSignature, createEIP712Domain, createFetchWithX402, createPaymentHeader, createTransferWithAuthorization, createX402Client, createX402Transport, createX402V1Payment, createX402V2Payment, detectVersionFromObject, detectX402Version, getPaymentHeader, getPaymentRequiredHeader, isPaymentRequiredResponse, isV1Requirements, isV1Settlement, isV2Requirements, isV2Settlement, parsePaymentRequired, parsePaymentRequiredFromBody, parsePaymentRequiredFromHeader, parseSignature, selectSchemeRequirements, signTypedData, signWithPrivateKey, validateTransferWithAuthorization };
package/dist/index.js CHANGED
@@ -3,7 +3,12 @@ import { Web3 } from "web3";
3
3
  import {
4
4
  getNetworkConfig,
5
5
  encodePaymentV1,
6
- encodePaymentV2
6
+ encodePaymentV2,
7
+ isX402V1Requirements,
8
+ isX402V2Requirements,
9
+ combineSignatureV2,
10
+ V1_HEADERS as V1_HEADERS2,
11
+ V2_HEADERS as V2_HEADERS2
7
12
  } from "@armory-sh/base";
8
13
 
9
14
  // src/eip3009.ts
@@ -118,6 +123,151 @@ var signWithPrivateKey = async (_privateKey, _domain, _message) => {
118
123
  );
119
124
  };
120
125
 
126
+ // src/protocol.ts
127
+ import {
128
+ V1_HEADERS,
129
+ V2_HEADERS,
130
+ decodeX402PaymentRequiredV1,
131
+ isX402V1PaymentRequired,
132
+ isX402V2PaymentRequired,
133
+ getPaymentRequiredHeaderName,
134
+ getPaymentHeaderName
135
+ } from "@armory-sh/base";
136
+ var detectX402Version = (response, fallbackVersion = 2) => {
137
+ if (response.headers.has(V2_HEADERS.PAYMENT_REQUIRED)) {
138
+ return 2;
139
+ }
140
+ if (response.headers.has(V1_HEADERS.PAYMENT_REQUIRED)) {
141
+ return 1;
142
+ }
143
+ if (response.headers.has("Payment-Required")) {
144
+ return 2;
145
+ }
146
+ return fallbackVersion;
147
+ };
148
+ var detectVersionFromObject = (obj) => {
149
+ if (isX402V2PaymentRequired(obj)) return 2;
150
+ if (isX402V1PaymentRequired(obj)) return 1;
151
+ return null;
152
+ };
153
+ var parsePaymentRequired = async (response, version) => {
154
+ const detectedVersion = version ?? detectX402Version(response);
155
+ const headerName = getPaymentRequiredHeaderName(detectedVersion);
156
+ const header = response.headers.get(headerName);
157
+ if (header) {
158
+ return parsePaymentRequiredFromHeader(header, detectedVersion);
159
+ }
160
+ const body = await response.clone().text();
161
+ return parsePaymentRequiredFromBody(body, detectedVersion);
162
+ };
163
+ var parsePaymentRequiredFromHeader = (header, version) => {
164
+ if (version === 1) {
165
+ const decoded = decodeX402PaymentRequiredV1(header);
166
+ return {
167
+ version: 1,
168
+ requirements: decoded.accepts,
169
+ raw: decoded
170
+ };
171
+ }
172
+ let parsed;
173
+ try {
174
+ parsed = JSON.parse(header);
175
+ } catch {
176
+ const decoded = Buffer.from(header, "base64").toString("utf-8");
177
+ parsed = JSON.parse(decoded);
178
+ }
179
+ return {
180
+ version: 2,
181
+ requirements: parsed.accepts,
182
+ raw: parsed
183
+ };
184
+ };
185
+ var parsePaymentRequiredFromBody = (body, version) => {
186
+ let parsed;
187
+ try {
188
+ parsed = JSON.parse(body);
189
+ } catch {
190
+ const decoded = Buffer.from(body, "base64").toString("utf-8");
191
+ parsed = JSON.parse(decoded);
192
+ }
193
+ const detectedVersion = detectVersionFromObject(parsed) ?? version;
194
+ if (detectedVersion === 1 && isX402V1PaymentRequired(parsed)) {
195
+ return {
196
+ version: 1,
197
+ requirements: parsed.accepts,
198
+ raw: parsed
199
+ };
200
+ }
201
+ if (detectedVersion === 2 && isX402V2PaymentRequired(parsed)) {
202
+ const v2Parsed = parsed;
203
+ return {
204
+ version: 2,
205
+ requirements: v2Parsed.accepts,
206
+ raw: v2Parsed
207
+ };
208
+ }
209
+ throw new Error("Unable to parse payment required response");
210
+ };
211
+ var createX402V1Payment = (params) => {
212
+ const authorization = {
213
+ from: params.from,
214
+ to: params.to,
215
+ value: params.value,
216
+ validAfter: params.validAfter,
217
+ validBefore: params.validBefore,
218
+ nonce: params.nonce
219
+ };
220
+ return {
221
+ x402Version: 1,
222
+ scheme: "exact",
223
+ network: params.network,
224
+ payload: {
225
+ signature: params.signature,
226
+ authorization
227
+ }
228
+ };
229
+ };
230
+ var createX402V2Payment = (params) => {
231
+ const authorization = {
232
+ from: params.from,
233
+ to: params.to,
234
+ value: params.value,
235
+ validAfter: params.validAfter,
236
+ validBefore: params.validBefore,
237
+ nonce: params.nonce
238
+ };
239
+ return {
240
+ x402Version: 2,
241
+ scheme: params.accepted.scheme,
242
+ network: params.accepted.network,
243
+ resource: params.resource,
244
+ payload: {
245
+ signature: params.signature,
246
+ authorization
247
+ }
248
+ };
249
+ };
250
+ var createPaymentHeader = (payload, version) => {
251
+ const headerName = getPaymentHeaderName(version);
252
+ const encoded = Buffer.from(JSON.stringify(payload)).toString("base64");
253
+ return encoded;
254
+ };
255
+ var getPaymentHeader = (version) => {
256
+ return getPaymentHeaderName(version);
257
+ };
258
+ var getPaymentRequiredHeader = (version) => {
259
+ return getPaymentRequiredHeaderName(version);
260
+ };
261
+ var isPaymentRequiredResponse = (response) => {
262
+ if (response.status === 402) return true;
263
+ return response.headers.has(V1_HEADERS.PAYMENT_REQUIRED) || response.headers.has(V2_HEADERS.PAYMENT_REQUIRED) || response.headers.has("Payment-Required");
264
+ };
265
+ var selectSchemeRequirements = (requirements, scheme = "exact") => {
266
+ return requirements.find(
267
+ (r) => "scheme" in r && r.scheme === scheme
268
+ );
269
+ };
270
+
121
271
  // src/client.ts
122
272
  var DEFAULT_EXPIRY_SECONDS = 3600;
123
273
  var DEFAULT_VALID_AFTER = 0;
@@ -167,7 +317,7 @@ var signTypedDataWrapper = async (account, domain, message) => {
167
317
  const sig = await acc.signTypedData(domain, message);
168
318
  return parseSignature2(sig);
169
319
  }
170
- const getAddress2 = () => {
320
+ const getAddressLocal = () => {
171
321
  if ("address" in account) return account.address;
172
322
  if (Array.isArray(account) && account[0]) return account[0].address;
173
323
  throw new Error("Unable to get address from account");
@@ -175,7 +325,7 @@ var signTypedDataWrapper = async (account, domain, message) => {
175
325
  if (typeof acc.request === "function") {
176
326
  const sig = await acc.request({
177
327
  method: "eth_signTypedData_v4",
178
- params: [getAddress2(), JSON.stringify({ domain, message })]
328
+ params: [getAddressLocal(), JSON.stringify({ domain, message })]
179
329
  });
180
330
  return parseSignature2(sig);
181
331
  }
@@ -197,7 +347,7 @@ var signPaymentV1 = async (state, params) => {
197
347
  nonce: `0x${nonce}`
198
348
  });
199
349
  const signature = await signTypedDataWrapper(state.account, domain, message);
200
- const payload = {
350
+ const legacyPayload = {
201
351
  from,
202
352
  to,
203
353
  amount,
@@ -210,10 +360,40 @@ var signPaymentV1 = async (state, params) => {
210
360
  contractAddress: network.usdcAddress,
211
361
  network: network.name.toLowerCase().replace(" ", "-")
212
362
  };
213
- return { v: signature.v, r: signature.r, s: signature.s, payload };
363
+ return { v: signature.v, r: signature.r, s: signature.s, payload: legacyPayload };
364
+ };
365
+ var handlePaymentRequirements = async (requirements, state) => {
366
+ const version = detectVersionFromRequirements(requirements);
367
+ if (version === 1) {
368
+ const req2 = requirements;
369
+ if (isX402V1Requirements(req2)) {
370
+ const x402Req = req2;
371
+ return signPayment({
372
+ amount: x402Req.maxAmountRequired,
373
+ to: x402Req.payTo
374
+ }, state);
375
+ }
376
+ const legacyReq = req2;
377
+ return signPayment({
378
+ amount: legacyReq.amount,
379
+ to: legacyReq.payTo,
380
+ expiry: legacyReq.expiry
381
+ }, state);
382
+ }
383
+ const req = requirements;
384
+ const to = typeof req.payTo === "string" ? req.payTo : "0x0000000000000000000000000000000000000000";
385
+ const from = getAddress(state.account);
386
+ return signPaymentV2(state, {
387
+ from,
388
+ to,
389
+ amount: req.amount,
390
+ nonce: crypto.randomUUID(),
391
+ expiry: Math.floor(Date.now() / 1e3) + DEFAULT_EXPIRY_SECONDS,
392
+ accepted: req
393
+ });
214
394
  };
215
395
  var signPaymentV2 = async (state, params) => {
216
- const { from, to, amount, nonce, expiry } = params;
396
+ const { from, to, amount, nonce, expiry, accepted } = params;
217
397
  const { network, domainName, domainVersion } = state;
218
398
  const domain = createEIP712Domain(network.chainId, network.usdcAddress, domainName, domainVersion);
219
399
  const message = createTransferWithAuthorization({
@@ -225,25 +405,73 @@ var signPaymentV2 = async (state, params) => {
225
405
  nonce: `0x${nonce}`
226
406
  });
227
407
  const signature = await signTypedDataWrapper(state.account, domain, message);
228
- const payload = {
408
+ const combinedSig = combineSignatureV2(signature.v, signature.r, signature.s);
409
+ const defaultAccepted = accepted ?? {
410
+ scheme: "exact",
411
+ network: network.caip2Id,
412
+ amount,
413
+ asset: network.usdcAddress,
414
+ payTo: to,
415
+ maxTimeoutSeconds: expiry - Math.floor(Date.now() / 1e3)
416
+ };
417
+ const x402Payload = createX402V2Payment({
229
418
  from,
230
419
  to,
231
- amount,
232
- nonce,
233
- expiry,
420
+ value: amount,
421
+ nonce: `0x${nonce.padStart(64, "0")}`,
422
+ validAfter: "0x0",
423
+ validBefore: `0x${expiry.toString(16)}`,
424
+ signature: combinedSig,
425
+ accepted: defaultAccepted
426
+ });
427
+ return {
234
428
  signature: {
235
429
  v: signature.v,
236
430
  r: signature.r,
237
431
  s: signature.s
238
432
  },
239
- chainId: network.caip2Id,
240
- assetId: network.caipAssetId
433
+ payload: x402Payload
241
434
  };
242
- return { signature: payload.signature, payload };
243
435
  };
244
436
  var createX402Client = (config) => {
245
437
  const state = createClientState(config);
438
+ const fetch2 = async (url, init) => {
439
+ let response = await fetch2(url, init);
440
+ if (response.status === 402) {
441
+ const version = detectX402Version(response, state.version);
442
+ const parsed = await parsePaymentRequired(response, version);
443
+ const selectedRequirements = selectSchemeRequirements(parsed.requirements, "exact");
444
+ if (!selectedRequirements) {
445
+ throw new Error("No supported payment scheme found in requirements");
446
+ }
447
+ const from = getAddress(state.account);
448
+ let result;
449
+ if (version === 1) {
450
+ result = await handlePaymentRequirements(selectedRequirements, state);
451
+ } else {
452
+ const req = selectedRequirements;
453
+ const to = typeof req.payTo === "string" ? req.payTo : "0x0000000000000000000000000000000000000000";
454
+ result = await signPaymentV2(state, {
455
+ from,
456
+ to,
457
+ amount: req.amount,
458
+ nonce: crypto.randomUUID(),
459
+ expiry: Math.floor(Date.now() / 1e3) + DEFAULT_EXPIRY_SECONDS,
460
+ accepted: req
461
+ });
462
+ }
463
+ const paymentHeaders = new Headers(init?.headers);
464
+ if (version === 1) {
465
+ paymentHeaders.set(V1_HEADERS2.PAYMENT, encodePaymentV1(result.payload));
466
+ } else {
467
+ paymentHeaders.set(V2_HEADERS2.PAYMENT_SIGNATURE, encodePaymentV2(result.payload));
468
+ }
469
+ response = await fetch2(url, { ...init, headers: paymentHeaders });
470
+ }
471
+ return response;
472
+ };
246
473
  return {
474
+ fetch: fetch2,
247
475
  getAccount: () => state.account,
248
476
  getNetwork: () => state.network,
249
477
  getVersion: () => state.version,
@@ -276,28 +504,28 @@ var createX402Client = (config) => {
276
504
  return headers;
277
505
  },
278
506
  handlePaymentRequired: async (requirements) => {
279
- if ("contractAddress" in requirements) {
280
- const req2 = requirements;
281
- return signPayment({
282
- amount: req2.amount,
283
- to: req2.payTo,
284
- expiry: req2.expiry
285
- }, state);
286
- }
287
- const req = requirements;
288
- const to = typeof req.to === "string" ? req.to : "0x0000000000000000000000000000000000000000";
289
- return signPayment({
290
- amount: req.amount,
291
- to,
292
- nonce: req.nonce,
293
- expiry: req.expiry
294
- }, state);
507
+ return handlePaymentRequirements(requirements, state);
295
508
  },
296
509
  verifySettlement: (response) => {
297
- return "success" in response ? response.success : response.status === "success";
510
+ if ("success" in response) {
511
+ return response.success;
512
+ }
513
+ return false;
298
514
  }
299
515
  };
300
516
  };
517
+ var detectVersionFromRequirements = (requirements) => {
518
+ if (isX402V2Requirements(requirements)) {
519
+ return 2;
520
+ }
521
+ if (isX402V1Requirements(requirements)) {
522
+ return 1;
523
+ }
524
+ if ("contractAddress" in requirements) {
525
+ return 1;
526
+ }
527
+ return 2;
528
+ };
301
529
  var signPayment = async (options, state) => {
302
530
  const from = getAddress(state.account);
303
531
  if (state.version === 1) {
@@ -320,50 +548,47 @@ var signPayment = async (options, state) => {
320
548
  };
321
549
 
322
550
  // src/transport.ts
323
- import { V1_HEADERS, V2_HEADERS, encodePaymentV1 as encodePaymentV12, encodePaymentV2 as encodePaymentV22 } from "@armory-sh/base";
551
+ import {
552
+ V1_HEADERS as V1_HEADERS3,
553
+ V2_HEADERS as V2_HEADERS3,
554
+ encodePaymentV1 as encodePaymentV12,
555
+ encodePaymentV2 as encodePaymentV22,
556
+ isX402V1Requirements as isX402V1Requirements2,
557
+ isX402V2Requirements as isX402V2Requirements2
558
+ } from "@armory-sh/base";
324
559
  var DEFAULT_MAX_RETRIES = 3;
325
- var detectProtocolVersion = (response, client) => {
326
- if (response.headers.has(V2_HEADERS.PAYMENT_REQUIRED) || response.headers.has("PAYMENT-REQUIRED")) {
327
- return 2;
328
- }
329
- if (response.headers.has("X-PAYMENT-REQUIRED")) {
330
- return 1;
331
- }
332
- return client.getVersion();
333
- };
334
- var parsePaymentRequirements = async (response, version) => {
335
- if (version === 2) {
336
- const header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
337
- if (header) return JSON.parse(header);
338
- } else {
339
- const header = response.headers.get("X-PAYMENT-REQUIRED");
340
- if (header) {
341
- const json = Buffer.from(header, "base64").toString("utf-8");
342
- return JSON.parse(json);
343
- }
344
- }
345
- return JSON.parse(await response.clone().text());
346
- };
347
560
  var createPaymentHeaders = (payload, version) => {
348
561
  const headers = new Headers();
349
562
  if (version === 1) {
350
- headers.set(V1_HEADERS.PAYMENT, encodePaymentV12(payload));
563
+ headers.set(V1_HEADERS3.PAYMENT, encodePaymentV12(payload));
351
564
  } else {
352
- headers.set(V2_HEADERS.PAYMENT_SIGNATURE, encodePaymentV22(payload));
565
+ headers.set(V2_HEADERS3.PAYMENT_SIGNATURE, encodePaymentV22(payload));
353
566
  }
354
567
  return headers;
355
568
  };
356
- var isPaymentRelatedError = (error) => error.message.includes("402") || error.message.includes("payment") || error.message.includes("signature");
569
+ var isPaymentRelatedError = (error) => error.message.includes("402") || error.message.includes("payment") || error.message.includes("signature") || error.message.includes("Payment");
357
570
  var backoff = (attempt) => {
358
- const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
359
- return new Promise((resolve) => setTimeout(resolve, delay));
571
+ const baseDelay = Math.min(1e3 * Math.pow(2, attempt - 1), 1e4);
572
+ const jitter = Math.random() * 100;
573
+ return new Promise((resolve) => setTimeout(resolve, baseDelay + jitter));
360
574
  };
361
575
  var handlePaymentRequired = async (response, client) => {
362
- const version = detectProtocolVersion(response, client);
363
- const requirements = await parsePaymentRequirements(response, version);
364
- const result = await client.handlePaymentRequired(
365
- requirements
366
- );
576
+ const version = detectX402Version(response, client.getVersion());
577
+ const parsed = await parsePaymentRequired(response, version);
578
+ const selectedRequirements = selectSchemeRequirements(parsed.requirements, "exact");
579
+ if (!selectedRequirements) {
580
+ throw new Error("No supported payment scheme found in requirements");
581
+ }
582
+ let result;
583
+ if (version === 1 && isX402V1Requirements2(selectedRequirements)) {
584
+ const req = selectedRequirements;
585
+ result = await client.handlePaymentRequired(req);
586
+ } else if (version === 2 && isX402V2Requirements2(selectedRequirements)) {
587
+ const req = selectedRequirements;
588
+ result = await client.handlePaymentRequired(req);
589
+ } else {
590
+ result = await client.handlePaymentRequired(selectedRequirements);
591
+ }
367
592
  return createPaymentHeaders(result.payload, version);
368
593
  };
369
594
  var mergePaymentHeaders = (init = {}, paymentHeaders) => {
@@ -386,7 +611,7 @@ var createX402Transport = (options) => {
386
611
  attempt++;
387
612
  try {
388
613
  const response = await fetch(url, init);
389
- if (response.status === 402 && autoSign) {
614
+ if (isPaymentRequiredResponse(response) && autoSign) {
390
615
  const paymentHeaders = await handlePaymentRequired(response, client);
391
616
  const newInit = mergePaymentHeaders(init, paymentHeaders);
392
617
  return await fetch(url, newInit);
@@ -416,15 +641,12 @@ var isV2Settlement = (response) => "status" in response;
416
641
 
417
642
  // src/index.ts
418
643
  import {
419
- V1_HEADERS as V1_HEADERS2,
644
+ V1_HEADERS as V1_HEADERS4,
645
+ V2_HEADERS as V2_HEADERS4,
420
646
  encodePaymentV1 as encodePaymentV13,
421
647
  decodePaymentV1,
422
648
  encodeSettlementV1,
423
649
  decodeSettlementV1,
424
- V2_HEADERS as V2_HEADERS2,
425
- isCAIP2ChainId,
426
- isCAIPAssetId,
427
- isAddress,
428
650
  encodePaymentV2 as encodePaymentV23,
429
651
  decodePaymentV2,
430
652
  encodeSettlementV2,
@@ -434,9 +656,9 @@ import {
434
656
  getPaymentVersion,
435
657
  getRequirementsVersion,
436
658
  getSettlementVersion,
437
- getPaymentHeaderName,
659
+ getPaymentHeaderName as getPaymentHeaderName2,
438
660
  getPaymentResponseHeaderName,
439
- getPaymentRequiredHeaderName,
661
+ getPaymentRequiredHeaderName as getPaymentRequiredHeaderName2,
440
662
  isSettlementSuccessful,
441
663
  getTxHash,
442
664
  NETWORKS,
@@ -444,6 +666,15 @@ import {
444
666
  getNetworkByChainId,
445
667
  getMainnets,
446
668
  getTestnets,
669
+ isX402V1Payload,
670
+ isX402V2Payload,
671
+ isX402V1Requirements as isX402V1Requirements3,
672
+ isX402V2Requirements as isX402V2Requirements3,
673
+ isX402V1Settlement,
674
+ isX402V2Settlement,
675
+ combineSignatureV2 as combineSignatureV22,
676
+ parseSignatureV2,
677
+ createNonce as createNonce2,
447
678
  EIP712_TYPES as EIP712_TYPES2,
448
679
  USDC_DOMAIN as USDC_DOMAIN2,
449
680
  createEIP712Domain as createEIP712Domain2,
@@ -456,21 +687,28 @@ export {
456
687
  EIP712_TYPES,
457
688
  NETWORKS,
458
689
  USDC_DOMAIN,
459
- V1_HEADERS2 as V1_HEADERS,
460
- V2_HEADERS2 as V2_HEADERS,
690
+ V1_HEADERS4 as V1_HEADERS,
691
+ V2_HEADERS4 as V2_HEADERS,
461
692
  adjustVForChainId,
693
+ combineSignatureV22 as combineSignatureV2,
462
694
  concatenateSignature,
463
695
  createEIP712Domain2 as createCoreEIP712Domain,
464
696
  createTransferWithAuthorization2 as createCoreTransferWithAuthorization,
465
697
  createEIP712Domain,
466
698
  createFetchWithX402,
699
+ createNonce2 as createNonce,
700
+ createPaymentHeader,
467
701
  createTransferWithAuthorization,
468
702
  createX402Client,
469
703
  createX402Transport,
704
+ createX402V1Payment,
705
+ createX402V2Payment,
470
706
  decodePaymentV1,
471
707
  decodePaymentV2,
472
708
  decodeSettlementV1,
473
709
  decodeSettlementV2,
710
+ detectVersionFromObject,
711
+ detectX402Version,
474
712
  encodePaymentV13 as encodePaymentV1,
475
713
  encodePaymentV23 as encodePaymentV2,
476
714
  encodeSettlementV1,
@@ -478,17 +716,17 @@ export {
478
716
  getMainnets,
479
717
  getNetworkByChainId,
480
718
  getNetworkConfig2 as getNetworkConfig,
481
- getPaymentHeaderName,
482
- getPaymentRequiredHeaderName,
719
+ getPaymentHeader,
720
+ getPaymentHeaderName2 as getPaymentHeaderName,
721
+ getPaymentRequiredHeader,
722
+ getPaymentRequiredHeaderName2 as getPaymentRequiredHeaderName,
483
723
  getPaymentResponseHeaderName,
484
724
  getPaymentVersion,
485
725
  getRequirementsVersion,
486
726
  getSettlementVersion,
487
727
  getTestnets,
488
728
  getTxHash,
489
- isAddress,
490
- isCAIP2ChainId,
491
- isCAIPAssetId,
729
+ isPaymentRequiredResponse,
492
730
  isSettlementSuccessful,
493
731
  isV1,
494
732
  isV1Requirements,
@@ -496,7 +734,18 @@ export {
496
734
  isV2,
497
735
  isV2Requirements,
498
736
  isV2Settlement,
737
+ isX402V1Payload,
738
+ isX402V1Requirements3 as isX402V1Requirements,
739
+ isX402V1Settlement,
740
+ isX402V2Payload,
741
+ isX402V2Requirements3 as isX402V2Requirements,
742
+ isX402V2Settlement,
743
+ parsePaymentRequired,
744
+ parsePaymentRequiredFromBody,
745
+ parsePaymentRequiredFromHeader,
499
746
  parseSignature,
747
+ parseSignatureV2,
748
+ selectSchemeRequirements,
500
749
  signTypedData,
501
750
  signWithPrivateKey,
502
751
  validateTransferWithAuthorization2 as validateCoreTransferWithAuthorization,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/client-web3",
3
- "version": "0.2.6",
3
+ "version": "0.2.10",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "type": "module",
@@ -27,7 +27,7 @@
27
27
  "directory": "packages/client-web3"
28
28
  },
29
29
  "dependencies": {
30
- "@armory-sh/base": "^0.2.9",
30
+ "@armory-sh/base": "^0.2.13",
31
31
  "web3": "4.16.0",
32
32
  "web3-types": "1.10.0"
33
33
  },
@@ -36,7 +36,7 @@
36
36
  "bun-types": "latest"
37
37
  },
38
38
  "scripts": {
39
- "build": "tsup",
39
+ "build": "rm -rf dist && tsup",
40
40
  "test": "bun test",
41
41
  "example": "bun run examples/"
42
42
  }