@armory-sh/client-ethers 0.2.26-alpha.23.76 → 0.2.26-alpha.23.78

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +155 -152
  2. package/dist/index.js +172 -65
  3. package/package.json +4 -2
package/dist/index.d.ts CHANGED
@@ -1,37 +1,120 @@
1
+ import { NetworkId, TokenId, ArmoryPaymentResult, ValidationError, X402ClientError, CustomToken, PaymentRequirementsV2, Address, PaymentPayloadV2 } from '@armory-sh/base';
2
+ export { PaymentException as PaymentError, SigningError, X402ClientError } from '@armory-sh/base';
1
3
  import { Signer, Provider } from 'ethers';
2
4
  export { Provider, Signer } from 'ethers';
3
- import { PaymentRequirementsV2, Address, PaymentPayloadV2, X402ClientError, CustomToken, NetworkId, TokenId, ArmoryPaymentResult, ValidationError } from '@armory-sh/base';
4
- export { PaymentException as PaymentError, SigningError, X402ClientError } from '@armory-sh/base';
5
+ import { ClientHook } from '@armory-sh/base/types/hooks';
5
6
 
6
7
  /**
7
- * X402 Protocol Implementation for Ethers Client (V2 Only)
8
- *
9
- * Handles parsing x402 V2 PAYMENT-REQUIRED headers
10
- * and generating x402 V2 PAYMENT-SIGNATURE payloads
8
+ * Simple one-line payment API for Armory (Ethers)
9
+ * Focus on DX/UX - "everything just magically works"
11
10
  */
12
11
 
13
12
  /**
14
- * Detect x402 protocol version from response headers
15
- * V2-only: Always returns 2
13
+ * Simple wallet input - accepts wallet directly or wrapped for backward compatibility
16
14
  */
17
- declare function detectX402Version(_response: Response): 2;
15
+ type SimpleWalletInput = Signer | {
16
+ signer: Signer;
17
+ };
18
18
  /**
19
- * Get payment header name for protocol version
19
+ * Normalized wallet (internal use)
20
20
  */
21
- declare function getPaymentHeaderName(_version: 2): string;
22
- interface ParsedPaymentRequirements {
23
- version: 2;
24
- requirements: PaymentRequirementsV2;
25
- }
21
+ type NormalizedWallet = Signer;
26
22
  /**
27
- * Parse x402 PAYMENT-REQUIRED header from response
28
- * V2 only
23
+ * Normalize wallet input to internal format
29
24
  */
30
- declare function parsePaymentRequired(response: Response): ParsedPaymentRequirements;
25
+ declare const normalizeWallet: (wallet: SimpleWalletInput) => NormalizedWallet;
31
26
  /**
32
- * Create x402 payment payload (V2-only wrapper)
27
+ * Make a payment-protected API request with one line of code
33
28
  */
34
- declare function createX402Payment(signer: Signer, parsed: ParsedPaymentRequirements, fromAddress: Address, nonce?: `0x${string}`, validBefore?: number, domainName?: string, domainVersion?: string): Promise<PaymentPayloadV2>;
29
+ declare const armoryPay: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: {
30
+ /** Request method (default: GET) */
31
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
32
+ /** Request body (for POST/PUT/PATCH) */
33
+ body?: unknown;
34
+ /** Request headers */
35
+ headers?: Record<string, string>;
36
+ /** Protocol version (V2 only) */
37
+ version?: 2;
38
+ /** Payment amount in token units (default: from 402 header) */
39
+ amount?: string;
40
+ /** Enable debug logging */
41
+ debug?: boolean;
42
+ }) => Promise<ArmoryPaymentResult<T>>;
43
+ /**
44
+ * Make a GET request with payment
45
+ */
46
+ declare const armoryGet: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
47
+ /**
48
+ * Make a POST request with payment
49
+ */
50
+ declare const armoryPost: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
51
+ /**
52
+ * Make a PUT request with payment
53
+ */
54
+ declare const armoryPut: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
55
+ /**
56
+ * Make a DELETE request with payment
57
+ */
58
+ declare const armoryDelete: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
59
+ /**
60
+ * Make a PATCH request with payment
61
+ */
62
+ declare const armoryPatch: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
63
+ /**
64
+ * Get the wallet address from a SimpleWalletInput
65
+ */
66
+ declare const getWalletAddress: (wallet: SimpleWalletInput) => Promise<string>;
67
+ /**
68
+ * Validate a network identifier without making a request
69
+ */
70
+ declare const validateNetwork: (network: NetworkId) => ValidationError | {
71
+ success: true;
72
+ network: string;
73
+ };
74
+ /**
75
+ * Validate a token identifier without making a request
76
+ */
77
+ declare const validateToken: (token: TokenId, network?: NetworkId) => ValidationError | {
78
+ success: true;
79
+ token: string;
80
+ network: string;
81
+ };
82
+ /**
83
+ * Get list of available networks
84
+ */
85
+ declare const getNetworks: () => string[];
86
+ /**
87
+ * Get list of available tokens
88
+ */
89
+ declare const getTokens: () => string[];
90
+
91
+ /**
92
+ * Armory API - Simplified payment interface (Ethers)
93
+ * Provides a configurable object with method-based payment functions
94
+ */
95
+
96
+ type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
97
+ interface ArmoryConfig {
98
+ wallet: SimpleWalletInput;
99
+ methods?: HttpMethod[] | HttpMethod;
100
+ tokens?: TokenId[] | TokenId;
101
+ chains?: NetworkId[] | NetworkId;
102
+ debug?: boolean;
103
+ }
104
+ interface PaymentOptions {
105
+ method?: HttpMethod;
106
+ body?: unknown;
107
+ }
108
+ interface ArmoryInstance {
109
+ get<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
110
+ post<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
111
+ put<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
112
+ delete<T>(url: string): Promise<ArmoryPaymentResult<T>>;
113
+ patch<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
114
+ pay<T>(url: string, options?: PaymentOptions): Promise<ArmoryPaymentResult<T>>;
115
+ call<T>(url: string): Promise<ArmoryPaymentResult<T>>;
116
+ }
117
+ declare const createArmory: (config: ArmoryConfig) => ArmoryInstance;
35
118
 
36
119
  declare class SignerRequiredError extends X402ClientError {
37
120
  constructor(message?: string);
@@ -58,6 +141,7 @@ interface X402ClientConfig {
58
141
  domainName?: string;
59
142
  /** Override EIP-712 domain version for custom tokens */
60
143
  domainVersion?: string;
144
+ hooks?: ClientHook<Signer>[];
61
145
  }
62
146
  interface SignerClientConfig extends X402ClientConfig {
63
147
  signer: Signer;
@@ -77,6 +161,7 @@ interface X402TransportConfig {
77
161
  onPaymentRequired?: (requirements: unknown) => boolean | Promise<boolean>;
78
162
  onPaymentSuccess?: (settlement: unknown) => void;
79
163
  onPaymentError?: (error: Error) => void;
164
+ hooks?: ClientHook<Signer>[];
80
165
  }
81
166
  interface X402RequestInit extends Omit<RequestInit, "headers"> {
82
167
  headers?: Record<string, string> | Headers;
@@ -98,40 +183,6 @@ interface EIP712Domain {
98
183
  verifyingContract: `0x${string}`;
99
184
  }
100
185
 
101
- interface X402Transport {
102
- fetch(url: string, init?: X402RequestInit): Promise<Response>;
103
- get(url: string, init?: X402RequestInit): Promise<Response>;
104
- post(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
105
- put(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
106
- del(url: string, init?: X402RequestInit): Promise<Response>;
107
- patch(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
108
- setSigner(signer: Signer): void;
109
- getSigner(): Signer | undefined;
110
- }
111
- declare const createX402Transport: (config?: X402TransportConfig) => X402Transport;
112
-
113
- declare function signEIP3009(signer: Signer, params: TransferWithAuthorizationParams, domain: EIP712Domain): Promise<{
114
- v: number;
115
- r: string;
116
- s: string;
117
- }>;
118
- declare function signEIP3009WithDomain(signer: Signer, params: TransferWithAuthorizationParams, chainId: number, verifyingContract: `0x${string}`, domainName?: string, domainVersion?: string): Promise<{
119
- v: number;
120
- r: string;
121
- s: string;
122
- }>;
123
- declare function signPayment(signer: Signer, from: `0x${string}`, to: `0x${string}`, value: bigint, chainId: number, verifyingContract: `0x${string}`, expirySeconds?: number, domainName?: string, domainVersion?: string): Promise<{
124
- v: number;
125
- r: string;
126
- s: string;
127
- nonce: `0x${string}`;
128
- }>;
129
- declare function recoverEIP3009Signer(params: TransferWithAuthorizationParams, signature: {
130
- v: number;
131
- r: string;
132
- s: string;
133
- }, domain: EIP712Domain): Promise<`0x${string}`>;
134
-
135
186
  /**
136
187
  * X402 Client Factory for Ethers
137
188
  *
@@ -184,116 +235,68 @@ declare function createX402Client(config: X402ClientConfig & SignerClientConfig
184
235
  }): X402Client;
185
236
  declare function createX402Client(config: X402ClientConfig & ProviderClientConfig): X402Client;
186
237
 
187
- /**
188
- * Simple one-line payment API for Armory (Ethers)
189
- * Focus on DX/UX - "everything just magically works"
190
- */
238
+ declare function signEIP3009(signer: Signer, params: TransferWithAuthorizationParams, domain: EIP712Domain): Promise<{
239
+ v: number;
240
+ r: string;
241
+ s: string;
242
+ }>;
243
+ declare function signEIP3009WithDomain(signer: Signer, params: TransferWithAuthorizationParams, chainId: number, verifyingContract: `0x${string}`, domainName?: string, domainVersion?: string): Promise<{
244
+ v: number;
245
+ r: string;
246
+ s: string;
247
+ }>;
248
+ declare function signPayment(signer: Signer, from: `0x${string}`, to: `0x${string}`, value: bigint, chainId: number, verifyingContract: `0x${string}`, expirySeconds?: number, domainName?: string, domainVersion?: string): Promise<{
249
+ v: number;
250
+ r: string;
251
+ s: string;
252
+ nonce: `0x${string}`;
253
+ }>;
254
+ declare function recoverEIP3009Signer(params: TransferWithAuthorizationParams, signature: {
255
+ v: number;
256
+ r: string;
257
+ s: string;
258
+ }, domain: EIP712Domain): Promise<`0x${string}`>;
191
259
 
192
260
  /**
193
- * Simple wallet input - accepts wallet directly or wrapped for backward compatibility
194
- */
195
- type SimpleWalletInput = Signer | {
196
- signer: Signer;
197
- };
198
- /**
199
- * Normalized wallet (internal use)
200
- */
201
- type NormalizedWallet = Signer;
202
- /**
203
- * Normalize wallet input to internal format
204
- */
205
- declare const normalizeWallet: (wallet: SimpleWalletInput) => NormalizedWallet;
206
- /**
207
- * Make a payment-protected API request with one line of code
208
- */
209
- declare const armoryPay: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: {
210
- /** Request method (default: GET) */
211
- method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
212
- /** Request body (for POST/PUT/PATCH) */
213
- body?: unknown;
214
- /** Request headers */
215
- headers?: Record<string, string>;
216
- /** Protocol version (V2 only) */
217
- version?: 2;
218
- /** Payment amount in token units (default: from 402 header) */
219
- amount?: string;
220
- /** Enable debug logging */
221
- debug?: boolean;
222
- }) => Promise<ArmoryPaymentResult<T>>;
223
- /**
224
- * Make a GET request with payment
225
- */
226
- declare const armoryGet: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
227
- /**
228
- * Make a POST request with payment
229
- */
230
- declare const armoryPost: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
231
- /**
232
- * Make a PUT request with payment
233
- */
234
- declare const armoryPut: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
235
- /**
236
- * Make a DELETE request with payment
237
- */
238
- declare const armoryDelete: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
239
- /**
240
- * Make a PATCH request with payment
241
- */
242
- declare const armoryPatch: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, body?: unknown, options?: Omit<Parameters<typeof armoryPay>[4], "method" | "body">) => Promise<ArmoryPaymentResult<T>>;
243
- /**
244
- * Get the wallet address from a SimpleWalletInput
245
- */
246
- declare const getWalletAddress: (wallet: SimpleWalletInput) => Promise<string>;
247
- /**
248
- * Validate a network identifier without making a request
261
+ * X402 Protocol Implementation for Ethers Client (V2 Only)
262
+ *
263
+ * Handles parsing x402 V2 PAYMENT-REQUIRED headers
264
+ * and generating x402 V2 PAYMENT-SIGNATURE payloads
249
265
  */
250
- declare const validateNetwork: (network: NetworkId) => ValidationError | {
251
- success: true;
252
- network: string;
253
- };
266
+
254
267
  /**
255
- * Validate a token identifier without making a request
268
+ * Detect x402 protocol version from response headers
269
+ * V2-only: Always returns 2
256
270
  */
257
- declare const validateToken: (token: TokenId, network?: NetworkId) => ValidationError | {
258
- success: true;
259
- token: string;
260
- network: string;
261
- };
271
+ declare function detectX402Version(_response: Response): 2;
262
272
  /**
263
- * Get list of available networks
273
+ * Get payment header name for protocol version
264
274
  */
265
- declare const getNetworks: () => string[];
275
+ declare function getPaymentHeaderName(_version: 2): string;
276
+ interface ParsedPaymentRequirements {
277
+ version: 2;
278
+ accepts: PaymentRequirementsV2[];
279
+ }
266
280
  /**
267
- * Get list of available tokens
281
+ * Parse x402 PAYMENT-REQUIRED header from response
282
+ * V2 only
268
283
  */
269
- declare const getTokens: () => string[];
270
-
284
+ declare function parsePaymentRequired(response: Response): ParsedPaymentRequirements;
271
285
  /**
272
- * Armory API - Simplified payment interface (Ethers)
273
- * Provides a configurable object with method-based payment functions
286
+ * Create x402 payment payload (V2-only wrapper)
274
287
  */
288
+ declare function createX402Payment(signer: Signer, requirement: PaymentRequirementsV2, fromAddress: Address, nonce?: `0x${string}`, validBefore?: number, domainName?: string, domainVersion?: string): Promise<PaymentPayloadV2>;
275
289
 
276
- type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
277
- interface ArmoryConfig {
278
- wallet: SimpleWalletInput;
279
- methods?: HttpMethod[] | HttpMethod;
280
- tokens?: TokenId[] | TokenId;
281
- chains?: NetworkId[] | NetworkId;
282
- debug?: boolean;
283
- }
284
- interface PaymentOptions {
285
- method?: HttpMethod;
286
- body?: unknown;
287
- }
288
- interface ArmoryInstance {
289
- get<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
290
- post<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
291
- put<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
292
- delete<T>(url: string): Promise<ArmoryPaymentResult<T>>;
293
- patch<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
294
- pay<T>(url: string, options?: PaymentOptions): Promise<ArmoryPaymentResult<T>>;
295
- call<T>(url: string): Promise<ArmoryPaymentResult<T>>;
290
+ interface X402Transport {
291
+ fetch(url: string, init?: X402RequestInit): Promise<Response>;
292
+ get(url: string, init?: X402RequestInit): Promise<Response>;
293
+ post(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
294
+ put(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
295
+ del(url: string, init?: X402RequestInit): Promise<Response>;
296
+ patch(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
297
+ setSigner(signer: Signer): void;
298
+ getSigner(): Signer | undefined;
296
299
  }
297
- declare const createArmory: (config: ArmoryConfig) => ArmoryInstance;
300
+ declare const createX402Transport: (config?: X402TransportConfig) => X402Transport;
298
301
 
299
302
  export { type ArmoryConfig, type ArmoryInstance, AuthorizationError, type ClientConfig, type HttpMethod, type NormalizedWallet, type ParsedPaymentRequirements, type PaymentOptions, ProviderRequiredError, SignerRequiredError, type SimpleWalletInput, type X402Client, type X402ClientConfig, type X402RequestInit, type X402Transport, type X402TransportConfig, armoryDelete, armoryGet, armoryPatch, armoryPay, armoryPost, armoryPut, createArmory, createX402Client, createX402Payment, createX402Transport, detectX402Version, getNetworks, getPaymentHeaderName, getTokens, getWalletAddress, normalizeWallet, parsePaymentRequired, recoverEIP3009Signer, signEIP3009, signEIP3009WithDomain, signPayment, validateNetwork, validateToken };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { X402ClientError, createEIP712Domain, V2_HEADERS, PaymentException, isX402V2PaymentRequired, validatePaymentConfig, isValidationError, resolveNetwork, resolveToken, normalizeBase64Url, decodeBase64ToUtf8, getNetworkConfig, normalizeNetworkName, encodePaymentV2, decodeSettlementV2 } from '@armory-sh/base';
2
2
  export { PaymentException as PaymentError, SigningError, X402ClientError } from '@armory-sh/base';
3
+ import { runOnPaymentRequiredHooks, selectRequirementWithHooks, runBeforeSignPaymentHooks, runAfterPaymentResponseHooks } from '@armory-sh/base/client-hooks-runtime';
3
4
  import { ethers } from 'ethers';
4
5
 
5
6
  var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
@@ -26,8 +27,6 @@ var ProviderRequiredError = class extends X402ClientError {
26
27
  this.name = "ProviderRequiredError";
27
28
  }
28
29
  };
29
-
30
- // src/eip3009.ts
31
30
  var EIP712_TYPES_ETHERS = {
32
31
  TransferWithAuthorization: [
33
32
  { name: "from", type: "address" },
@@ -48,7 +47,11 @@ async function signEIP3009(signer, params, domain) {
48
47
  validBefore: BigInt(params.validBefore),
49
48
  nonce: params.nonce
50
49
  };
51
- const signature = await signer.signTypedData(domain, EIP712_TYPES_ETHERS, message);
50
+ const signature = await signer.signTypedData(
51
+ domain,
52
+ EIP712_TYPES_ETHERS,
53
+ message
54
+ );
52
55
  const { v, r, s } = ethers.Signature.from(signature);
53
56
  return { v: Number(v), r, s };
54
57
  } catch (error) {
@@ -60,7 +63,11 @@ async function signEIP3009(signer, params, domain) {
60
63
  }
61
64
  async function signEIP3009WithDomain(signer, params, chainId, verifyingContract, domainName, domainVersion) {
62
65
  const domain = createEIP712Domain(chainId, verifyingContract);
63
- const customDomain = domainName || domainVersion ? { ...domain, name: domainName ?? domain.name, version: domainVersion ?? domain.version } : domain;
66
+ const customDomain = domainName || domainVersion ? {
67
+ ...domain,
68
+ name: domainName ?? domain.name,
69
+ version: domainVersion ?? domain.version
70
+ } : domain;
64
71
  return signEIP3009(signer, params, customDomain);
65
72
  }
66
73
  async function signPayment(signer, from, to, value, chainId, verifyingContract, expirySeconds = 3600, domainName, domainVersion) {
@@ -68,7 +75,14 @@ async function signPayment(signer, from, to, value, chainId, verifyingContract,
68
75
  const nonce = `0x${(now * 1e3).toString(16).padStart(64, "0")}`;
69
76
  const signature = await signEIP3009WithDomain(
70
77
  signer,
71
- { from, to, value, validAfter: 0n, validBefore: BigInt(now + expirySeconds), nonce },
78
+ {
79
+ from,
80
+ to,
81
+ value,
82
+ validAfter: 0n,
83
+ validBefore: BigInt(now + expirySeconds),
84
+ nonce
85
+ },
72
86
  chainId,
73
87
  verifyingContract,
74
88
  domainName,
@@ -90,7 +104,12 @@ async function recoverEIP3009Signer(params, signature, domain) {
90
104
  r: signature.r,
91
105
  s: signature.s
92
106
  });
93
- const address = ethers.verifyTypedData(domain, EIP712_TYPES_ETHERS, message, sig);
107
+ const address = ethers.verifyTypedData(
108
+ domain,
109
+ EIP712_TYPES_ETHERS,
110
+ message,
111
+ sig
112
+ );
94
113
  return address;
95
114
  }
96
115
 
@@ -124,11 +143,13 @@ function parsePaymentRequired(response) {
124
143
  }
125
144
  return {
126
145
  version: 2,
127
- requirements: parsed.accepts[0]
146
+ accepts: parsed.accepts
128
147
  };
129
148
  } catch (error) {
130
149
  if (error instanceof PaymentException) throw error;
131
- throw new PaymentException(`Failed to parse V2 PAYMENT-REQUIRED header: ${error}`);
150
+ throw new PaymentException(
151
+ `Failed to parse V2 PAYMENT-REQUIRED header: ${error}`
152
+ );
132
153
  }
133
154
  }
134
155
  function extractChainId(network) {
@@ -150,7 +171,11 @@ async function createX402V2Payment(signer, requirements, fromAddress, nonce, val
150
171
  const chainId = extractChainId(requirements.network);
151
172
  const now = Math.floor(Date.now() / 1e3);
152
173
  const domain = createEIP712Domain(chainId, contractAddress);
153
- const customDomain = domainName || domainVersion ? { ...domain, name: domainName ?? domain.name, version: domainVersion ?? domain.version } : domain;
174
+ const customDomain = domainName || domainVersion ? {
175
+ ...domain,
176
+ name: domainName ?? domain.name,
177
+ version: domainVersion ?? domain.version
178
+ } : domain;
154
179
  const authorization = {
155
180
  from: fromAddress,
156
181
  to: requirements.payTo,
@@ -179,12 +204,12 @@ async function createX402V2Payment(signer, requirements, fromAddress, nonce, val
179
204
  payload
180
205
  };
181
206
  }
182
- async function createX402Payment(signer, parsed, fromAddress, nonce, validBefore, domainName, domainVersion) {
207
+ async function createX402Payment(signer, requirement, fromAddress, nonce, validBefore, domainName, domainVersion) {
183
208
  const effectiveNonce = nonce ?? createNonce();
184
209
  const effectiveValidBefore = validBefore ?? Math.floor(Date.now() / 1e3) + 3600;
185
210
  return createX402V2Payment(
186
211
  signer,
187
- parsed.requirements,
212
+ requirement,
188
213
  fromAddress,
189
214
  effectiveNonce,
190
215
  effectiveValidBefore,
@@ -192,6 +217,8 @@ async function createX402Payment(signer, parsed, fromAddress, nonce, validBefore
192
217
  domainVersion
193
218
  );
194
219
  }
220
+
221
+ // src/transport.ts
195
222
  var defaultConfig = {
196
223
  baseURL: "",
197
224
  headers: {},
@@ -203,7 +230,8 @@ var defaultConfig = {
203
230
  onPaymentSuccess: () => {
204
231
  },
205
232
  onPaymentError: () => {
206
- }
233
+ },
234
+ hooks: []
207
235
  };
208
236
  var createState = (config) => ({
209
237
  config: { ...defaultConfig, ...config }
@@ -224,11 +252,11 @@ var fetchWithTimeout = async (url, init, timeout) => {
224
252
  }
225
253
  };
226
254
  var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
227
- var getRequirementDomainOverrides = (parsed) => {
228
- const requirement = parsed.requirements;
255
+ var getRequirementDomainOverrides = (_parsed, requirement) => {
229
256
  const extra = requirement.extra;
230
- const extraName = extra && typeof extra === "object" && typeof extra["name"] === "string" ? extra["name"] : void 0;
231
- const extraVersion = extra && typeof extra === "object" && typeof extra["version"] === "string" ? extra["version"] : void 0;
257
+ const extraRecord = extra && typeof extra === "object" ? extra : void 0;
258
+ const extraName = extraRecord && typeof extraRecord.name === "string" ? extraRecord.name : void 0;
259
+ const extraVersion = extraRecord && typeof extraRecord.version === "string" ? extraRecord.version : void 0;
232
260
  return {
233
261
  domainName: requirement.name ?? extraName,
234
262
  domainVersion: requirement.version ?? extraVersion
@@ -236,21 +264,52 @@ var getRequirementDomainOverrides = (parsed) => {
236
264
  };
237
265
  var handlePaymentRequired = async (state, response) => {
238
266
  if (!state.signer) {
239
- throw new SignerRequiredError("Cannot handle payment: no signer configured.");
267
+ throw new SignerRequiredError(
268
+ "Cannot handle payment: no signer configured."
269
+ );
240
270
  }
241
271
  try {
242
272
  const parsed = parsePaymentRequired(response);
273
+ const initialRequirement = parsed.accepts[0];
274
+ if (!initialRequirement) {
275
+ throw new Error("No payment requirements found in accepts array");
276
+ }
243
277
  const from = await state.signer.getAddress();
244
- const requirementDomain = getRequirementDomainOverrides(parsed);
278
+ const paymentRequiredContext = {
279
+ url: response.url,
280
+ requestInit: void 0,
281
+ accepts: parsed.accepts,
282
+ requirements: initialRequirement,
283
+ selectedRequirement: initialRequirement,
284
+ serverExtensions: void 0,
285
+ fromAddress: from,
286
+ nonce: `0x${Date.now().toString(16).padStart(64, "0")}`,
287
+ validBefore: Math.floor(Date.now() / 1e3) + initialRequirement.maxTimeoutSeconds
288
+ };
289
+ await runOnPaymentRequiredHooks(state.config.hooks, paymentRequiredContext);
290
+ const selectedRequirement = await selectRequirementWithHooks(
291
+ state.config.hooks,
292
+ paymentRequiredContext
293
+ );
294
+ const requirementDomain = getRequirementDomainOverrides(
295
+ parsed,
296
+ selectedRequirement
297
+ );
245
298
  const payload = await createX402Payment(
246
299
  state.signer,
247
- parsed,
300
+ selectedRequirement,
248
301
  from,
249
- void 0,
250
- Math.floor(Date.now() / 1e3) + parsed.requirements.maxTimeoutSeconds,
302
+ paymentRequiredContext.nonce,
303
+ paymentRequiredContext.validBefore,
251
304
  requirementDomain.domainName,
252
305
  requirementDomain.domainVersion
253
306
  );
307
+ await runBeforeSignPaymentHooks(state.config.hooks, {
308
+ payload,
309
+ requirements: selectedRequirement,
310
+ wallet: state.signer,
311
+ paymentContext: paymentRequiredContext
312
+ });
254
313
  const encoded = encodePaymentV2(payload);
255
314
  const headerName = getPaymentHeaderName(parsed.version);
256
315
  const paymentResponse = await fetchWithTimeout(
@@ -261,30 +320,48 @@ var handlePaymentRequired = async (state, response) => {
261
320
  },
262
321
  state.config.timeout
263
322
  );
264
- const settlement = decodeSettlementV2(paymentResponse.headers.get(V2_HEADERS.PAYMENT_RESPONSE) || "");
323
+ const settlement = decodeSettlementV2(
324
+ paymentResponse.headers.get(V2_HEADERS.PAYMENT_RESPONSE) || ""
325
+ );
326
+ await runAfterPaymentResponseHooks(state.config.hooks, {
327
+ payload,
328
+ requirements: selectedRequirement,
329
+ wallet: state.signer,
330
+ paymentContext: paymentRequiredContext,
331
+ response: paymentResponse
332
+ });
265
333
  return { success: true, settlement };
266
334
  } catch (error) {
267
- return { success: false, error: error instanceof Error ? error : new Error(String(error)) };
335
+ return {
336
+ success: false,
337
+ error: error instanceof Error ? error : new Error(String(error))
338
+ };
268
339
  }
269
340
  };
270
341
  var shouldRetryPayment = async (state, response) => {
271
342
  if (!state.config.autoPay) return false;
272
343
  const parsed = parsePaymentRequired(response);
273
- return await state.config.onPaymentRequired(parsed.requirements);
344
+ return await state.config.onPaymentRequired(parsed.accepts[0]);
274
345
  };
275
346
  var x402Fetch = async (state, url, init = {}) => {
276
347
  const fullUrl = state.config.baseURL ? new URL(url, state.config.baseURL).toString() : url;
277
348
  const headers = new Headers({ ...state.config.headers, ...init.headers });
278
349
  for (let attempt = 1; attempt <= state.config.maxRetries; attempt++) {
279
350
  try {
280
- const response = await fetchWithTimeout(fullUrl, { ...init, headers }, state.config.timeout);
351
+ const response = await fetchWithTimeout(
352
+ fullUrl,
353
+ { ...init, headers },
354
+ state.config.timeout
355
+ );
281
356
  if (response.status === 402 && !init.skipAutoPay && await shouldRetryPayment(state, response)) {
282
357
  const paymentResult = await handlePaymentRequired(state, response);
283
358
  if (paymentResult.success) {
284
359
  state.config.onPaymentSuccess(paymentResult.settlement);
285
360
  continue;
286
361
  }
287
- state.config.onPaymentError(paymentResult.error ?? new Error("Payment failed"));
362
+ state.config.onPaymentError(
363
+ paymentResult.error ?? new Error("Payment failed")
364
+ );
288
365
  }
289
366
  return response;
290
367
  } catch (error) {
@@ -307,7 +384,7 @@ var createMethod = (state, method) => async (url, bodyOrInit, init) => {
307
384
  });
308
385
  };
309
386
  var createX402Transport = (config) => {
310
- let state = createState(config);
387
+ const state = createState(config);
311
388
  return {
312
389
  fetch: (url, init) => x402Fetch(state, url, init),
313
390
  get: createMethod(state, "GET"),
@@ -322,37 +399,7 @@ var createX402Transport = (config) => {
322
399
  };
323
400
  };
324
401
 
325
- // src/client.ts
326
- function createX402Client(config) {
327
- const transport = createX402Transport(void 0);
328
- if ("signer" in config && config.signer) {
329
- transport.setSigner(config.signer);
330
- } else if ("provider" in config && config.provider) {
331
- const provider = config.provider;
332
- if (provider.getSigner) {
333
- try {
334
- const signer = provider.getSigner();
335
- if (signer) {
336
- transport.setSigner(signer);
337
- }
338
- } catch {
339
- }
340
- }
341
- } else {
342
- throw new SignerRequiredError(
343
- "Either 'signer' or 'provider' with getSigner() must be provided"
344
- );
345
- }
346
- return {
347
- get: (url, init) => transport.get(url, init),
348
- post: (url, body, init) => transport.post(url, body, init),
349
- put: (url, body, init) => transport.put(url, body, init),
350
- del: (url, init) => transport.del(url, init),
351
- patch: (url, body, init) => transport.patch(url, body, init),
352
- setSigner: (signer) => transport.setSigner(signer),
353
- getSigner: () => transport.getSigner()
354
- };
355
- }
402
+ // src/payment-api.ts
356
403
  var normalizeWallet = (wallet) => {
357
404
  if (typeof wallet === "object" && wallet !== null && "signer" in wallet) {
358
405
  return wallet.signer;
@@ -414,19 +461,37 @@ var armoryPay = async (wallet, url, network, token, options) => {
414
461
  }
415
462
  };
416
463
  var armoryGet = (wallet, url, network, token, options) => {
417
- return armoryPay(wallet, url, network, token, { ...options, method: "GET" });
464
+ return armoryPay(wallet, url, network, token, {
465
+ ...options,
466
+ method: "GET"
467
+ });
418
468
  };
419
469
  var armoryPost = (wallet, url, network, token, body, options) => {
420
- return armoryPay(wallet, url, network, token, { ...options, method: "POST", body });
470
+ return armoryPay(wallet, url, network, token, {
471
+ ...options,
472
+ method: "POST",
473
+ body
474
+ });
421
475
  };
422
476
  var armoryPut = (wallet, url, network, token, body, options) => {
423
- return armoryPay(wallet, url, network, token, { ...options, method: "PUT", body });
477
+ return armoryPay(wallet, url, network, token, {
478
+ ...options,
479
+ method: "PUT",
480
+ body
481
+ });
424
482
  };
425
483
  var armoryDelete = (wallet, url, network, token, options) => {
426
- return armoryPay(wallet, url, network, token, { ...options, method: "DELETE" });
484
+ return armoryPay(wallet, url, network, token, {
485
+ ...options,
486
+ method: "DELETE"
487
+ });
427
488
  };
428
489
  var armoryPatch = (wallet, url, network, token, body, options) => {
429
- return armoryPay(wallet, url, network, token, { ...options, method: "PATCH", body });
490
+ return armoryPay(wallet, url, network, token, {
491
+ ...options,
492
+ method: "PATCH",
493
+ body
494
+ });
430
495
  };
431
496
  var getWalletAddress = async (wallet) => {
432
497
  const signer = normalizeWallet(wallet);
@@ -440,7 +505,7 @@ var validateNetwork = (network) => {
440
505
  return { success: true, network: resolved.config.name };
441
506
  };
442
507
  var validateToken = (token, network) => {
443
- let resolvedNetwork = void 0;
508
+ let resolvedNetwork;
444
509
  if (network) {
445
510
  const networkResult = resolveNetwork(network);
446
511
  if (isValidationError(networkResult)) {
@@ -466,7 +531,15 @@ var getTokens = () => {
466
531
  const { getAvailableTokens } = __require("@armory-sh/base");
467
532
  return getAvailableTokens();
468
533
  };
469
- var ALL_METHODS = /* @__PURE__ */ new Set(["GET", "POST", "PUT", "DELETE", "PATCH"]);
534
+
535
+ // src/armory-api.ts
536
+ var ALL_METHODS = /* @__PURE__ */ new Set([
537
+ "GET",
538
+ "POST",
539
+ "PUT",
540
+ "DELETE",
541
+ "PATCH"
542
+ ]);
470
543
  var arrayify = (value) => {
471
544
  if (value === void 0) return void 0;
472
545
  return Array.isArray(value) ? value : [value];
@@ -534,4 +607,38 @@ var createArmory = (config) => {
534
607
  };
535
608
  };
536
609
 
610
+ // src/client.ts
611
+ function createX402Client(config) {
612
+ const transport = createX402Transport({
613
+ hooks: config.hooks
614
+ });
615
+ if ("signer" in config && config.signer) {
616
+ transport.setSigner(config.signer);
617
+ } else if ("provider" in config && config.provider) {
618
+ const provider = config.provider;
619
+ if (provider.getSigner) {
620
+ try {
621
+ const signer = provider.getSigner();
622
+ if (signer) {
623
+ transport.setSigner(signer);
624
+ }
625
+ } catch {
626
+ }
627
+ }
628
+ } else {
629
+ throw new SignerRequiredError(
630
+ "Either 'signer' or 'provider' with getSigner() must be provided"
631
+ );
632
+ }
633
+ return {
634
+ get: (url, init) => transport.get(url, init),
635
+ post: (url, body, init) => transport.post(url, body, init),
636
+ put: (url, body, init) => transport.put(url, body, init),
637
+ del: (url, init) => transport.del(url, init),
638
+ patch: (url, body, init) => transport.patch(url, body, init),
639
+ setSigner: (signer) => transport.setSigner(signer),
640
+ getSigner: () => transport.getSigner()
641
+ };
642
+ }
643
+
537
644
  export { AuthorizationError, ProviderRequiredError, SignerRequiredError, armoryDelete, armoryGet, armoryPatch, armoryPay, armoryPost, armoryPut, createArmory, createX402Client, createX402Payment, createX402Transport, detectX402Version, getNetworks, getPaymentHeaderName, getTokens, getWalletAddress, normalizeWallet, parsePaymentRequired, recoverEIP3009Signer, signEIP3009, signEIP3009WithDomain, signPayment, validateNetwork, validateToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@armory-sh/client-ethers",
3
- "version": "0.2.26-alpha.23.76",
3
+ "version": "0.2.26-alpha.23.78",
4
4
  "license": "MIT",
5
5
  "author": "Sawyer Cutler <sawyer@dirtroad.dev>",
6
6
  "keywords": [
@@ -47,7 +47,7 @@
47
47
  "directory": "packages/client-ethers"
48
48
  },
49
49
  "dependencies": {
50
- "@armory-sh/base": "0.2.27-alpha.23.76",
50
+ "@armory-sh/base": "0.2.27-alpha.23.78",
51
51
  "ethers": "6.16.0"
52
52
  },
53
53
  "devDependencies": {
@@ -56,6 +56,8 @@
56
56
  },
57
57
  "scripts": {
58
58
  "build": "rm -rf dist && tsup",
59
+ "lint": "bun run build",
60
+ "format": "bun run lint",
59
61
  "test": "bun test",
60
62
  "example": "bun run examples/"
61
63
  }