@armory-sh/client-ethers 0.2.26-alpha.23.82 → 0.2.27-alpha.23.83
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 +2 -0
- package/dist/index.js +86 -41
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -79,10 +79,12 @@ const client = createX402Client({
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
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.
|
|
82
83
|
|
|
83
84
|
## Features
|
|
84
85
|
|
|
85
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`)
|
|
86
88
|
- **EIP-3009 Signing**: Full support for EIP-3009 TransferWithAuthorization
|
|
87
89
|
- **Multi-Network**: Ethereum, Base, SKALE support
|
|
88
90
|
- **Multi-Token**: USDC, EURC, USDT, WBTC, WETH, SKL
|
package/dist/index.js
CHANGED
|
@@ -1,6 +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,
|
|
3
|
+
import { runOnPaymentRequiredHooks, getRequirementAttemptOrderWithHooks, runBeforeSignPaymentHooks, runAfterPaymentResponseHooks } from '@armory-sh/base/client-hooks-runtime';
|
|
4
4
|
import { ethers } from 'ethers';
|
|
5
5
|
|
|
6
6
|
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
@@ -262,6 +262,29 @@ var getRequirementDomainOverrides = (_parsed, requirement) => {
|
|
|
262
262
|
domainVersion: requirement.version ?? extraVersion
|
|
263
263
|
};
|
|
264
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
|
+
};
|
|
265
288
|
var handlePaymentRequired = async (state, response) => {
|
|
266
289
|
if (!state.signer) {
|
|
267
290
|
throw new SignerRequiredError(
|
|
@@ -286,51 +309,73 @@ var handlePaymentRequired = async (state, response) => {
|
|
|
286
309
|
nonce: `0x${Date.now().toString(16).padStart(64, "0")}`,
|
|
287
310
|
validBefore: Math.floor(Date.now() / 1e3) + initialRequirement.maxTimeoutSeconds
|
|
288
311
|
};
|
|
289
|
-
await runOnPaymentRequiredHooks(
|
|
290
|
-
const selectedRequirement = await selectRequirementWithHooks(
|
|
312
|
+
await runOnPaymentRequiredHooks(
|
|
291
313
|
state.config.hooks,
|
|
292
314
|
paymentRequiredContext
|
|
293
315
|
);
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
);
|
|
298
|
-
const payload = await createX402Payment(
|
|
299
|
-
state.signer,
|
|
300
|
-
selectedRequirement,
|
|
301
|
-
from,
|
|
302
|
-
paymentRequiredContext.nonce,
|
|
303
|
-
paymentRequiredContext.validBefore,
|
|
304
|
-
requirementDomain.domainName,
|
|
305
|
-
requirementDomain.domainVersion
|
|
316
|
+
const attemptRequirements = await getRequirementAttemptOrderWithHooks(
|
|
317
|
+
state.config.hooks,
|
|
318
|
+
paymentRequiredContext
|
|
306
319
|
);
|
|
307
|
-
await runBeforeSignPaymentHooks(state.config.hooks, {
|
|
308
|
-
payload,
|
|
309
|
-
requirements: selectedRequirement,
|
|
310
|
-
wallet: state.signer,
|
|
311
|
-
paymentContext: paymentRequiredContext
|
|
312
|
-
});
|
|
313
|
-
const encoded = encodePaymentV2(payload);
|
|
314
320
|
const headerName = getPaymentHeaderName(parsed.version);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
{
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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");
|
|
334
379
|
} catch (error) {
|
|
335
380
|
return {
|
|
336
381
|
success: false,
|
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.27-alpha.23.83",
|
|
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.28-alpha.23.83",
|
|
51
51
|
"ethers": "6.16.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|