@armory-sh/client-ethers 0.2.27 → 0.2.28
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/README.md +22 -0
- package/dist/index.d.ts +151 -162
- package/dist/index.js +250 -156
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Armory x402 SDK — Payment client for ethers.js v6. Make payments from any ethe
|
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
bun add @armory-sh/client-ethers
|
|
11
|
+
bun add @armory-sh/client-hooks # optional preference/logger hooks
|
|
11
12
|
```
|
|
12
13
|
|
|
13
14
|
## Why Armory?
|
|
@@ -60,9 +61,30 @@ const response = await client.fetch('https://api.example.com/protected')
|
|
|
60
61
|
const data = await response.json()
|
|
61
62
|
```
|
|
62
63
|
|
|
64
|
+
## Hook Pipeline
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
import { createX402Client } from '@armory-sh/client-ethers'
|
|
68
|
+
import { PaymentPreference, Logger } from '@armory-sh/client-hooks'
|
|
69
|
+
|
|
70
|
+
const client = createX402Client({
|
|
71
|
+
signer,
|
|
72
|
+
hooks: [
|
|
73
|
+
PaymentPreference.chain(['base', 'polygon', 'skale']),
|
|
74
|
+
PaymentPreference.token(['USDT', 'USDC', 'WBTC']),
|
|
75
|
+
PaymentPreference.cheapest(),
|
|
76
|
+
Logger.console(),
|
|
77
|
+
],
|
|
78
|
+
})
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
`parsePaymentRequired` returns `accepts[]` (x402 v2 challenge options). Clients select from this list.
|
|
82
|
+
`hooks` are lifecycle callbacks. `extensions` are protocol payload fields. Hooks can drive selection and payload behavior, but they are not extensions.
|
|
83
|
+
|
|
63
84
|
## Features
|
|
64
85
|
|
|
65
86
|
- **Auto 402 Handling**: Automatically intercepts and pays for 402 responses
|
|
87
|
+
- **Detailed Verification Errors**: 402 retry failures include server details (for example `insufficient_funds`)
|
|
66
88
|
- **EIP-3009 Signing**: Full support for EIP-3009 TransferWithAuthorization
|
|
67
89
|
- **Multi-Network**: Ethereum, Base, SKALE support
|
|
68
90
|
- **Multi-Token**: USDC, EURC, USDT, WBTC, WETH, SKL
|
package/dist/index.d.ts
CHANGED
|
@@ -1,52 +1,121 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
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';
|
|
3
3
|
import { Signer, Provider } from 'ethers';
|
|
4
4
|
export { Provider, Signer } from 'ethers';
|
|
5
|
+
import { ClientHook } from '@armory-sh/base/types/hooks';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
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
|
-
*
|
|
15
|
-
* V2-only: Always returns 2
|
|
13
|
+
* Simple wallet input - accepts wallet directly or wrapped for backward compatibility
|
|
16
14
|
*/
|
|
17
|
-
|
|
15
|
+
type SimpleWalletInput = Signer | {
|
|
16
|
+
signer: Signer;
|
|
17
|
+
};
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
19
|
+
* Normalized wallet (internal use)
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
interface ParsedPaymentRequirements {
|
|
23
|
-
version: 2;
|
|
24
|
-
requirements: PaymentRequirementsV2;
|
|
25
|
-
}
|
|
21
|
+
type NormalizedWallet = Signer;
|
|
26
22
|
/**
|
|
27
|
-
*
|
|
28
|
-
* V2 only
|
|
23
|
+
* Normalize wallet input to internal format
|
|
29
24
|
*/
|
|
30
|
-
declare
|
|
25
|
+
declare const normalizeWallet: (wallet: SimpleWalletInput) => NormalizedWallet;
|
|
31
26
|
/**
|
|
32
|
-
*
|
|
27
|
+
* Make a payment-protected API request with one line of code
|
|
28
|
+
*/
|
|
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
|
|
33
45
|
*/
|
|
34
|
-
declare
|
|
46
|
+
declare const armoryGet: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
|
|
35
47
|
/**
|
|
36
|
-
*
|
|
48
|
+
* Make a POST request with payment
|
|
37
49
|
*/
|
|
38
|
-
declare
|
|
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[];
|
|
39
90
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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;
|
|
43
103
|
}
|
|
44
|
-
|
|
45
|
-
|
|
104
|
+
interface PaymentOptions {
|
|
105
|
+
method?: HttpMethod;
|
|
106
|
+
body?: unknown;
|
|
46
107
|
}
|
|
47
|
-
|
|
48
|
-
|
|
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>>;
|
|
49
116
|
}
|
|
117
|
+
declare const createArmory: (config: ArmoryConfig) => ArmoryInstance;
|
|
118
|
+
|
|
50
119
|
declare class SignerRequiredError extends X402ClientError {
|
|
51
120
|
constructor(message?: string);
|
|
52
121
|
}
|
|
@@ -72,6 +141,7 @@ interface X402ClientConfig {
|
|
|
72
141
|
domainName?: string;
|
|
73
142
|
/** Override EIP-712 domain version for custom tokens */
|
|
74
143
|
domainVersion?: string;
|
|
144
|
+
hooks?: ClientHook<Signer>[];
|
|
75
145
|
}
|
|
76
146
|
interface SignerClientConfig extends X402ClientConfig {
|
|
77
147
|
signer: Signer;
|
|
@@ -91,6 +161,7 @@ interface X402TransportConfig {
|
|
|
91
161
|
onPaymentRequired?: (requirements: unknown) => boolean | Promise<boolean>;
|
|
92
162
|
onPaymentSuccess?: (settlement: unknown) => void;
|
|
93
163
|
onPaymentError?: (error: Error) => void;
|
|
164
|
+
hooks?: ClientHook<Signer>[];
|
|
94
165
|
}
|
|
95
166
|
interface X402RequestInit extends Omit<RequestInit, "headers"> {
|
|
96
167
|
headers?: Record<string, string> | Headers;
|
|
@@ -112,40 +183,6 @@ interface EIP712Domain {
|
|
|
112
183
|
verifyingContract: `0x${string}`;
|
|
113
184
|
}
|
|
114
185
|
|
|
115
|
-
interface X402Transport {
|
|
116
|
-
fetch(url: string, init?: X402RequestInit): Promise<Response>;
|
|
117
|
-
get(url: string, init?: X402RequestInit): Promise<Response>;
|
|
118
|
-
post(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
|
|
119
|
-
put(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
|
|
120
|
-
del(url: string, init?: X402RequestInit): Promise<Response>;
|
|
121
|
-
patch(url: string, body?: unknown, init?: X402RequestInit): Promise<Response>;
|
|
122
|
-
setSigner(signer: Signer): void;
|
|
123
|
-
getSigner(): Signer | undefined;
|
|
124
|
-
}
|
|
125
|
-
declare const createX402Transport: (config?: X402TransportConfig) => X402Transport;
|
|
126
|
-
|
|
127
|
-
declare function signEIP3009(signer: Signer, params: TransferWithAuthorizationParams, domain: EIP712Domain): Promise<{
|
|
128
|
-
v: number;
|
|
129
|
-
r: string;
|
|
130
|
-
s: string;
|
|
131
|
-
}>;
|
|
132
|
-
declare function signEIP3009WithDomain(signer: Signer, params: TransferWithAuthorizationParams, chainId: number, verifyingContract: `0x${string}`, domainName?: string, domainVersion?: string): Promise<{
|
|
133
|
-
v: number;
|
|
134
|
-
r: string;
|
|
135
|
-
s: string;
|
|
136
|
-
}>;
|
|
137
|
-
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<{
|
|
138
|
-
v: number;
|
|
139
|
-
r: string;
|
|
140
|
-
s: string;
|
|
141
|
-
nonce: `0x${string}`;
|
|
142
|
-
}>;
|
|
143
|
-
declare function recoverEIP3009Signer(params: TransferWithAuthorizationParams, signature: {
|
|
144
|
-
v: number;
|
|
145
|
-
r: string;
|
|
146
|
-
s: string;
|
|
147
|
-
}, domain: EIP712Domain): Promise<`0x${string}`>;
|
|
148
|
-
|
|
149
186
|
/**
|
|
150
187
|
* X402 Client Factory for Ethers
|
|
151
188
|
*
|
|
@@ -198,116 +235,68 @@ declare function createX402Client(config: X402ClientConfig & SignerClientConfig
|
|
|
198
235
|
}): X402Client;
|
|
199
236
|
declare function createX402Client(config: X402ClientConfig & ProviderClientConfig): X402Client;
|
|
200
237
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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}`>;
|
|
205
259
|
|
|
206
260
|
/**
|
|
207
|
-
*
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
};
|
|
212
|
-
/**
|
|
213
|
-
* Normalized wallet (internal use)
|
|
214
|
-
*/
|
|
215
|
-
type NormalizedWallet = Signer;
|
|
216
|
-
/**
|
|
217
|
-
* Normalize wallet input to internal format
|
|
218
|
-
*/
|
|
219
|
-
declare const normalizeWallet: (wallet: SimpleWalletInput) => NormalizedWallet;
|
|
220
|
-
/**
|
|
221
|
-
* Make a payment-protected API request with one line of code
|
|
222
|
-
*/
|
|
223
|
-
declare const armoryPay: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: {
|
|
224
|
-
/** Request method (default: GET) */
|
|
225
|
-
method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
|
|
226
|
-
/** Request body (for POST/PUT/PATCH) */
|
|
227
|
-
body?: unknown;
|
|
228
|
-
/** Request headers */
|
|
229
|
-
headers?: Record<string, string>;
|
|
230
|
-
/** Protocol version (V2 only) */
|
|
231
|
-
version?: 2;
|
|
232
|
-
/** Payment amount in token units (default: from 402 header) */
|
|
233
|
-
amount?: string;
|
|
234
|
-
/** Enable debug logging */
|
|
235
|
-
debug?: boolean;
|
|
236
|
-
}) => Promise<ArmoryPaymentResult<T>>;
|
|
237
|
-
/**
|
|
238
|
-
* Make a GET request with payment
|
|
239
|
-
*/
|
|
240
|
-
declare const armoryGet: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
|
|
241
|
-
/**
|
|
242
|
-
* Make a POST request with payment
|
|
243
|
-
*/
|
|
244
|
-
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>>;
|
|
245
|
-
/**
|
|
246
|
-
* Make a PUT request with payment
|
|
247
|
-
*/
|
|
248
|
-
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>>;
|
|
249
|
-
/**
|
|
250
|
-
* Make a DELETE request with payment
|
|
251
|
-
*/
|
|
252
|
-
declare const armoryDelete: <T = unknown>(wallet: SimpleWalletInput, url: string, network: NetworkId, token: TokenId, options?: Omit<Parameters<typeof armoryPay>[4], "method">) => Promise<ArmoryPaymentResult<T>>;
|
|
253
|
-
/**
|
|
254
|
-
* Make a PATCH request with payment
|
|
255
|
-
*/
|
|
256
|
-
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>>;
|
|
257
|
-
/**
|
|
258
|
-
* Get the wallet address from a SimpleWalletInput
|
|
259
|
-
*/
|
|
260
|
-
declare const getWalletAddress: (wallet: SimpleWalletInput) => Promise<string>;
|
|
261
|
-
/**
|
|
262
|
-
* 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
|
|
263
265
|
*/
|
|
264
|
-
|
|
265
|
-
success: true;
|
|
266
|
-
network: string;
|
|
267
|
-
};
|
|
266
|
+
|
|
268
267
|
/**
|
|
269
|
-
*
|
|
268
|
+
* Detect x402 protocol version from response headers
|
|
269
|
+
* V2-only: Always returns 2
|
|
270
270
|
*/
|
|
271
|
-
declare
|
|
272
|
-
success: true;
|
|
273
|
-
token: string;
|
|
274
|
-
network: string;
|
|
275
|
-
};
|
|
271
|
+
declare function detectX402Version(_response: Response): 2;
|
|
276
272
|
/**
|
|
277
|
-
* Get
|
|
273
|
+
* Get payment header name for protocol version
|
|
278
274
|
*/
|
|
279
|
-
declare
|
|
275
|
+
declare function getPaymentHeaderName(_version: 2): string;
|
|
276
|
+
interface ParsedPaymentRequirements {
|
|
277
|
+
version: 2;
|
|
278
|
+
accepts: PaymentRequirementsV2[];
|
|
279
|
+
}
|
|
280
280
|
/**
|
|
281
|
-
*
|
|
281
|
+
* Parse x402 PAYMENT-REQUIRED header from response
|
|
282
|
+
* V2 only
|
|
282
283
|
*/
|
|
283
|
-
declare
|
|
284
|
-
|
|
284
|
+
declare function parsePaymentRequired(response: Response): ParsedPaymentRequirements;
|
|
285
285
|
/**
|
|
286
|
-
*
|
|
287
|
-
* Provides a configurable object with method-based payment functions
|
|
286
|
+
* Create x402 payment payload (V2-only wrapper)
|
|
288
287
|
*/
|
|
288
|
+
declare function createX402Payment(signer: Signer, requirement: PaymentRequirementsV2, fromAddress: Address, nonce?: `0x${string}`, validBefore?: number, domainName?: string, domainVersion?: string): Promise<PaymentPayloadV2>;
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
method?: HttpMethod;
|
|
300
|
-
body?: unknown;
|
|
301
|
-
}
|
|
302
|
-
interface ArmoryInstance {
|
|
303
|
-
get<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
|
|
304
|
-
post<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
|
|
305
|
-
put<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
|
|
306
|
-
delete<T>(url: string): Promise<ArmoryPaymentResult<T>>;
|
|
307
|
-
patch<T>(url: string, body?: unknown): Promise<ArmoryPaymentResult<T>>;
|
|
308
|
-
pay<T>(url: string, options?: PaymentOptions): Promise<ArmoryPaymentResult<T>>;
|
|
309
|
-
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;
|
|
310
299
|
}
|
|
311
|
-
declare const
|
|
300
|
+
declare const createX402Transport: (config?: X402TransportConfig) => X402Transport;
|
|
312
301
|
|
|
313
|
-
export { type ArmoryConfig, type ArmoryInstance, AuthorizationError, type ClientConfig, type HttpMethod, type NormalizedWallet, type ParsedPaymentRequirements,
|
|
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
|
-
import { createEIP712Domain, V2_HEADERS, isX402V2PaymentRequired, validatePaymentConfig, isValidationError, resolveNetwork, resolveToken, getNetworkConfig, normalizeNetworkName, decodeSettlementV2 } from '@armory-sh/base';
|
|
2
|
-
export {
|
|
1
|
+
import { X402ClientError, createEIP712Domain, V2_HEADERS, PaymentException, isX402V2PaymentRequired, validatePaymentConfig, isValidationError, resolveNetwork, resolveToken, normalizeBase64Url, decodeBase64ToUtf8, getNetworkConfig, normalizeNetworkName, encodePaymentV2, decodeSettlementV2 } from '@armory-sh/base';
|
|
2
|
+
export { PaymentException as PaymentError, SigningError, X402ClientError } from '@armory-sh/base';
|
|
3
|
+
import { runOnPaymentRequiredHooks, getRequirementAttemptOrderWithHooks, 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, {
|
|
@@ -8,28 +9,6 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
|
|
|
8
9
|
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
9
10
|
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
10
11
|
});
|
|
11
|
-
|
|
12
|
-
// src/errors.ts
|
|
13
|
-
var X402ClientError = class extends Error {
|
|
14
|
-
cause;
|
|
15
|
-
constructor(message, cause) {
|
|
16
|
-
super(message);
|
|
17
|
-
this.name = "X402ClientError";
|
|
18
|
-
this.cause = cause;
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
var SigningError = class extends X402ClientError {
|
|
22
|
-
constructor(message, cause) {
|
|
23
|
-
super(`Signing failed: ${message}`, cause);
|
|
24
|
-
this.name = "SigningError";
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
var PaymentError = class extends X402ClientError {
|
|
28
|
-
constructor(message, cause) {
|
|
29
|
-
super(`Payment failed: ${message}`, cause);
|
|
30
|
-
this.name = "PaymentError";
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
12
|
var SignerRequiredError = class extends X402ClientError {
|
|
34
13
|
constructor(message = "Signer is required for this operation") {
|
|
35
14
|
super(message);
|
|
@@ -48,8 +27,6 @@ var ProviderRequiredError = class extends X402ClientError {
|
|
|
48
27
|
this.name = "ProviderRequiredError";
|
|
49
28
|
}
|
|
50
29
|
};
|
|
51
|
-
|
|
52
|
-
// src/eip3009.ts
|
|
53
30
|
var EIP712_TYPES_ETHERS = {
|
|
54
31
|
TransferWithAuthorization: [
|
|
55
32
|
{ name: "from", type: "address" },
|
|
@@ -70,7 +47,11 @@ async function signEIP3009(signer, params, domain) {
|
|
|
70
47
|
validBefore: BigInt(params.validBefore),
|
|
71
48
|
nonce: params.nonce
|
|
72
49
|
};
|
|
73
|
-
const signature = await signer.signTypedData(
|
|
50
|
+
const signature = await signer.signTypedData(
|
|
51
|
+
domain,
|
|
52
|
+
EIP712_TYPES_ETHERS,
|
|
53
|
+
message
|
|
54
|
+
);
|
|
74
55
|
const { v, r, s } = ethers.Signature.from(signature);
|
|
75
56
|
return { v: Number(v), r, s };
|
|
76
57
|
} catch (error) {
|
|
@@ -82,7 +63,11 @@ async function signEIP3009(signer, params, domain) {
|
|
|
82
63
|
}
|
|
83
64
|
async function signEIP3009WithDomain(signer, params, chainId, verifyingContract, domainName, domainVersion) {
|
|
84
65
|
const domain = createEIP712Domain(chainId, verifyingContract);
|
|
85
|
-
const customDomain = domainName || domainVersion ? {
|
|
66
|
+
const customDomain = domainName || domainVersion ? {
|
|
67
|
+
...domain,
|
|
68
|
+
name: domainName ?? domain.name,
|
|
69
|
+
version: domainVersion ?? domain.version
|
|
70
|
+
} : domain;
|
|
86
71
|
return signEIP3009(signer, params, customDomain);
|
|
87
72
|
}
|
|
88
73
|
async function signPayment(signer, from, to, value, chainId, verifyingContract, expirySeconds = 3600, domainName, domainVersion) {
|
|
@@ -90,7 +75,14 @@ async function signPayment(signer, from, to, value, chainId, verifyingContract,
|
|
|
90
75
|
const nonce = `0x${(now * 1e3).toString(16).padStart(64, "0")}`;
|
|
91
76
|
const signature = await signEIP3009WithDomain(
|
|
92
77
|
signer,
|
|
93
|
-
{
|
|
78
|
+
{
|
|
79
|
+
from,
|
|
80
|
+
to,
|
|
81
|
+
value,
|
|
82
|
+
validAfter: 0n,
|
|
83
|
+
validBefore: BigInt(now + expirySeconds),
|
|
84
|
+
nonce
|
|
85
|
+
},
|
|
94
86
|
chainId,
|
|
95
87
|
verifyingContract,
|
|
96
88
|
domainName,
|
|
@@ -112,60 +104,15 @@ async function recoverEIP3009Signer(params, signature, domain) {
|
|
|
112
104
|
r: signature.r,
|
|
113
105
|
s: signature.s
|
|
114
106
|
});
|
|
115
|
-
const address = ethers.verifyTypedData(
|
|
107
|
+
const address = ethers.verifyTypedData(
|
|
108
|
+
domain,
|
|
109
|
+
EIP712_TYPES_ETHERS,
|
|
110
|
+
message,
|
|
111
|
+
sig
|
|
112
|
+
);
|
|
116
113
|
return address;
|
|
117
114
|
}
|
|
118
115
|
|
|
119
|
-
// src/bytes.ts
|
|
120
|
-
var textEncoder = new TextEncoder();
|
|
121
|
-
var textDecoder = new TextDecoder();
|
|
122
|
-
function getNodeBuffer() {
|
|
123
|
-
if ("Buffer" in globalThis) {
|
|
124
|
-
return globalThis.Buffer;
|
|
125
|
-
}
|
|
126
|
-
return void 0;
|
|
127
|
-
}
|
|
128
|
-
function toBase64(bytes) {
|
|
129
|
-
if (typeof btoa === "function") {
|
|
130
|
-
let binary = "";
|
|
131
|
-
for (let index = 0; index < bytes.length; index += 1) {
|
|
132
|
-
binary += String.fromCharCode(bytes[index]);
|
|
133
|
-
}
|
|
134
|
-
return btoa(binary);
|
|
135
|
-
}
|
|
136
|
-
const nodeBuffer = getNodeBuffer();
|
|
137
|
-
if (nodeBuffer) {
|
|
138
|
-
return nodeBuffer.from(bytes).toString("base64");
|
|
139
|
-
}
|
|
140
|
-
throw new Error("No base64 encoder available in this runtime");
|
|
141
|
-
}
|
|
142
|
-
function fromBase64(base64) {
|
|
143
|
-
if (typeof atob === "function") {
|
|
144
|
-
const binary = atob(base64);
|
|
145
|
-
const bytes = new Uint8Array(binary.length);
|
|
146
|
-
for (let index = 0; index < binary.length; index += 1) {
|
|
147
|
-
bytes[index] = binary.charCodeAt(index);
|
|
148
|
-
}
|
|
149
|
-
return bytes;
|
|
150
|
-
}
|
|
151
|
-
const nodeBuffer = getNodeBuffer();
|
|
152
|
-
if (nodeBuffer) {
|
|
153
|
-
return Uint8Array.from(nodeBuffer.from(base64, "base64"));
|
|
154
|
-
}
|
|
155
|
-
throw new Error("No base64 decoder available in this runtime");
|
|
156
|
-
}
|
|
157
|
-
function encodeUtf8ToBase64(value) {
|
|
158
|
-
const bytes = textEncoder.encode(value);
|
|
159
|
-
return toBase64(bytes);
|
|
160
|
-
}
|
|
161
|
-
function decodeBase64ToUtf8(value) {
|
|
162
|
-
const bytes = fromBase64(value);
|
|
163
|
-
return textDecoder.decode(bytes);
|
|
164
|
-
}
|
|
165
|
-
function normalizeBase64Url(value) {
|
|
166
|
-
return value.replace(/-/g, "+").replace(/_/g, "/").padEnd(Math.ceil(value.length / 4) * 4, "=");
|
|
167
|
-
}
|
|
168
|
-
|
|
169
116
|
// src/protocol.ts
|
|
170
117
|
function detectX402Version(_response) {
|
|
171
118
|
return 2;
|
|
@@ -184,28 +131,27 @@ function parseJsonOrBase64(value) {
|
|
|
184
131
|
function parsePaymentRequired(response) {
|
|
185
132
|
const v2Header = response.headers.get(V2_HEADERS.PAYMENT_REQUIRED);
|
|
186
133
|
if (!v2Header) {
|
|
187
|
-
throw new
|
|
134
|
+
throw new PaymentException("No PAYMENT-REQUIRED header found in V2 response");
|
|
188
135
|
}
|
|
189
136
|
try {
|
|
190
137
|
const parsed = parseJsonOrBase64(v2Header);
|
|
191
138
|
if (!isX402V2PaymentRequired(parsed)) {
|
|
192
|
-
throw new
|
|
139
|
+
throw new PaymentException("Invalid x402 V2 payment required format");
|
|
193
140
|
}
|
|
194
141
|
if (!parsed.accepts || parsed.accepts.length === 0) {
|
|
195
|
-
throw new
|
|
142
|
+
throw new PaymentException("No payment requirements found in accepts array");
|
|
196
143
|
}
|
|
197
144
|
return {
|
|
198
145
|
version: 2,
|
|
199
|
-
|
|
146
|
+
accepts: parsed.accepts
|
|
200
147
|
};
|
|
201
148
|
} catch (error) {
|
|
202
|
-
if (error instanceof
|
|
203
|
-
throw new
|
|
149
|
+
if (error instanceof PaymentException) throw error;
|
|
150
|
+
throw new PaymentException(
|
|
151
|
+
`Failed to parse V2 PAYMENT-REQUIRED header: ${error}`
|
|
152
|
+
);
|
|
204
153
|
}
|
|
205
154
|
}
|
|
206
|
-
function toAtomicUnits(amount) {
|
|
207
|
-
return Math.floor(parseFloat(amount) * 1e6).toString();
|
|
208
|
-
}
|
|
209
155
|
function extractChainId(network) {
|
|
210
156
|
if (network.startsWith("eip155:")) {
|
|
211
157
|
return parseInt(network.split(":")[1], 10);
|
|
@@ -214,7 +160,7 @@ function extractChainId(network) {
|
|
|
214
160
|
if (net) {
|
|
215
161
|
return net.chainId;
|
|
216
162
|
}
|
|
217
|
-
throw new
|
|
163
|
+
throw new PaymentException(`Unsupported network: ${network}`);
|
|
218
164
|
}
|
|
219
165
|
function createNonce() {
|
|
220
166
|
const now = Math.floor(Date.now() / 1e3);
|
|
@@ -223,13 +169,18 @@ function createNonce() {
|
|
|
223
169
|
async function createX402V2Payment(signer, requirements, fromAddress, nonce, validBefore, domainName, domainVersion) {
|
|
224
170
|
const contractAddress = requirements.asset;
|
|
225
171
|
const chainId = extractChainId(requirements.network);
|
|
172
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
226
173
|
const domain = createEIP712Domain(chainId, contractAddress);
|
|
227
|
-
const customDomain = domainName || domainVersion ? {
|
|
174
|
+
const customDomain = domainName || domainVersion ? {
|
|
175
|
+
...domain,
|
|
176
|
+
name: domainName ?? domain.name,
|
|
177
|
+
version: domainVersion ?? domain.version
|
|
178
|
+
} : domain;
|
|
228
179
|
const authorization = {
|
|
229
180
|
from: fromAddress,
|
|
230
181
|
to: requirements.payTo,
|
|
231
|
-
value:
|
|
232
|
-
validAfter:
|
|
182
|
+
value: requirements.amount,
|
|
183
|
+
validAfter: (now - 600).toString(),
|
|
233
184
|
validBefore: validBefore.toString(),
|
|
234
185
|
nonce
|
|
235
186
|
};
|
|
@@ -253,12 +204,12 @@ async function createX402V2Payment(signer, requirements, fromAddress, nonce, val
|
|
|
253
204
|
payload
|
|
254
205
|
};
|
|
255
206
|
}
|
|
256
|
-
async function createX402Payment(signer,
|
|
207
|
+
async function createX402Payment(signer, requirement, fromAddress, nonce, validBefore, domainName, domainVersion) {
|
|
257
208
|
const effectiveNonce = nonce ?? createNonce();
|
|
258
209
|
const effectiveValidBefore = validBefore ?? Math.floor(Date.now() / 1e3) + 3600;
|
|
259
210
|
return createX402V2Payment(
|
|
260
211
|
signer,
|
|
261
|
-
|
|
212
|
+
requirement,
|
|
262
213
|
fromAddress,
|
|
263
214
|
effectiveNonce,
|
|
264
215
|
effectiveValidBefore,
|
|
@@ -266,9 +217,8 @@ async function createX402Payment(signer, parsed, fromAddress, nonce, validBefore
|
|
|
266
217
|
domainVersion
|
|
267
218
|
);
|
|
268
219
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
}
|
|
220
|
+
|
|
221
|
+
// src/transport.ts
|
|
272
222
|
var defaultConfig = {
|
|
273
223
|
baseURL: "",
|
|
274
224
|
headers: {},
|
|
@@ -280,7 +230,8 @@ var defaultConfig = {
|
|
|
280
230
|
onPaymentSuccess: () => {
|
|
281
231
|
},
|
|
282
232
|
onPaymentError: () => {
|
|
283
|
-
}
|
|
233
|
+
},
|
|
234
|
+
hooks: []
|
|
284
235
|
};
|
|
285
236
|
var createState = (config) => ({
|
|
286
237
|
config: { ...defaultConfig, ...config }
|
|
@@ -301,48 +252,161 @@ var fetchWithTimeout = async (url, init, timeout) => {
|
|
|
301
252
|
}
|
|
302
253
|
};
|
|
303
254
|
var delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
255
|
+
var getRequirementDomainOverrides = (_parsed, requirement) => {
|
|
256
|
+
const extra = requirement.extra;
|
|
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;
|
|
260
|
+
return {
|
|
261
|
+
domainName: requirement.name ?? extraName,
|
|
262
|
+
domainVersion: requirement.version ?? extraVersion
|
|
263
|
+
};
|
|
264
|
+
};
|
|
265
|
+
var getPaymentFailureDetail = async (response) => {
|
|
266
|
+
const text = (await response.clone().text()).trim();
|
|
267
|
+
if (!text) {
|
|
268
|
+
return void 0;
|
|
269
|
+
}
|
|
270
|
+
try {
|
|
271
|
+
const parsed = JSON.parse(text);
|
|
272
|
+
if (typeof parsed.message === "string" && parsed.message.trim()) {
|
|
273
|
+
return parsed.message.trim();
|
|
274
|
+
}
|
|
275
|
+
if (typeof parsed.error === "string" && parsed.error.trim()) {
|
|
276
|
+
return parsed.error.trim();
|
|
277
|
+
}
|
|
278
|
+
} catch {
|
|
279
|
+
}
|
|
280
|
+
return text;
|
|
281
|
+
};
|
|
282
|
+
var createPaymentVerificationError = async (response) => {
|
|
283
|
+
const detail = await getPaymentFailureDetail(response);
|
|
284
|
+
return new Error(
|
|
285
|
+
detail ? `Payment verification failed: ${detail}` : "Payment verification failed"
|
|
286
|
+
);
|
|
287
|
+
};
|
|
304
288
|
var handlePaymentRequired = async (state, response) => {
|
|
305
289
|
if (!state.signer) {
|
|
306
|
-
throw new SignerRequiredError(
|
|
290
|
+
throw new SignerRequiredError(
|
|
291
|
+
"Cannot handle payment: no signer configured."
|
|
292
|
+
);
|
|
307
293
|
}
|
|
308
294
|
try {
|
|
309
295
|
const parsed = parsePaymentRequired(response);
|
|
296
|
+
const initialRequirement = parsed.accepts[0];
|
|
297
|
+
if (!initialRequirement) {
|
|
298
|
+
throw new Error("No payment requirements found in accepts array");
|
|
299
|
+
}
|
|
310
300
|
const from = await state.signer.getAddress();
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
301
|
+
const paymentRequiredContext = {
|
|
302
|
+
url: response.url,
|
|
303
|
+
requestInit: void 0,
|
|
304
|
+
accepts: parsed.accepts,
|
|
305
|
+
requirements: initialRequirement,
|
|
306
|
+
selectedRequirement: initialRequirement,
|
|
307
|
+
serverExtensions: void 0,
|
|
308
|
+
fromAddress: from,
|
|
309
|
+
nonce: `0x${Date.now().toString(16).padStart(64, "0")}`,
|
|
310
|
+
validBefore: Math.floor(Date.now() / 1e3) + initialRequirement.maxTimeoutSeconds
|
|
311
|
+
};
|
|
312
|
+
await runOnPaymentRequiredHooks(
|
|
313
|
+
state.config.hooks,
|
|
314
|
+
paymentRequiredContext
|
|
315
|
+
);
|
|
316
|
+
const attemptRequirements = await getRequirementAttemptOrderWithHooks(
|
|
317
|
+
state.config.hooks,
|
|
318
|
+
paymentRequiredContext
|
|
321
319
|
);
|
|
322
|
-
const
|
|
323
|
-
|
|
320
|
+
const headerName = getPaymentHeaderName(parsed.version);
|
|
321
|
+
let lastError;
|
|
322
|
+
for (const selectedRequirement of attemptRequirements) {
|
|
323
|
+
try {
|
|
324
|
+
const validBefore = Math.floor(Date.now() / 1e3) + selectedRequirement.maxTimeoutSeconds;
|
|
325
|
+
const attemptContext = {
|
|
326
|
+
...paymentRequiredContext,
|
|
327
|
+
requirements: selectedRequirement,
|
|
328
|
+
selectedRequirement,
|
|
329
|
+
validBefore
|
|
330
|
+
};
|
|
331
|
+
const requirementDomain = getRequirementDomainOverrides(
|
|
332
|
+
parsed,
|
|
333
|
+
selectedRequirement
|
|
334
|
+
);
|
|
335
|
+
const payload = await createX402Payment(
|
|
336
|
+
state.signer,
|
|
337
|
+
selectedRequirement,
|
|
338
|
+
from,
|
|
339
|
+
attemptContext.nonce,
|
|
340
|
+
attemptContext.validBefore,
|
|
341
|
+
requirementDomain.domainName,
|
|
342
|
+
requirementDomain.domainVersion
|
|
343
|
+
);
|
|
344
|
+
await runBeforeSignPaymentHooks(state.config.hooks, {
|
|
345
|
+
payload,
|
|
346
|
+
requirements: selectedRequirement,
|
|
347
|
+
wallet: state.signer,
|
|
348
|
+
paymentContext: attemptContext
|
|
349
|
+
});
|
|
350
|
+
const encoded = encodePaymentV2(payload);
|
|
351
|
+
const paymentResponse = await fetchWithTimeout(
|
|
352
|
+
response.url,
|
|
353
|
+
{
|
|
354
|
+
method: "GET",
|
|
355
|
+
headers: { ...state.config.headers, [headerName]: encoded }
|
|
356
|
+
},
|
|
357
|
+
state.config.timeout
|
|
358
|
+
);
|
|
359
|
+
await runAfterPaymentResponseHooks(state.config.hooks, {
|
|
360
|
+
payload,
|
|
361
|
+
requirements: selectedRequirement,
|
|
362
|
+
wallet: state.signer,
|
|
363
|
+
paymentContext: attemptContext,
|
|
364
|
+
response: paymentResponse
|
|
365
|
+
});
|
|
366
|
+
if (paymentResponse.status === 402) {
|
|
367
|
+
lastError = await createPaymentVerificationError(paymentResponse);
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const settlement = decodeSettlementV2(
|
|
371
|
+
paymentResponse.headers.get(V2_HEADERS.PAYMENT_RESPONSE) || ""
|
|
372
|
+
);
|
|
373
|
+
return { success: true, settlement };
|
|
374
|
+
} catch (error) {
|
|
375
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
throw lastError ?? new Error("Payment verification failed");
|
|
324
379
|
} catch (error) {
|
|
325
|
-
return {
|
|
380
|
+
return {
|
|
381
|
+
success: false,
|
|
382
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
383
|
+
};
|
|
326
384
|
}
|
|
327
385
|
};
|
|
328
386
|
var shouldRetryPayment = async (state, response) => {
|
|
329
387
|
if (!state.config.autoPay) return false;
|
|
330
388
|
const parsed = parsePaymentRequired(response);
|
|
331
|
-
return await state.config.onPaymentRequired(parsed.
|
|
389
|
+
return await state.config.onPaymentRequired(parsed.accepts[0]);
|
|
332
390
|
};
|
|
333
391
|
var x402Fetch = async (state, url, init = {}) => {
|
|
334
392
|
const fullUrl = state.config.baseURL ? new URL(url, state.config.baseURL).toString() : url;
|
|
335
393
|
const headers = new Headers({ ...state.config.headers, ...init.headers });
|
|
336
394
|
for (let attempt = 1; attempt <= state.config.maxRetries; attempt++) {
|
|
337
395
|
try {
|
|
338
|
-
const response = await fetchWithTimeout(
|
|
396
|
+
const response = await fetchWithTimeout(
|
|
397
|
+
fullUrl,
|
|
398
|
+
{ ...init, headers },
|
|
399
|
+
state.config.timeout
|
|
400
|
+
);
|
|
339
401
|
if (response.status === 402 && !init.skipAutoPay && await shouldRetryPayment(state, response)) {
|
|
340
402
|
const paymentResult = await handlePaymentRequired(state, response);
|
|
341
403
|
if (paymentResult.success) {
|
|
342
404
|
state.config.onPaymentSuccess(paymentResult.settlement);
|
|
343
405
|
continue;
|
|
344
406
|
}
|
|
345
|
-
state.config.onPaymentError(
|
|
407
|
+
state.config.onPaymentError(
|
|
408
|
+
paymentResult.error ?? new Error("Payment failed")
|
|
409
|
+
);
|
|
346
410
|
}
|
|
347
411
|
return response;
|
|
348
412
|
} catch (error) {
|
|
@@ -365,7 +429,7 @@ var createMethod = (state, method) => async (url, bodyOrInit, init) => {
|
|
|
365
429
|
});
|
|
366
430
|
};
|
|
367
431
|
var createX402Transport = (config) => {
|
|
368
|
-
|
|
432
|
+
const state = createState(config);
|
|
369
433
|
return {
|
|
370
434
|
fetch: (url, init) => x402Fetch(state, url, init),
|
|
371
435
|
get: createMethod(state, "GET"),
|
|
@@ -380,37 +444,7 @@ var createX402Transport = (config) => {
|
|
|
380
444
|
};
|
|
381
445
|
};
|
|
382
446
|
|
|
383
|
-
// src/
|
|
384
|
-
function createX402Client(config) {
|
|
385
|
-
const transport = createX402Transport(void 0);
|
|
386
|
-
if ("signer" in config && config.signer) {
|
|
387
|
-
transport.setSigner(config.signer);
|
|
388
|
-
} else if ("provider" in config && config.provider) {
|
|
389
|
-
const provider = config.provider;
|
|
390
|
-
if (provider.getSigner) {
|
|
391
|
-
try {
|
|
392
|
-
const signer = provider.getSigner();
|
|
393
|
-
if (signer) {
|
|
394
|
-
transport.setSigner(signer);
|
|
395
|
-
}
|
|
396
|
-
} catch {
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
} else {
|
|
400
|
-
throw new SignerRequiredError(
|
|
401
|
-
"Either 'signer' or 'provider' with getSigner() must be provided"
|
|
402
|
-
);
|
|
403
|
-
}
|
|
404
|
-
return {
|
|
405
|
-
get: (url, init) => transport.get(url, init),
|
|
406
|
-
post: (url, body, init) => transport.post(url, body, init),
|
|
407
|
-
put: (url, body, init) => transport.put(url, body, init),
|
|
408
|
-
del: (url, init) => transport.del(url, init),
|
|
409
|
-
patch: (url, body, init) => transport.patch(url, body, init),
|
|
410
|
-
setSigner: (signer) => transport.setSigner(signer),
|
|
411
|
-
getSigner: () => transport.getSigner()
|
|
412
|
-
};
|
|
413
|
-
}
|
|
447
|
+
// src/payment-api.ts
|
|
414
448
|
var normalizeWallet = (wallet) => {
|
|
415
449
|
if (typeof wallet === "object" && wallet !== null && "signer" in wallet) {
|
|
416
450
|
return wallet.signer;
|
|
@@ -472,19 +506,37 @@ var armoryPay = async (wallet, url, network, token, options) => {
|
|
|
472
506
|
}
|
|
473
507
|
};
|
|
474
508
|
var armoryGet = (wallet, url, network, token, options) => {
|
|
475
|
-
return armoryPay(wallet, url, network, token, {
|
|
509
|
+
return armoryPay(wallet, url, network, token, {
|
|
510
|
+
...options,
|
|
511
|
+
method: "GET"
|
|
512
|
+
});
|
|
476
513
|
};
|
|
477
514
|
var armoryPost = (wallet, url, network, token, body, options) => {
|
|
478
|
-
return armoryPay(wallet, url, network, token, {
|
|
515
|
+
return armoryPay(wallet, url, network, token, {
|
|
516
|
+
...options,
|
|
517
|
+
method: "POST",
|
|
518
|
+
body
|
|
519
|
+
});
|
|
479
520
|
};
|
|
480
521
|
var armoryPut = (wallet, url, network, token, body, options) => {
|
|
481
|
-
return armoryPay(wallet, url, network, token, {
|
|
522
|
+
return armoryPay(wallet, url, network, token, {
|
|
523
|
+
...options,
|
|
524
|
+
method: "PUT",
|
|
525
|
+
body
|
|
526
|
+
});
|
|
482
527
|
};
|
|
483
528
|
var armoryDelete = (wallet, url, network, token, options) => {
|
|
484
|
-
return armoryPay(wallet, url, network, token, {
|
|
529
|
+
return armoryPay(wallet, url, network, token, {
|
|
530
|
+
...options,
|
|
531
|
+
method: "DELETE"
|
|
532
|
+
});
|
|
485
533
|
};
|
|
486
534
|
var armoryPatch = (wallet, url, network, token, body, options) => {
|
|
487
|
-
return armoryPay(wallet, url, network, token, {
|
|
535
|
+
return armoryPay(wallet, url, network, token, {
|
|
536
|
+
...options,
|
|
537
|
+
method: "PATCH",
|
|
538
|
+
body
|
|
539
|
+
});
|
|
488
540
|
};
|
|
489
541
|
var getWalletAddress = async (wallet) => {
|
|
490
542
|
const signer = normalizeWallet(wallet);
|
|
@@ -498,7 +550,7 @@ var validateNetwork = (network) => {
|
|
|
498
550
|
return { success: true, network: resolved.config.name };
|
|
499
551
|
};
|
|
500
552
|
var validateToken = (token, network) => {
|
|
501
|
-
let resolvedNetwork
|
|
553
|
+
let resolvedNetwork;
|
|
502
554
|
if (network) {
|
|
503
555
|
const networkResult = resolveNetwork(network);
|
|
504
556
|
if (isValidationError(networkResult)) {
|
|
@@ -524,7 +576,15 @@ var getTokens = () => {
|
|
|
524
576
|
const { getAvailableTokens } = __require("@armory-sh/base");
|
|
525
577
|
return getAvailableTokens();
|
|
526
578
|
};
|
|
527
|
-
|
|
579
|
+
|
|
580
|
+
// src/armory-api.ts
|
|
581
|
+
var ALL_METHODS = /* @__PURE__ */ new Set([
|
|
582
|
+
"GET",
|
|
583
|
+
"POST",
|
|
584
|
+
"PUT",
|
|
585
|
+
"DELETE",
|
|
586
|
+
"PATCH"
|
|
587
|
+
]);
|
|
528
588
|
var arrayify = (value) => {
|
|
529
589
|
if (value === void 0) return void 0;
|
|
530
590
|
return Array.isArray(value) ? value : [value];
|
|
@@ -592,4 +652,38 @@ var createArmory = (config) => {
|
|
|
592
652
|
};
|
|
593
653
|
};
|
|
594
654
|
|
|
595
|
-
|
|
655
|
+
// src/client.ts
|
|
656
|
+
function createX402Client(config) {
|
|
657
|
+
const transport = createX402Transport({
|
|
658
|
+
hooks: config.hooks
|
|
659
|
+
});
|
|
660
|
+
if ("signer" in config && config.signer) {
|
|
661
|
+
transport.setSigner(config.signer);
|
|
662
|
+
} else if ("provider" in config && config.provider) {
|
|
663
|
+
const provider = config.provider;
|
|
664
|
+
if (provider.getSigner) {
|
|
665
|
+
try {
|
|
666
|
+
const signer = provider.getSigner();
|
|
667
|
+
if (signer) {
|
|
668
|
+
transport.setSigner(signer);
|
|
669
|
+
}
|
|
670
|
+
} catch {
|
|
671
|
+
}
|
|
672
|
+
}
|
|
673
|
+
} else {
|
|
674
|
+
throw new SignerRequiredError(
|
|
675
|
+
"Either 'signer' or 'provider' with getSigner() must be provided"
|
|
676
|
+
);
|
|
677
|
+
}
|
|
678
|
+
return {
|
|
679
|
+
get: (url, init) => transport.get(url, init),
|
|
680
|
+
post: (url, body, init) => transport.post(url, body, init),
|
|
681
|
+
put: (url, body, init) => transport.put(url, body, init),
|
|
682
|
+
del: (url, init) => transport.del(url, init),
|
|
683
|
+
patch: (url, body, init) => transport.patch(url, body, init),
|
|
684
|
+
setSigner: (signer) => transport.setSigner(signer),
|
|
685
|
+
getSigner: () => transport.getSigner()
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
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.
|
|
3
|
+
"version": "0.2.28",
|
|
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.
|
|
50
|
+
"@armory-sh/base": "0.2.29",
|
|
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
|
}
|