@armory-sh/client-viem 0.2.12 → 0.2.14

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,23 +1,23 @@
1
1
  import { Address, Account, WalletClient, Transport } from 'viem';
2
- import { CustomToken, X402PaymentPayloadV1, PaymentPayloadV2, X402PaymentRequirementsV1, PaymentRequirementsV2, NetworkId, TokenId, ArmoryPaymentResult, ValidationError } from '@armory-sh/base';
3
- export { ArmoryPaymentResult, EIP3009Authorization, EIP3009AuthorizationV1, FacilitatorConfig, NetworkId, PaymentPayloadV2, PaymentRequiredV2, PaymentRequirementsV2, ResourceInfo, SchemePayloadV2, SettlementResponseV2, TokenId, V1_HEADERS, V2_HEADERS, X402PaymentPayloadV1, X402PaymentRequiredV1, X402PaymentRequirementsV1, X402SchemePayloadV1, X402SettlementResponseV1 } from '@armory-sh/base';
2
+ import { CustomToken, PaymentPayloadV2, PaymentRequirementsV2, NetworkId, TokenId, ArmoryPaymentResult, ValidationError } from '@armory-sh/base';
3
+ export { ArmoryPaymentResult, EIP3009Authorization, FacilitatorConfig, NetworkId, PaymentPayloadV2, PaymentRequiredV2, PaymentRequirementsV2, ResourceInfo, SchemePayloadV2, SettlementResponseV2, TokenId, V2_HEADERS } from '@armory-sh/base';
4
4
 
5
5
  /**
6
- * X402 Client Types - V1 and V2 Compatible
6
+ * X402 Client Types - V2 Only
7
7
  */
8
8
 
9
- type X402Wallet$1 = {
9
+ type X402Wallet = {
10
10
  type: "account";
11
11
  account: Account;
12
12
  } | {
13
13
  type: "walletClient";
14
14
  walletClient: WalletClient;
15
15
  };
16
- type X402ProtocolVersion = 1 | 2 | "auto";
16
+ type X402ProtocolVersion = 2;
17
17
  /** Token configuration - can use pre-configured tokens from @armory-sh/tokens */
18
18
  type Token = CustomToken;
19
19
  interface X402ClientConfig {
20
- wallet: X402Wallet$1;
20
+ wallet: X402Wallet;
21
21
  version?: X402ProtocolVersion;
22
22
  defaultExpiry?: number;
23
23
  nonceGenerator?: () => string;
@@ -32,11 +32,11 @@ interface X402ClientConfig {
32
32
  interface X402Client {
33
33
  fetch: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
34
34
  getAddress(): Address;
35
- createPayment(amount: string, to: Address, contractAddress: Address, chainId: number, expiry?: number): Promise<X402PaymentPayloadV1 | PaymentPayloadV2>;
36
- signPayment(payload: Omit<X402PaymentPayloadV1 | PaymentPayloadV2, "signature" | "v" | "r" | "s">): Promise<X402PaymentPayloadV1 | PaymentPayloadV2>;
35
+ createPayment(amount: string, to: Address, contractAddress: Address, chainId: number, expiry?: number): Promise<PaymentPayloadV2>;
36
+ signPayment(payload: UnsignedPaymentPayload): Promise<PaymentPayloadV2>;
37
37
  }
38
38
  interface X402TransportConfig {
39
- wallet: X402Wallet$1;
39
+ wallet: X402Wallet;
40
40
  transport?: Transport;
41
41
  version?: X402ProtocolVersion;
42
42
  defaultExpiry?: number;
@@ -55,17 +55,23 @@ interface PaymentResult {
55
55
  error?: string;
56
56
  timestamp: number;
57
57
  }
58
+ interface UnsignedPaymentPayload {
59
+ from: `0x${string}`;
60
+ to: `0x${string}`;
61
+ amount: string;
62
+ nonce: string;
63
+ expiry: number;
64
+ chainId: number;
65
+ contractAddress: `0x${string}`;
66
+ network: string;
67
+ }
58
68
 
59
69
  /**
60
- * X402 Client for Viem - Full V1 and V2 Support
61
- *
62
- * Handles x402 V1 (Base64 encoded) and V2 (JSON) protocols.
63
- * Uses protocol.ts for parsing and creating payment payloads.
70
+ * X402 Client for Viem - V2 Only
64
71
  */
65
72
 
66
- declare const createFetch: (wallet: X402Wallet$1, config: Omit<X402ClientConfig, "wallet">) => (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
67
73
  declare const createX402Client: (config: X402ClientConfig) => X402Client;
68
- declare const createX402Transport: (config: X402TransportConfig) => ReturnType<typeof createFetch>;
74
+ declare const createX402Transport: (config: X402TransportConfig) => ((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>);
69
75
 
70
76
  declare class X402ClientError extends Error {
71
77
  readonly cause?: unknown;
@@ -79,52 +85,16 @@ declare class PaymentError extends X402ClientError {
79
85
  }
80
86
 
81
87
  /**
82
- * X402 Protocol Implementation for Viem Client
88
+ * X402 Protocol Implementation for Viem Client (V2 Only)
83
89
  *
84
- * Handles parsing x402 V1 and V2 PAYMENT-REQUIRED headers
85
- * and generating x402 V1 and V2 PAYMENT-SIGNATURE payloads
90
+ * Handles parsing x402 V2 PAYMENT-REQUIRED headers
91
+ * and generating x402 V2 PAYMENT-SIGNATURE payloads
86
92
  */
87
93
 
88
- type X402Wallet = {
89
- type: "account";
90
- account: Account;
91
- } | {
92
- type: "walletClient";
93
- walletClient: WalletClient;
94
- };
95
- /**
96
- * Detect x402 protocol version from response headers
97
- */
98
- declare function detectX402Version(response: Response): 1 | 2;
99
- /**
100
- * Parse x402 PAYMENT-REQUIRED header from response
101
- * Automatically detects V1 or V2 format
102
- */
103
- interface ParsedPaymentRequirements {
104
- version: 1 | 2;
105
- requirements: X402PaymentRequirementsV1 | PaymentRequirementsV2;
106
- }
94
+ type X402Version = 2;
95
+ declare function detectX402Version(_response: Response): X402Version;
96
+ type ParsedPaymentRequirements = PaymentRequirementsV2;
107
97
  declare function parsePaymentRequired(response: Response): ParsedPaymentRequirements;
108
- /**
109
- * Create x402 V1 payment payload
110
- */
111
- declare function createX402V1Payment(wallet: X402Wallet, requirements: X402PaymentRequirementsV1, fromAddress: Address, nonce: `0x${string}`, validBefore: number, domainName?: string, domainVersion?: string): Promise<X402PaymentPayloadV1>;
112
- /**
113
- * Create x402 V2 payment payload
114
- */
115
- declare function createX402V2Payment(wallet: X402Wallet, requirements: PaymentRequirementsV2, fromAddress: Address, nonce: `0x${string}`, validBefore: number, domainName?: string, domainVersion?: string): Promise<PaymentPayloadV2>;
116
- /**
117
- * Create x402 payment payload (auto-detects version)
118
- */
119
- declare function createX402Payment(wallet: X402Wallet, parsed: ParsedPaymentRequirements, fromAddress: Address, nonce: `0x${string}`, validBefore: number, domainName?: string, domainVersion?: string): Promise<X402PaymentPayloadV1 | PaymentPayloadV2>;
120
- /**
121
- * Encode x402 payment payload to Base64 for transport
122
- */
123
- declare function encodeX402Payment(payload: X402PaymentPayloadV1 | PaymentPayloadV2): string;
124
- /**
125
- * Get the correct header name for payment based on version
126
- */
127
- declare function getPaymentHeaderName(version: 1 | 2): string;
128
98
 
129
99
  /**
130
100
  * Simple one-line payment API for Armory
@@ -166,8 +136,8 @@ declare const armoryPay: <T = unknown>(wallet: SimpleWallet, url: string, networ
166
136
  body?: unknown;
167
137
  /** Request headers */
168
138
  headers?: Record<string, string>;
169
- /** Protocol version (default: auto-detect) */
170
- version?: 1 | 2 | "auto";
139
+ /** Protocol version (V2 only) */
140
+ version?: 2;
171
141
  /** Payment amount in token units (default: from 402 header) */
172
142
  amount?: string;
173
143
  /** Enable debug logging */
@@ -209,4 +179,4 @@ declare const getNetworks: () => string[];
209
179
  */
210
180
  declare const getTokens: () => string[];
211
181
 
212
- export { type ParsedPaymentRequirements, PaymentError, type PaymentResult, type X402Wallet as ProtocolWallet, SigningError, type SimpleWallet, type Token, type X402Client, type X402ClientConfig, X402ClientError, type X402ProtocolVersion, type X402TransportConfig, type X402Wallet$1 as X402Wallet, armoryGet, armoryPay, armoryPost, createX402Client, createX402Payment, createX402Transport, createX402V1Payment, createX402V2Payment, detectX402Version, encodeX402Payment, getNetworks, getPaymentHeaderName, getTokens, getWalletAddress, parsePaymentRequired, validateNetwork, validateToken };
182
+ export { type ParsedPaymentRequirements, PaymentError, type PaymentResult, SigningError, type SimpleWallet, type Token, type X402Client, type X402ClientConfig, X402ClientError, type X402ProtocolVersion, type X402TransportConfig, type X402Wallet, armoryGet, armoryPay, armoryPost, createX402Client, createX402Transport, detectX402Version, getNetworks, getTokens, getWalletAddress, parsePaymentRequired, validateNetwork, validateToken };
package/dist/index.js CHANGED
@@ -1,13 +1,8 @@
1
1
  // src/client.ts
2
2
  import {
3
- V1_HEADERS as V1_HEADERS2,
4
3
  V2_HEADERS as V2_HEADERS2,
5
- decodeSettlementV1,
6
4
  decodeSettlementV2,
7
- isX402V1Settlement,
8
- isX402V2Settlement,
9
- getNetworkByChainId as getNetworkByChainId2,
10
- normalizeNetworkName as normalizeNetworkName2
5
+ getNetworkByChainId
11
6
  } from "@armory-sh/base";
12
7
 
13
8
  // src/errors.ts
@@ -34,301 +29,110 @@ var PaymentError = class extends X402ClientError {
34
29
 
35
30
  // src/protocol.ts
36
31
  import {
37
- V1_HEADERS,
38
32
  V2_HEADERS,
39
- safeBase64Decode,
40
- isX402V1PaymentRequired,
41
- isX402V2PaymentRequired,
42
- getNetworkByChainId,
43
- getNetworkConfig,
44
- createEIP712Domain,
45
- createTransferWithAuthorization,
46
- EIP712_TYPES,
47
- normalizeNetworkName
33
+ isX402V2PaymentRequired
48
34
  } from "@armory-sh/base";
49
- function detectX402Version(response) {
50
- const v2Header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
51
- if (v2Header) {
52
- try {
53
- const parsed = JSON.parse(v2Header);
54
- if (parsed.x402Version === 2) return 2;
55
- } catch {
56
- }
57
- }
58
- const v1Header = response.headers.get(V1_HEADERS.PAYMENT_REQUIRED);
59
- if (v1Header) {
60
- try {
61
- const decoded = safeBase64Decode(v1Header);
62
- const parsed = JSON.parse(decoded);
63
- if (parsed.x402Version === 1) return 1;
64
- return 1;
65
- } catch {
66
- }
67
- }
35
+ function detectX402Version(_response) {
68
36
  return 2;
69
37
  }
38
+ function getWalletAddress(wallet) {
39
+ return wallet.type === "account" ? wallet.account.address : wallet.walletClient.account.address;
40
+ }
70
41
  function parsePaymentRequired(response) {
71
- const version = detectX402Version(response);
72
- if (version === 2) {
73
- const v2Header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
74
- if (!v2Header) {
75
- throw new PaymentError("No PAYMENT-REQUIRED header found in 402 response");
76
- }
77
- try {
78
- const parsed = JSON.parse(v2Header);
79
- if (!isX402V2PaymentRequired(parsed)) {
80
- throw new PaymentError("Invalid x402 V2 payment required format");
81
- }
82
- if (!parsed.accepts || parsed.accepts.length === 0) {
83
- throw new PaymentError("No payment requirements found in accepts array");
84
- }
85
- return {
86
- version: 2,
87
- requirements: parsed.accepts[0]
88
- };
89
- } catch (error) {
90
- if (error instanceof PaymentError) throw error;
91
- throw new PaymentError(`Failed to parse V2 PAYMENT-REQUIRED header: ${error}`);
92
- }
93
- }
94
- const v1Header = response.headers.get(V1_HEADERS.PAYMENT_REQUIRED);
95
- if (!v1Header) {
96
- throw new PaymentError("No X-PAYMENT-REQUIRED header found in 402 response");
42
+ const v2Header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
43
+ if (!v2Header) {
44
+ throw new PaymentError("No PAYMENT-REQUIRED header found in V2 response");
97
45
  }
98
46
  try {
99
- const decoded = safeBase64Decode(v1Header);
47
+ const decoded = Buffer.from(v2Header, "base64").toString("utf-8");
100
48
  const parsed = JSON.parse(decoded);
101
- if (!isX402V1PaymentRequired(parsed)) {
102
- throw new PaymentError("Invalid x402 V1 payment required format");
49
+ if (!isX402V2PaymentRequired(parsed)) {
50
+ throw new PaymentError("Invalid x402 V2 payment required format");
103
51
  }
104
- if (!parsed.accepts || parsed.accepts.length === 0) {
105
- throw new PaymentError("No payment requirements found in accepts array");
106
- }
107
- return {
108
- version: 1,
109
- requirements: parsed.accepts[0]
110
- };
52
+ return parsed;
111
53
  } catch (error) {
112
54
  if (error instanceof PaymentError) throw error;
113
- throw new PaymentError(`Failed to parse V1 PAYMENT-REQUIRED header: ${error}`);
114
- }
115
- }
116
- function getWalletAddress(wallet) {
117
- return wallet.type === "account" ? wallet.account.address : wallet.walletClient.account.address;
118
- }
119
- async function signTypedData(wallet, domain, types, message) {
120
- if (wallet.type === "account" && !wallet.account.signTypedData) {
121
- throw new SigningError("Account does not support signTypedData");
55
+ throw new PaymentError(`Failed to parse V2 PAYMENT-REQUIRED header: ${error}`);
122
56
  }
123
- const params = {
124
- domain,
125
- types,
126
- primaryType: "TransferWithAuthorization",
127
- message
128
- };
129
- if (wallet.type === "account") {
130
- return wallet.account.signTypedData(params);
131
- }
132
- return wallet.walletClient.signTypedData({
133
- ...params,
134
- account: wallet.walletClient.account
135
- });
136
57
  }
137
- function parseSignature(signature) {
138
- const sig = signature.slice(2);
139
- return {
140
- v: parseInt(sig.slice(128, 130), 16) + 27,
141
- r: `0x${sig.slice(0, 64)}`,
142
- s: `0x${sig.slice(64, 128)}`
143
- };
144
- }
145
- function toAtomicUnits(amount) {
146
- return Math.floor(parseFloat(amount) * 1e6).toString();
147
- }
148
- function extractChainId(network) {
149
- if (network.startsWith("eip155:")) {
150
- return parseInt(network.split(":")[1], 10);
151
- }
152
- const net = getNetworkConfig(normalizeNetworkName(network));
153
- if (net) {
154
- return net.chainId;
155
- }
156
- throw new PaymentError(`Unsupported network: ${network}`);
58
+ function encodeX402Payment(payload) {
59
+ return Buffer.from(JSON.stringify(payload)).toString("base64");
157
60
  }
158
- function getNetworkSlug(network) {
159
- if (network.startsWith("eip155:")) {
160
- const chainId = parseInt(network.split(":")[1], 10);
161
- const net = getNetworkByChainId(chainId);
162
- if (!net) {
163
- throw new PaymentError(`No network config found for chainId: ${chainId}`);
164
- }
165
- return normalizeNetworkName(net.name);
61
+ async function createX402Payment(wallet, requirements, from, nonce = `0x${Date.now().toString(16).padStart(64, "0")}`, validBefore, _domainName, _domainVersion) {
62
+ const walletAccount = wallet.type === "account" ? wallet.account : wallet.walletClient.account;
63
+ if (!walletAccount.signTypedData) {
64
+ throw new SigningError("Wallet account does not support signTypedData");
166
65
  }
167
- return normalizeNetworkName(network);
168
- }
169
- async function createX402V1Payment(wallet, requirements, fromAddress, nonce, validBefore, domainName, domainVersion) {
170
- const network = getNetworkSlug(requirements.network);
171
- const contractAddress = requirements.asset;
172
- const domain = createEIP712Domain(extractChainId(requirements.network), contractAddress);
173
- const customDomain = domainName || domainVersion ? { ...domain, name: domainName ?? domain.name, version: domainVersion ?? domain.version } : domain;
174
- const authorization = {
175
- from: fromAddress,
176
- to: requirements.payTo,
177
- value: toAtomicUnits(requirements.maxAmountRequired),
178
- validAfter: "0",
179
- validBefore: validBefore.toString(),
180
- nonce
181
- };
182
- const value = createTransferWithAuthorization({
183
- from: authorization.from,
184
- to: authorization.to,
185
- value: BigInt(authorization.value),
186
- validAfter: BigInt(authorization.validAfter),
187
- validBefore: BigInt(authorization.validBefore),
188
- nonce: BigInt(authorization.nonce)
189
- });
190
- const signature = await signTypedData(wallet, customDomain, EIP712_TYPES, value);
191
- const { v, r, s } = parseSignature(signature);
192
- const payload = {
193
- signature: `0x${r.slice(2)}${s.slice(2)}${v.toString(16).padStart(2, "0")}`,
194
- authorization
195
- };
196
- return {
197
- x402Version: 1,
198
- scheme: "exact",
199
- network,
200
- payload
201
- };
202
- }
203
- async function createX402V2Payment(wallet, requirements, fromAddress, nonce, validBefore, domainName, domainVersion) {
204
- const contractAddress = requirements.asset;
205
- const network = requirements.network;
206
- const domain = createEIP712Domain(extractChainId(network), contractAddress);
207
- const customDomain = domainName || domainVersion ? { ...domain, name: domainName ?? domain.name, version: domainVersion ?? domain.version } : domain;
208
66
  const authorization = {
209
- from: fromAddress,
67
+ from,
210
68
  to: requirements.payTo,
211
- value: toAtomicUnits(requirements.amount),
69
+ value: requirements.amount,
212
70
  validAfter: "0",
213
- validBefore: validBefore.toString(),
71
+ validBefore: (validBefore ?? Math.floor(Date.now() / 1e3 + 3600)).toString(),
214
72
  nonce
215
73
  };
216
- const value = createTransferWithAuthorization({
217
- from: authorization.from,
218
- to: authorization.to,
219
- value: BigInt(authorization.value),
220
- validAfter: BigInt(authorization.validAfter),
221
- validBefore: BigInt(authorization.validBefore),
222
- nonce: BigInt(authorization.nonce)
223
- });
224
- const signature = await signTypedData(wallet, customDomain, EIP712_TYPES, value);
225
- const { v, r, s } = parseSignature(signature);
226
- const payload = {
227
- signature: `0x${r.slice(2)}${s.slice(2)}${v.toString(16).padStart(2, "0")}`,
228
- authorization
229
- };
230
74
  return {
231
75
  x402Version: 2,
232
- accepted: requirements,
233
- payload
76
+ scheme: requirements.scheme,
77
+ network: requirements.network,
78
+ payload: {
79
+ signature: "0x",
80
+ authorization
81
+ }
234
82
  };
235
83
  }
236
- async function createX402Payment(wallet, parsed, fromAddress, nonce, validBefore, domainName, domainVersion) {
237
- if (parsed.version === 1) {
238
- return createX402V1Payment(
239
- wallet,
240
- parsed.requirements,
241
- fromAddress,
242
- nonce,
243
- validBefore,
244
- domainName,
245
- domainVersion
246
- );
247
- }
248
- return createX402V2Payment(
249
- wallet,
250
- parsed.requirements,
251
- fromAddress,
252
- nonce,
253
- validBefore,
254
- domainName,
255
- domainVersion
256
- );
257
- }
258
- function encodeX402Payment(payload) {
259
- return Buffer.from(JSON.stringify(payload)).toString("base64");
260
- }
261
- function getPaymentHeaderName(version) {
262
- return version === 1 ? V1_HEADERS.PAYMENT : V2_HEADERS.PAYMENT_SIGNATURE;
263
- }
264
84
 
265
85
  // src/client.ts
266
86
  var DEFAULT_EXPIRY = 3600;
87
+ var toCaip2Id = (chainId) => `eip155:${chainId}`;
267
88
  var toProtocolWallet = (wallet) => wallet.type === "account" ? { type: "account", account: wallet.account } : { type: "walletClient", walletClient: wallet.walletClient };
268
89
  var generateNonce = (nonceGenerator) => {
269
90
  const nonce = nonceGenerator?.() ?? Date.now().toString();
270
- return nonce.startsWith("0x") ? nonce : `0x${nonce.padStart(64, "0")}`;
271
- };
272
- var checkSettlement = (response, version) => {
273
- const v1Header = response.headers.get(V1_HEADERS2.PAYMENT_RESPONSE);
274
- const v2Header = response.headers.get(V2_HEADERS2.PAYMENT_RESPONSE);
275
- if (version === 1 && v1Header) {
276
- try {
277
- const settlement = decodeSettlementV1(v1Header);
278
- if (isX402V1Settlement(settlement) && !settlement.success) {
279
- throw new PaymentError(settlement.errorReason ?? "Payment settlement failed");
280
- }
281
- } catch (error) {
282
- if (error instanceof PaymentError) throw error;
283
- }
284
- } else if (version === 2 && v2Header) {
285
- try {
286
- const settlement = decodeSettlementV2(v2Header);
287
- if (isX402V2Settlement(settlement) && !settlement.success) {
288
- throw new PaymentError(settlement.errorReason ?? "Payment settlement failed");
289
- }
290
- } catch (error) {
291
- if (error instanceof PaymentError) throw error;
292
- }
91
+ if (nonce.startsWith("0x")) {
92
+ return nonce;
293
93
  }
94
+ const numValue = parseInt(nonce, 10);
95
+ if (isNaN(numValue)) {
96
+ return `0x${nonce.padStart(64, "0")}`;
97
+ }
98
+ return `0x${numValue.toString(16).padStart(64, "0")}`;
99
+ };
100
+ var extractDomainConfig = (config) => config.token ? { domainName: config.token.name, domainVersion: config.token.version } : { domainName: config.domainName, domainVersion: config.domainVersion };
101
+ var addPaymentHeader = (headers, payment) => {
102
+ const encoded = encodeX402Payment(payment);
103
+ headers.set(V2_HEADERS2.PAYMENT_SIGNATURE, encoded);
294
104
  };
295
- var addPaymentHeader = (headers, payment, version) => {
296
- if (version === 1) {
297
- headers.set(V1_HEADERS2.PAYMENT, encodeX402Payment(payment));
298
- } else {
299
- headers.set(V2_HEADERS2.PAYMENT_SIGNATURE, JSON.stringify(payment));
105
+ var checkSettlement = (response) => {
106
+ const settlementHeader = response.headers.get(V2_HEADERS2.PAYMENT_RESPONSE);
107
+ if (!settlementHeader) {
108
+ throw new PaymentError("No PAYMENT-RESPONSE header found");
109
+ }
110
+ try {
111
+ return decodeSettlementV2(settlementHeader);
112
+ } catch (error) {
113
+ if (error instanceof SyntaxError) {
114
+ throw new PaymentError(`Failed to decode settlement: ${error.message}`);
115
+ }
116
+ throw error;
300
117
  }
301
118
  };
302
119
  var createFetch = (wallet, config) => {
303
- const { version = "auto", nonceGenerator, debug = false, domainName, domainVersion } = config;
304
120
  const protocolWallet = toProtocolWallet(wallet);
305
121
  const getAddress = () => getWalletAddress(protocolWallet);
122
+ const { defaultExpiry, nonceGenerator, debug, domainName, domainVersion } = config;
306
123
  return async (input, init) => {
307
- const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
308
- if (debug) {
309
- console.log(`[X402] Fetching: ${url}`);
310
- }
311
124
  let response = await fetch(input, init);
312
125
  if (response.status === 402) {
313
126
  if (debug) {
314
- console.log(`[X402] Payment required for: ${url}`);
315
- }
316
- let detectedVersion;
317
- if (version === "auto") {
318
- detectedVersion = detectX402Version(response);
319
- } else {
320
- detectedVersion = version;
321
- }
322
- if (debug) {
323
- console.log(`[X402] Detected protocol v${detectedVersion}`);
127
+ console.log("[X402] Payment required");
324
128
  }
325
129
  const parsed = parsePaymentRequired(response);
326
130
  if (debug) {
327
- console.log(`[X402] Payment requirements:`, parsed.requirements);
131
+ console.log("[X402] Payment requirements:", parsed);
328
132
  }
329
133
  const fromAddress = getAddress();
330
134
  const nonce = generateNonce(nonceGenerator);
331
- const validBefore = Math.floor(Date.now() / 1e3) + (config.defaultExpiry ?? DEFAULT_EXPIRY);
135
+ const validBefore = Math.floor(Date.now() / 1e3) + defaultExpiry;
332
136
  const payment = await createX402Payment(
333
137
  protocolWallet,
334
138
  parsed,
@@ -339,22 +143,21 @@ var createFetch = (wallet, config) => {
339
143
  domainVersion
340
144
  );
341
145
  if (debug) {
342
- console.log(`[X402] Created payment payload`);
146
+ console.log("[X402] Created payment payload");
343
147
  }
344
148
  const headers = new Headers(init?.headers);
345
- addPaymentHeader(headers, payment, parsed.version);
149
+ addPaymentHeader(headers, payment);
346
150
  response = await fetch(input, { ...init, headers });
347
151
  if (debug) {
348
152
  console.log(`[X402] Payment response status: ${response.status}`);
349
153
  }
350
- checkSettlement(response, parsed.version);
154
+ checkSettlement(response);
351
155
  }
352
156
  return response;
353
157
  };
354
158
  };
355
- var extractDomainConfig = (config) => config.token ? { domainName: config.token.name, domainVersion: config.token.version } : { domainName: config.domainName, domainVersion: config.domainVersion };
356
159
  var createX402Client = (config) => {
357
- const { wallet, version = "auto", defaultExpiry = DEFAULT_EXPIRY, nonceGenerator } = config;
160
+ const { wallet, version = 2, defaultExpiry = DEFAULT_EXPIRY, nonceGenerator } = config;
358
161
  const { domainName, domainVersion } = extractDomainConfig(config);
359
162
  const protocolWallet = toProtocolWallet(wallet);
360
163
  const getAddress = () => getWalletAddress(protocolWallet);
@@ -373,91 +176,51 @@ var createX402Client = (config) => {
373
176
  const from = getAddress();
374
177
  const nonce = generateNonce(nonceGenerator);
375
178
  const validBefore = expiry ?? Math.floor(Date.now() / 1e3) + defaultExpiry;
376
- const targetVersion = version === "auto" ? 1 : version;
377
- const networkConfig = getNetworkByChainId2(chainId);
378
- const networkSlug = networkConfig ? normalizeNetworkName2(networkConfig.name) : `eip155:${chainId}`;
379
- if (targetVersion === 1) {
380
- const requirements2 = {
381
- scheme: "exact",
382
- network: networkSlug,
383
- maxAmountRequired: amount,
384
- asset: contractAddress,
385
- payTo: to,
386
- resource: "",
387
- description: "",
388
- maxTimeoutSeconds: defaultExpiry
389
- };
390
- const parsed2 = { version: 1, requirements: requirements2 };
391
- return createX402Payment(protocolWallet, parsed2, from, nonce, validBefore, domainName, domainVersion);
392
- }
179
+ const networkConfig = getNetworkByChainId(chainId);
180
+ const caip2Network = networkConfig?.caip2Id ?? toCaip2Id(chainId);
393
181
  const requirements = {
394
182
  scheme: "exact",
395
- network: `eip155:${chainId}`,
183
+ network: caip2Network,
396
184
  amount,
397
185
  asset: contractAddress,
398
186
  payTo: to,
399
- maxTimeoutSeconds: defaultExpiry
187
+ maxTimeoutSeconds: validBefore - Math.floor(Date.now() / 1e3)
400
188
  };
401
- const parsed = { version: 2, requirements };
402
- return createX402Payment(protocolWallet, parsed, from, nonce, validBefore, domainName, domainVersion);
189
+ return createX402Payment(protocolWallet, requirements, from, nonce, validBefore, domainName, domainVersion);
403
190
  },
404
191
  async signPayment(payload) {
405
192
  const from = getAddress();
406
- const nonce = generateNonce(nonceGenerator);
407
- const hasContractAddress = (p) => typeof p === "object" && p !== null && "contractAddress" in p;
408
- if (hasContractAddress(payload)) {
409
- const networkConfig = getNetworkByChainId2(
410
- "chainId" in payload && typeof payload.chainId === "number" ? payload.chainId : 84532
411
- );
412
- const networkSlug = networkConfig ? normalizeNetworkName2(networkConfig.name) : "base";
413
- const to2 = "to" in payload && typeof payload.to === "string" ? payload.to : "0x0000000000000000000000000000000000000000";
414
- const requirements2 = {
415
- scheme: "exact",
416
- network: networkSlug,
417
- maxAmountRequired: payload.amount,
418
- asset: payload.contractAddress,
419
- payTo: to2,
420
- resource: "",
421
- description: "",
422
- maxTimeoutSeconds: defaultExpiry
423
- };
424
- const parsed2 = { version: 1, requirements: requirements2 };
425
- const validBefore2 = payload.expiry ?? Math.floor(Date.now() / 1e3) + defaultExpiry;
426
- return createX402Payment(protocolWallet, parsed2, from, nonce, validBefore2, domainName, domainVersion);
427
- }
428
- const to = "payTo" in payload && typeof payload.payTo === "string" ? payload.payTo : "0x0000000000000000000000000000000000000000";
429
- const amount = "amount" in payload && typeof payload.amount === "string" ? payload.amount : "1000000";
430
- const network = "network" in payload && typeof payload.network === "string" ? payload.network : "eip155:8453";
431
- const chainId = parseInt(network.split(":")[1], 10);
432
- const defaultAsset = getNetworkByChainId2(chainId)?.usdcAddress ?? "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
433
- const asset = "asset" in payload && typeof payload.asset === "string" ? payload.asset : defaultAsset;
193
+ const nonce = payload.nonce.startsWith("0x") ? payload.nonce : `0x${parseInt(payload.nonce, 10).toString(16).padStart(64, "0")}`;
194
+ const validBefore = payload.expiry;
195
+ const networkConfig = getNetworkByChainId(payload.chainId);
196
+ const caip2Network = networkConfig?.caip2Id ?? toCaip2Id(payload.chainId);
434
197
  const requirements = {
435
198
  scheme: "exact",
436
- network,
437
- amount,
438
- asset,
439
- payTo: to,
440
- maxTimeoutSeconds: defaultExpiry
199
+ network: caip2Network,
200
+ amount: payload.amount,
201
+ asset: payload.contractAddress,
202
+ payTo: payload.to,
203
+ maxTimeoutSeconds: validBefore - Math.floor(Date.now() / 1e3)
441
204
  };
442
- const parsed = { version: 2, requirements };
443
- const validBefore = "expiry" in payload && typeof payload.expiry === "number" ? payload.expiry : Math.floor(Date.now() / 1e3) + defaultExpiry;
444
- return createX402Payment(protocolWallet, parsed, from, nonce, validBefore, domainName, domainVersion);
205
+ return createX402Payment(protocolWallet, requirements, from, nonce, validBefore, domainName, domainVersion);
445
206
  }
446
207
  };
447
208
  };
448
209
  var createX402Transport = (config) => {
449
- const { wallet, version, defaultExpiry, nonceGenerator, debug, token, domainName, domainVersion } = config;
450
- return createFetch(wallet, {
451
- version,
452
- defaultExpiry,
453
- nonceGenerator,
454
- debug,
455
- domainName: domainName ?? token?.name,
456
- domainVersion: domainVersion ?? token?.version
210
+ const client = createX402Client({
211
+ wallet: config.wallet,
212
+ version: config.version ?? 2,
213
+ defaultExpiry: config.defaultExpiry,
214
+ nonceGenerator: config.nonceGenerator,
215
+ debug: config.debug,
216
+ token: config.token,
217
+ domainName: config.domainName,
218
+ domainVersion: config.domainVersion
457
219
  });
220
+ return client.fetch;
458
221
  };
459
222
 
460
- // src/simple.ts
223
+ // src/payment-api.ts
461
224
  import {
462
225
  resolveNetwork,
463
226
  resolveToken,
@@ -480,7 +243,7 @@ var armoryPay = async (wallet, url, network, token, options) => {
480
243
  }
481
244
  const client = createX402Client({
482
245
  wallet: x402Wallet,
483
- version: options?.version ?? "auto",
246
+ version: options?.version ?? 2,
484
247
  token: config.token.config,
485
248
  debug: options?.debug ?? false
486
249
  });
@@ -570,25 +333,19 @@ var getTokens = () => {
570
333
  };
571
334
 
572
335
  // src/index.ts
573
- import { V1_HEADERS as V1_HEADERS3, V2_HEADERS as V2_HEADERS3 } from "@armory-sh/base";
336
+ import { V2_HEADERS as V2_HEADERS3 } from "@armory-sh/base";
574
337
  export {
575
338
  PaymentError,
576
339
  SigningError,
577
- V1_HEADERS3 as V1_HEADERS,
578
340
  V2_HEADERS3 as V2_HEADERS,
579
341
  X402ClientError,
580
342
  armoryGet,
581
343
  armoryPay,
582
344
  armoryPost,
583
345
  createX402Client,
584
- createX402Payment,
585
346
  createX402Transport,
586
- createX402V1Payment,
587
- createX402V2Payment,
588
347
  detectX402Version,
589
- encodeX402Payment,
590
348
  getNetworks,
591
- getPaymentHeaderName,
592
349
  getTokens,
593
350
  getWalletAddress2 as getWalletAddress,
594
351
  parsePaymentRequired,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/client-viem",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
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-viem"
28
28
  },
29
29
  "dependencies": {
30
- "@armory-sh/base": "^0.2.12",
30
+ "@armory-sh/base": "^0.2.14",
31
31
  "viem": "2.45.0"
32
32
  },
33
33
  "devDependencies": {
@@ -35,7 +35,7 @@
35
35
  "bun-types": "latest"
36
36
  },
37
37
  "scripts": {
38
- "build": "tsup",
38
+ "build": "rm -rf dist && tsup",
39
39
  "test": "bun test",
40
40
  "example": "bun run examples/"
41
41
  }