@armory-sh/client-viem 0.2.13 → 0.2.15

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