@armory-sh/base 0.2.29 → 0.2.30
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 +304 -28
- package/dist/facilitator-capabilities.d.ts +9 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +117 -1
- package/dist/payment-requirements.d.ts +6 -0
- package/package.json +1 -1
- package/src/facilitator-capabilities.ts +125 -0
- package/src/index.ts +7 -0
- package/src/payment-requirements.ts +54 -0
package/README.md
CHANGED
|
@@ -14,56 +14,343 @@ bun add @armory-sh/base
|
|
|
14
14
|
|
|
15
15
|
Armory enables HTTP API payments via EIP-3009 `transferWithAuthorization`. Let your users pay with USDC directly from their wallet—no credit cards, no middlemen, no gas for payers.
|
|
16
16
|
|
|
17
|
-
##
|
|
17
|
+
## API Reference
|
|
18
|
+
|
|
19
|
+
### Protocol Types
|
|
18
20
|
|
|
19
21
|
```typescript
|
|
20
|
-
import {
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
22
|
+
import type {
|
|
23
|
+
// V2 Protocol (x402 v2 / CAIP)
|
|
24
|
+
PaymentPayloadV2,
|
|
25
|
+
PaymentRequirementsV2,
|
|
26
|
+
PaymentRequiredV2,
|
|
27
|
+
SettlementResponseV2,
|
|
28
|
+
PayToV2,
|
|
29
|
+
EIP3009Authorization,
|
|
30
|
+
Extensions,
|
|
31
|
+
ResourceInfo,
|
|
32
|
+
Signature,
|
|
33
|
+
Address,
|
|
34
|
+
CAIPAssetId,
|
|
35
|
+
|
|
36
|
+
// Unified Types
|
|
37
|
+
PaymentPayload,
|
|
38
|
+
PaymentRequirements,
|
|
39
|
+
SettlementResponse,
|
|
40
|
+
} from '@armory-sh/base';
|
|
41
|
+
```
|
|
28
42
|
|
|
29
|
-
|
|
43
|
+
### Encoding / Decoding
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import {
|
|
47
|
+
// V2 Encoding
|
|
30
48
|
encodePaymentV2,
|
|
31
49
|
decodePaymentV2,
|
|
32
50
|
encodeSettlementV2,
|
|
33
51
|
decodeSettlementV2,
|
|
52
|
+
|
|
53
|
+
// x402 Cross-Version Encoding
|
|
54
|
+
encodePayment,
|
|
55
|
+
decodePayment,
|
|
56
|
+
encodeSettlementResponse,
|
|
57
|
+
decodeSettlementResponse,
|
|
58
|
+
encodeX402Response,
|
|
59
|
+
decodeX402Response,
|
|
60
|
+
|
|
61
|
+
// Base64 Utilities
|
|
34
62
|
safeBase64Encode,
|
|
35
63
|
safeBase64Decode,
|
|
64
|
+
toBase64Url,
|
|
65
|
+
normalizeBase64Url,
|
|
66
|
+
encodeUtf8ToBase64,
|
|
67
|
+
decodeBase64ToUtf8,
|
|
68
|
+
|
|
69
|
+
// Header Creation
|
|
70
|
+
createPaymentRequiredHeaders,
|
|
71
|
+
createSettlementHeaders,
|
|
72
|
+
extractPaymentFromHeaders,
|
|
73
|
+
|
|
74
|
+
// Type Guards
|
|
75
|
+
isPaymentV2,
|
|
76
|
+
isSettlementV2,
|
|
77
|
+
isX402V2Payload,
|
|
78
|
+
isX402V2PaymentRequired,
|
|
79
|
+
isX402V2Settlement,
|
|
80
|
+
isPaymentPayload,
|
|
81
|
+
detectPaymentVersion,
|
|
82
|
+
} from '@armory-sh/base';
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### EIP-712 (EIP-3009 Signing)
|
|
36
86
|
|
|
37
|
-
|
|
87
|
+
```typescript
|
|
88
|
+
import {
|
|
89
|
+
// Domain & Typed Data
|
|
38
90
|
createEIP712Domain,
|
|
91
|
+
EIP712_TYPES,
|
|
92
|
+
USDC_DOMAIN,
|
|
93
|
+
|
|
94
|
+
// Transfer With Authorization
|
|
39
95
|
createTransferWithAuthorization,
|
|
40
96
|
validateTransferWithAuthorization,
|
|
41
|
-
EIP712_TYPES,
|
|
42
97
|
|
|
43
|
-
//
|
|
98
|
+
// Types
|
|
99
|
+
type EIP712Domain,
|
|
100
|
+
type TransferWithAuthorization,
|
|
101
|
+
type TypedDataDomain,
|
|
102
|
+
} from '@armory-sh/base';
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Networks
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
import {
|
|
109
|
+
// Network Registry
|
|
44
110
|
NETWORKS,
|
|
45
111
|
getNetworkConfig,
|
|
46
112
|
getNetworkByChainId,
|
|
47
113
|
getMainnets,
|
|
48
114
|
getTestnets,
|
|
49
115
|
|
|
50
|
-
//
|
|
116
|
+
// Network Resolution
|
|
117
|
+
resolveNetwork,
|
|
118
|
+
normalizeNetworkName,
|
|
119
|
+
getAvailableNetworks,
|
|
120
|
+
|
|
121
|
+
// CAIP-2 Conversion
|
|
122
|
+
networkToCaip2,
|
|
123
|
+
caip2ToNetwork,
|
|
124
|
+
|
|
125
|
+
// Types
|
|
126
|
+
type NetworkConfig,
|
|
127
|
+
type ResolvedNetwork,
|
|
128
|
+
} from '@armory-sh/base';
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**Supported Networks**
|
|
132
|
+
|
|
133
|
+
| Key | Chain ID | Name |
|
|
134
|
+
|-----|----------|------|
|
|
135
|
+
| `ethereum` | 1 | Ethereum Mainnet |
|
|
136
|
+
| `base` | 8453 | Base Mainnet |
|
|
137
|
+
| `base-sepolia` | 84532 | Base Sepolia |
|
|
138
|
+
| `skale-base` | 1187947933 | SKALE Base |
|
|
139
|
+
| `skale-base-sepolia` | 324705682 | SKALE Base Sepolia |
|
|
140
|
+
| `ethereum-sepolia` | 11155111 | Ethereum Sepolia |
|
|
141
|
+
|
|
142
|
+
### Tokens
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
import {
|
|
146
|
+
// Pre-configured Tokens
|
|
51
147
|
TOKENS,
|
|
148
|
+
USDC_BASE,
|
|
149
|
+
USDC_BASE_SEPOLIA,
|
|
150
|
+
EURC_BASE,
|
|
151
|
+
getUSDCTokens,
|
|
152
|
+
getEURCTokens,
|
|
153
|
+
getAllTokens,
|
|
154
|
+
getTokensByChain,
|
|
155
|
+
getTokensBySymbol,
|
|
156
|
+
|
|
157
|
+
// Custom Token Registry
|
|
52
158
|
registerToken,
|
|
53
159
|
getCustomToken,
|
|
54
160
|
getAllCustomTokens,
|
|
55
161
|
unregisterToken,
|
|
56
162
|
isCustomToken,
|
|
57
163
|
|
|
58
|
-
//
|
|
164
|
+
// Token Resolution
|
|
165
|
+
resolveToken,
|
|
166
|
+
getAvailableTokens,
|
|
167
|
+
addressToAssetId,
|
|
168
|
+
assetIdToAddress,
|
|
169
|
+
|
|
170
|
+
// Types
|
|
171
|
+
type CustomToken,
|
|
172
|
+
type ResolvedToken,
|
|
173
|
+
} from '@armory-sh/base';
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**Supported Tokens**
|
|
177
|
+
|
|
178
|
+
USDC, EURC, USDT, WBTC, WETH, SKL across supported networks.
|
|
179
|
+
|
|
180
|
+
### Protocol Utilities
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import {
|
|
184
|
+
// Nonce & Time
|
|
185
|
+
generateNonce,
|
|
59
186
|
createNonce,
|
|
60
|
-
|
|
61
|
-
|
|
187
|
+
getCurrentTimestamp,
|
|
188
|
+
calculateValidBefore,
|
|
189
|
+
|
|
190
|
+
// Parsing
|
|
191
|
+
parseJsonOrBase64,
|
|
192
|
+
|
|
193
|
+
// Headers
|
|
194
|
+
getPaymentHeaderName,
|
|
195
|
+
PAYMENT_REQUIRED_HEADER,
|
|
196
|
+
PAYMENT_SIGNATURE_HEADER,
|
|
197
|
+
PAYMENT_RESPONSE_HEADER,
|
|
198
|
+
|
|
199
|
+
// Version Detection
|
|
200
|
+
detectX402Version,
|
|
201
|
+
X402_VERSION,
|
|
202
|
+
} from '@armory-sh/base';
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Payment Requirements (Middleware)
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
import {
|
|
209
|
+
// Requirement Creation
|
|
210
|
+
createPaymentRequirements,
|
|
211
|
+
|
|
212
|
+
// Requirement Lookup
|
|
213
|
+
findRequirementByNetwork,
|
|
214
|
+
findRequirementByAccepted,
|
|
215
|
+
|
|
216
|
+
// Types
|
|
217
|
+
type PaymentConfig,
|
|
218
|
+
type ResolvedRequirementsConfig,
|
|
219
|
+
} from '@armory-sh/base';
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Validation
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
import {
|
|
226
|
+
// Network & Token Validation
|
|
62
227
|
resolveNetwork,
|
|
63
228
|
resolveToken,
|
|
229
|
+
validatePaymentConfig,
|
|
230
|
+
validateAcceptConfig,
|
|
231
|
+
|
|
232
|
+
// Facilitator Validation
|
|
233
|
+
resolveFacilitator,
|
|
234
|
+
checkFacilitatorSupport,
|
|
235
|
+
|
|
236
|
+
// Error Creation
|
|
237
|
+
createError,
|
|
238
|
+
|
|
239
|
+
// Type Guards
|
|
240
|
+
isValidationError,
|
|
241
|
+
isResolvedNetwork,
|
|
242
|
+
isResolvedToken,
|
|
243
|
+
|
|
244
|
+
// Available Options
|
|
245
|
+
getAvailableNetworks,
|
|
246
|
+
getAvailableTokens,
|
|
64
247
|
} from '@armory-sh/base';
|
|
65
248
|
```
|
|
66
249
|
|
|
250
|
+
### Utilities
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import {
|
|
254
|
+
// Unit Conversion
|
|
255
|
+
toAtomicUnits,
|
|
256
|
+
fromAtomicUnits,
|
|
257
|
+
|
|
258
|
+
// Address Utilities
|
|
259
|
+
normalizeAddress,
|
|
260
|
+
isValidAddress,
|
|
261
|
+
isAddress,
|
|
262
|
+
|
|
263
|
+
// Route Matching
|
|
264
|
+
matchRoute,
|
|
265
|
+
parseRoutePattern,
|
|
266
|
+
findMatchingRoute,
|
|
267
|
+
validateRouteConfig,
|
|
268
|
+
|
|
269
|
+
// Types
|
|
270
|
+
type RouteMatcher,
|
|
271
|
+
type RoutePattern,
|
|
272
|
+
} from '@armory-sh/base';
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Client Hooks Runtime
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
import {
|
|
279
|
+
// Hook Execution
|
|
280
|
+
runOnPaymentRequiredHooks,
|
|
281
|
+
runBeforeSignPaymentHooks,
|
|
282
|
+
runAfterPaymentResponseHooks,
|
|
283
|
+
selectRequirementWithHooks,
|
|
284
|
+
|
|
285
|
+
// Types
|
|
286
|
+
type ClientHook,
|
|
287
|
+
type OnPaymentRequiredHook,
|
|
288
|
+
type BeforePaymentHook,
|
|
289
|
+
type PaymentRequiredContext,
|
|
290
|
+
type PaymentPayloadContext,
|
|
291
|
+
type ClientHookErrorContext,
|
|
292
|
+
} from '@armory-sh/base';
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### ERC20 ABI
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
import {
|
|
299
|
+
ERC20_ABI,
|
|
300
|
+
|
|
301
|
+
// Types
|
|
302
|
+
type ERC20Abi,
|
|
303
|
+
type TransferWithAuthorizationParams,
|
|
304
|
+
type BalanceOfParams,
|
|
305
|
+
} from '@armory-sh/base';
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Payment Client (Facilitator)
|
|
309
|
+
|
|
310
|
+
```typescript
|
|
311
|
+
import {
|
|
312
|
+
// Payment Operations
|
|
313
|
+
verifyPayment,
|
|
314
|
+
settlePayment,
|
|
315
|
+
|
|
316
|
+
// Payload Extraction
|
|
317
|
+
decodePayloadHeader,
|
|
318
|
+
extractPayerAddress,
|
|
319
|
+
|
|
320
|
+
// Facilitator Discovery
|
|
321
|
+
getSupported,
|
|
322
|
+
|
|
323
|
+
// Types
|
|
324
|
+
type FacilitatorClientConfig,
|
|
325
|
+
type SupportedResponse,
|
|
326
|
+
} from '@armory-sh/base';
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Error Classes
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
import {
|
|
333
|
+
X402ClientError,
|
|
334
|
+
SigningError,
|
|
335
|
+
PaymentException,
|
|
336
|
+
} from '@armory-sh/base';
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Headers Constants
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import {
|
|
343
|
+
PAYMENT_REQUIRED_HEADER,
|
|
344
|
+
PAYMENT_SIGNATURE_HEADER,
|
|
345
|
+
PAYMENT_RESPONSE_HEADER,
|
|
346
|
+
V2_HEADERS,
|
|
347
|
+
} from '@armory-sh/base';
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
---
|
|
351
|
+
|
|
352
|
+
## Quick Start
|
|
353
|
+
|
|
67
354
|
## Quick Start
|
|
68
355
|
|
|
69
356
|
### Pre-configured Tokens
|
|
@@ -155,17 +442,6 @@ const decoded = decodePaymentV2(encoded)
|
|
|
155
442
|
- **Custom Tokens**: Register your own tokens
|
|
156
443
|
- **Encoding**: Base64URL encoding for HTTP headers
|
|
157
444
|
|
|
158
|
-
## Supported Networks
|
|
159
|
-
|
|
160
|
-
| Network | Chain ID |
|
|
161
|
-
|---------|----------|
|
|
162
|
-
| Ethereum | 1 |
|
|
163
|
-
| Base | 8453 |
|
|
164
|
-
| Base Sepolia | 84532 |
|
|
165
|
-
| SKALE Base | 1187947933 |
|
|
166
|
-
| SKALE Base Sepolia | 324705682 |
|
|
167
|
-
| Ethereum Sepolia | 11155111 |
|
|
168
|
-
|
|
169
445
|
## License
|
|
170
446
|
|
|
171
447
|
MIT © [Sawyer Cutler](https://github.com/TheGreatAxios/armory)
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PaymentRequirementsV2 } from "./types/v2";
|
|
2
|
+
import type { FacilitatorRoutingConfig } from "./payment-requirements";
|
|
3
|
+
export declare function filterExtensionsForFacilitator(extensions: Record<string, unknown> | undefined, facilitatorUrl: string | undefined, network: string, ttlMs?: number): Promise<Record<string, unknown>>;
|
|
4
|
+
export declare function filterExtensionsForFacilitators(extensions: Record<string, unknown> | undefined, facilitators: Array<{
|
|
5
|
+
url?: string;
|
|
6
|
+
network: string;
|
|
7
|
+
}>, ttlMs?: number): Promise<Record<string, unknown>>;
|
|
8
|
+
export declare function filterExtensionsForRequirements(extensions: Record<string, unknown> | undefined, requirements: PaymentRequirementsV2[], config: FacilitatorRoutingConfig, ttlMs?: number): Promise<Record<string, unknown>>;
|
|
9
|
+
export declare function clearFacilitatorCapabilityCache(): void;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,8 +10,9 @@ export { createPaymentRequiredHeaders, createSettlementHeaders, decodePayment, d
|
|
|
10
10
|
export { PaymentException, SigningError, X402ClientError, } from "./errors";
|
|
11
11
|
export type { FacilitatorClientConfig, SupportedKind, SupportedResponse, } from "./payment-client";
|
|
12
12
|
export { decodePayloadHeader, extractPayerAddress, getSupported, settlePayment, verifyPayment, } from "./payment-client";
|
|
13
|
+
export { clearFacilitatorCapabilityCache, filterExtensionsForFacilitator, filterExtensionsForFacilitators, filterExtensionsForRequirements, } from "./facilitator-capabilities";
|
|
13
14
|
export type { PaymentConfig, ResolvedRequirementsConfig, } from "./payment-requirements";
|
|
14
|
-
export { createPaymentRequirements, findRequirementByAccepted, findRequirementByNetwork, } from "./payment-requirements";
|
|
15
|
+
export { createPaymentRequirements, findRequirementByAccepted, findRequirementByNetwork, resolveFacilitatorUrlFromRequirement, } from "./payment-requirements";
|
|
15
16
|
export { calculateValidBefore, detectX402Version, generateNonce, getPaymentHeaderName, parseJsonOrBase64, } from "./protocol";
|
|
16
17
|
export type { AcceptPaymentOptions, ArmoryPaymentResult, FacilitatorConfig, FacilitatorSettleResult, FacilitatorVerifyResult, NetworkId, PaymentError, PaymentErrorCode, PaymentResult, PayToAddress, PricingConfig, ResolvedFacilitator, ResolvedNetwork, ResolvedPaymentConfig, ResolvedToken, SettlementMode, TokenId, ValidationError, } from "./types/api";
|
|
17
18
|
export type { BeforePaymentHook, ClientHook, ClientHookErrorContext, ExtensionHook, HookConfig, HookRegistry, HookResult, OnPaymentRequiredHook, PaymentPayloadContext, PaymentRequiredContext, } from "./types/hooks";
|
package/dist/index.js
CHANGED
|
@@ -1434,6 +1434,37 @@ function resolveFacilitatorUrl(config, network, token) {
|
|
|
1434
1434
|
}
|
|
1435
1435
|
return config.facilitatorUrl;
|
|
1436
1436
|
}
|
|
1437
|
+
function resolveFacilitatorUrlFromRequirement(config, requirement) {
|
|
1438
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
1439
|
+
const assetAddress = requirement.asset.toLowerCase();
|
|
1440
|
+
if (config.facilitatorUrlByToken) {
|
|
1441
|
+
for (const [chainKey, tokenMap] of Object.entries(
|
|
1442
|
+
config.facilitatorUrlByToken
|
|
1443
|
+
)) {
|
|
1444
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
1445
|
+
if (isValidationError2(resolvedChain) || resolvedChain.config.chainId !== chainId) {
|
|
1446
|
+
continue;
|
|
1447
|
+
}
|
|
1448
|
+
for (const [tokenKey2, url] of Object.entries(tokenMap)) {
|
|
1449
|
+
const resolvedToken = resolveToken(tokenKey2, resolvedChain);
|
|
1450
|
+
if (!isValidationError2(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
|
|
1451
|
+
return url;
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
if (config.facilitatorUrlByChain) {
|
|
1457
|
+
for (const [chainKey, url] of Object.entries(
|
|
1458
|
+
config.facilitatorUrlByChain
|
|
1459
|
+
)) {
|
|
1460
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
1461
|
+
if (!isValidationError2(resolvedChain) && resolvedChain.config.chainId === chainId) {
|
|
1462
|
+
return url;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
return config.facilitatorUrl;
|
|
1467
|
+
}
|
|
1437
1468
|
function resolveNetworks(chainInputs) {
|
|
1438
1469
|
const resolvedNetworks = [];
|
|
1439
1470
|
const errors = [];
|
|
@@ -1540,6 +1571,91 @@ function findRequirementByAccepted(requirements, accepted) {
|
|
|
1540
1571
|
);
|
|
1541
1572
|
}
|
|
1542
1573
|
|
|
1574
|
+
// src/facilitator-capabilities.ts
|
|
1575
|
+
var DEFAULT_CAPABILITY_TTL_MS = 5 * 60 * 1e3;
|
|
1576
|
+
var capabilityCache = /* @__PURE__ */ new Map();
|
|
1577
|
+
var normalizeNetwork = (network) => {
|
|
1578
|
+
return network.toLowerCase();
|
|
1579
|
+
};
|
|
1580
|
+
async function getExtensionKeysForFacilitator(url, network, ttlMs) {
|
|
1581
|
+
const cacheKey = `${url}|${normalizeNetwork(network)}`;
|
|
1582
|
+
const now = Date.now();
|
|
1583
|
+
const cached = capabilityCache.get(cacheKey);
|
|
1584
|
+
if (cached && cached.expiresAt > now) {
|
|
1585
|
+
return cached.keys;
|
|
1586
|
+
}
|
|
1587
|
+
try {
|
|
1588
|
+
const supported = await getSupported({ url });
|
|
1589
|
+
const extensionKeys = /* @__PURE__ */ new Set();
|
|
1590
|
+
for (const kind of supported.kinds) {
|
|
1591
|
+
if (normalizeNetwork(kind.network) !== normalizeNetwork(network)) {
|
|
1592
|
+
continue;
|
|
1593
|
+
}
|
|
1594
|
+
if (kind.extra && typeof kind.extra === "object") {
|
|
1595
|
+
for (const key of Object.keys(kind.extra)) {
|
|
1596
|
+
extensionKeys.add(key);
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
capabilityCache.set(cacheKey, {
|
|
1601
|
+
expiresAt: now + ttlMs,
|
|
1602
|
+
keys: extensionKeys
|
|
1603
|
+
});
|
|
1604
|
+
return extensionKeys;
|
|
1605
|
+
} catch {
|
|
1606
|
+
capabilityCache.set(cacheKey, {
|
|
1607
|
+
expiresAt: now + ttlMs,
|
|
1608
|
+
keys: /* @__PURE__ */ new Set()
|
|
1609
|
+
});
|
|
1610
|
+
return /* @__PURE__ */ new Set();
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
async function filterExtensionsForFacilitator(extensions, facilitatorUrl, network, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1614
|
+
if (!extensions || !facilitatorUrl) {
|
|
1615
|
+
return extensions ?? {};
|
|
1616
|
+
}
|
|
1617
|
+
const supportedKeys = await getExtensionKeysForFacilitator(
|
|
1618
|
+
facilitatorUrl,
|
|
1619
|
+
network,
|
|
1620
|
+
ttlMs
|
|
1621
|
+
);
|
|
1622
|
+
const filtered = {};
|
|
1623
|
+
for (const [key, value] of Object.entries(extensions)) {
|
|
1624
|
+
if (supportedKeys.has(key)) {
|
|
1625
|
+
filtered[key] = value;
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
return filtered;
|
|
1629
|
+
}
|
|
1630
|
+
async function filterExtensionsForFacilitators(extensions, facilitators, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1631
|
+
if (!extensions) {
|
|
1632
|
+
return {};
|
|
1633
|
+
}
|
|
1634
|
+
let filtered = { ...extensions };
|
|
1635
|
+
for (const facilitator of facilitators) {
|
|
1636
|
+
filtered = await filterExtensionsForFacilitator(
|
|
1637
|
+
filtered,
|
|
1638
|
+
facilitator.url,
|
|
1639
|
+
facilitator.network,
|
|
1640
|
+
ttlMs
|
|
1641
|
+
);
|
|
1642
|
+
if (Object.keys(filtered).length === 0) {
|
|
1643
|
+
return {};
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return filtered;
|
|
1647
|
+
}
|
|
1648
|
+
async function filterExtensionsForRequirements(extensions, requirements, config, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1649
|
+
const facilitators = requirements.map((requirement) => ({
|
|
1650
|
+
url: resolveFacilitatorUrlFromRequirement(config, requirement),
|
|
1651
|
+
network: requirement.network
|
|
1652
|
+
}));
|
|
1653
|
+
return filterExtensionsForFacilitators(extensions, facilitators, ttlMs);
|
|
1654
|
+
}
|
|
1655
|
+
function clearFacilitatorCapabilityCache() {
|
|
1656
|
+
capabilityCache.clear();
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1543
1659
|
// src/protocol.ts
|
|
1544
1660
|
function parseJsonOrBase64(value) {
|
|
1545
1661
|
try {
|
|
@@ -1753,4 +1869,4 @@ function validateRouteConfig(config) {
|
|
|
1753
1869
|
return null;
|
|
1754
1870
|
}
|
|
1755
1871
|
|
|
1756
|
-
export { EIP712_TYPES, ERC20_ABI, EURC_BASE, NETWORKS, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PAYMENT_SIGNATURE_HEADER, PaymentException, SCHEMES, SKL_SKALE_BASE, SKL_SKALE_BASE_SEPOLIA, SigningError, TOKENS, USDC_BASE, USDC_BASE_SEPOLIA, USDC_DOMAIN, USDC_SKALE_BASE, USDC_SKALE_BASE_SEPOLIA, USDT_SKALE_BASE, USDT_SKALE_BASE_SEPOLIA, V2_HEADERS, WBTC_SKALE_BASE, WBTC_SKALE_BASE_SEPOLIA, WETH_SKALE_BASE, WETH_SKALE_BASE_SEPOLIA, X402ClientError, X402_VERSION, addressToAssetId, assetIdToAddress, caip2ToNetwork, calculateValidBefore, checkFacilitatorSupport, combineSignature as combineSignatureV2, createEIP712Domain, createError, createNonce, createPaymentRequiredHeaders, createPaymentRequirements, createSettlementHeaders, createTransferWithAuthorization, decodeBase64ToUtf8, decodePayloadHeader, decodePayment, decodePaymentV2, decodeSettlementResponse, decodeSettlementV2, decodeX402Response, detectPaymentVersion, detectX402Version, encodePayment, encodePaymentV2, encodeSettlementResponse, encodeSettlementV2, encodeUtf8ToBase64, encodeX402Response, extractPayerAddress, extractPaymentFromHeaders, findMatchingRoute, findRequirementByAccepted, findRequirementByNetwork, fromAtomicUnits, generateNonce, getAllCustomTokens, getAllTokens, getAvailableNetworks, getAvailableTokens, getCurrentTimestamp, getCustomToken, getEURCTokens, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getSKLTokens, getSupported, getTestnets, getToken, getTokensByChain, getTokensBySymbol, getTxHash, getUSDCTokens, getUSDTTokens, getWBTCTokens, getWETHTokens, isAddress2 as isAddress, isCAIP2ChainId, isCAIPAssetId, isCustomToken, isExactEvmPayload, isPaymentPayload, isPaymentPayloadV2, isPaymentRequiredV2, isPaymentV2, isResolvedNetwork, isResolvedToken, isSettlementSuccessful, isSettlementV2, isValidAddress, isValidationError, isX402V2Payload, isX402V2PaymentRequired, isX402V2Requirements, isX402V2Settlement, matchRoute, networkToCaip2, normalizeAddress, normalizeBase64Url, normalizeNetworkName, parseJsonOrBase64, parseRoutePattern, parseSignature as parseSignatureV2, registerToken, resolveFacilitator, resolveNetwork, resolveToken, runAfterPaymentResponseHooks, runBeforeSignPaymentHooks, runOnPaymentRequiredHooks, safeBase64Decode2 as safeBase64Decode, safeBase64Encode2 as safeBase64Encode, selectRequirementWithHooks, settlePayment, toAtomicUnits, toBase64Url, unregisterToken, validateAcceptConfig, validatePaymentConfig, validateRouteConfig, validateTransferWithAuthorization, verifyPayment };
|
|
1872
|
+
export { EIP712_TYPES, ERC20_ABI, EURC_BASE, NETWORKS, PAYMENT_REQUIRED_HEADER, PAYMENT_RESPONSE_HEADER, PAYMENT_SIGNATURE_HEADER, PaymentException, SCHEMES, SKL_SKALE_BASE, SKL_SKALE_BASE_SEPOLIA, SigningError, TOKENS, USDC_BASE, USDC_BASE_SEPOLIA, USDC_DOMAIN, USDC_SKALE_BASE, USDC_SKALE_BASE_SEPOLIA, USDT_SKALE_BASE, USDT_SKALE_BASE_SEPOLIA, V2_HEADERS, WBTC_SKALE_BASE, WBTC_SKALE_BASE_SEPOLIA, WETH_SKALE_BASE, WETH_SKALE_BASE_SEPOLIA, X402ClientError, X402_VERSION, addressToAssetId, assetIdToAddress, caip2ToNetwork, calculateValidBefore, checkFacilitatorSupport, clearFacilitatorCapabilityCache, combineSignature as combineSignatureV2, createEIP712Domain, createError, createNonce, createPaymentRequiredHeaders, createPaymentRequirements, createSettlementHeaders, createTransferWithAuthorization, decodeBase64ToUtf8, decodePayloadHeader, decodePayment, decodePaymentV2, decodeSettlementResponse, decodeSettlementV2, decodeX402Response, detectPaymentVersion, detectX402Version, encodePayment, encodePaymentV2, encodeSettlementResponse, encodeSettlementV2, encodeUtf8ToBase64, encodeX402Response, extractPayerAddress, extractPaymentFromHeaders, filterExtensionsForFacilitator, filterExtensionsForFacilitators, filterExtensionsForRequirements, findMatchingRoute, findRequirementByAccepted, findRequirementByNetwork, fromAtomicUnits, generateNonce, getAllCustomTokens, getAllTokens, getAvailableNetworks, getAvailableTokens, getCurrentTimestamp, getCustomToken, getEURCTokens, getMainnets, getNetworkByChainId, getNetworkConfig, getPaymentHeaderName, getSKLTokens, getSupported, getTestnets, getToken, getTokensByChain, getTokensBySymbol, getTxHash, getUSDCTokens, getUSDTTokens, getWBTCTokens, getWETHTokens, isAddress2 as isAddress, isCAIP2ChainId, isCAIPAssetId, isCustomToken, isExactEvmPayload, isPaymentPayload, isPaymentPayloadV2, isPaymentRequiredV2, isPaymentV2, isResolvedNetwork, isResolvedToken, isSettlementSuccessful, isSettlementV2, isValidAddress, isValidationError, isX402V2Payload, isX402V2PaymentRequired, isX402V2Requirements, isX402V2Settlement, matchRoute, networkToCaip2, normalizeAddress, normalizeBase64Url, normalizeNetworkName, parseJsonOrBase64, parseRoutePattern, parseSignature as parseSignatureV2, registerToken, resolveFacilitator, resolveFacilitatorUrlFromRequirement, resolveNetwork, resolveToken, runAfterPaymentResponseHooks, runBeforeSignPaymentHooks, runOnPaymentRequiredHooks, safeBase64Decode2 as safeBase64Decode, safeBase64Encode2 as safeBase64Encode, selectRequirementWithHooks, settlePayment, toAtomicUnits, toBase64Url, unregisterToken, validateAcceptConfig, validatePaymentConfig, validateRouteConfig, validateTransferWithAuthorization, verifyPayment };
|
|
@@ -18,6 +18,12 @@ export interface ResolvedRequirementsConfig {
|
|
|
18
18
|
requirements: PaymentRequirementsV2[];
|
|
19
19
|
error?: ValidationError;
|
|
20
20
|
}
|
|
21
|
+
export interface FacilitatorRoutingConfig {
|
|
22
|
+
facilitatorUrl?: string;
|
|
23
|
+
facilitatorUrlByChain?: Record<string, string>;
|
|
24
|
+
facilitatorUrlByToken?: Record<string, Record<string, string>>;
|
|
25
|
+
}
|
|
26
|
+
export declare function resolveFacilitatorUrlFromRequirement(config: FacilitatorRoutingConfig, requirement: Pick<PaymentRequirementsV2, "network" | "asset">): string | undefined;
|
|
21
27
|
export declare function createPaymentRequirements(config: PaymentConfig): ResolvedRequirementsConfig;
|
|
22
28
|
export declare function findRequirementByNetwork(requirements: PaymentRequirementsV2[], network: string): PaymentRequirementsV2 | undefined;
|
|
23
29
|
export declare function findRequirementByAccepted(requirements: PaymentRequirementsV2[], accepted: {
|
package/package.json
CHANGED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { getSupported } from "./payment-client";
|
|
2
|
+
import type { PaymentRequirementsV2 } from "./types/v2";
|
|
3
|
+
import type { FacilitatorRoutingConfig } from "./payment-requirements";
|
|
4
|
+
import { resolveFacilitatorUrlFromRequirement } from "./payment-requirements";
|
|
5
|
+
|
|
6
|
+
const DEFAULT_CAPABILITY_TTL_MS = 5 * 60 * 1000;
|
|
7
|
+
|
|
8
|
+
type CapabilityCacheEntry = {
|
|
9
|
+
expiresAt: number;
|
|
10
|
+
keys: Set<string>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const capabilityCache = new Map<string, CapabilityCacheEntry>();
|
|
14
|
+
|
|
15
|
+
const normalizeNetwork = (network: string): string => {
|
|
16
|
+
return network.toLowerCase();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
async function getExtensionKeysForFacilitator(
|
|
20
|
+
url: string,
|
|
21
|
+
network: string,
|
|
22
|
+
ttlMs: number,
|
|
23
|
+
): Promise<Set<string>> {
|
|
24
|
+
const cacheKey = `${url}|${normalizeNetwork(network)}`;
|
|
25
|
+
const now = Date.now();
|
|
26
|
+
const cached = capabilityCache.get(cacheKey);
|
|
27
|
+
if (cached && cached.expiresAt > now) {
|
|
28
|
+
return cached.keys;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
const supported = await getSupported({ url });
|
|
33
|
+
const extensionKeys = new Set<string>();
|
|
34
|
+
for (const kind of supported.kinds) {
|
|
35
|
+
if (normalizeNetwork(kind.network) !== normalizeNetwork(network)) {
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
if (kind.extra && typeof kind.extra === "object") {
|
|
39
|
+
for (const key of Object.keys(kind.extra)) {
|
|
40
|
+
extensionKeys.add(key);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
capabilityCache.set(cacheKey, {
|
|
45
|
+
expiresAt: now + ttlMs,
|
|
46
|
+
keys: extensionKeys,
|
|
47
|
+
});
|
|
48
|
+
return extensionKeys;
|
|
49
|
+
} catch {
|
|
50
|
+
capabilityCache.set(cacheKey, {
|
|
51
|
+
expiresAt: now + ttlMs,
|
|
52
|
+
keys: new Set<string>(),
|
|
53
|
+
});
|
|
54
|
+
return new Set<string>();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function filterExtensionsForFacilitator(
|
|
59
|
+
extensions: Record<string, unknown> | undefined,
|
|
60
|
+
facilitatorUrl: string | undefined,
|
|
61
|
+
network: string,
|
|
62
|
+
ttlMs: number = DEFAULT_CAPABILITY_TTL_MS,
|
|
63
|
+
): Promise<Record<string, unknown>> {
|
|
64
|
+
if (!extensions || !facilitatorUrl) {
|
|
65
|
+
return extensions ?? {};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const supportedKeys = await getExtensionKeysForFacilitator(
|
|
69
|
+
facilitatorUrl,
|
|
70
|
+
network,
|
|
71
|
+
ttlMs,
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
const filtered: Record<string, unknown> = {};
|
|
75
|
+
for (const [key, value] of Object.entries(extensions)) {
|
|
76
|
+
if (supportedKeys.has(key)) {
|
|
77
|
+
filtered[key] = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return filtered;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function filterExtensionsForFacilitators(
|
|
85
|
+
extensions: Record<string, unknown> | undefined,
|
|
86
|
+
facilitators: Array<{ url?: string; network: string }>,
|
|
87
|
+
ttlMs: number = DEFAULT_CAPABILITY_TTL_MS,
|
|
88
|
+
): Promise<Record<string, unknown>> {
|
|
89
|
+
if (!extensions) {
|
|
90
|
+
return {};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let filtered = { ...extensions };
|
|
94
|
+
for (const facilitator of facilitators) {
|
|
95
|
+
filtered = await filterExtensionsForFacilitator(
|
|
96
|
+
filtered,
|
|
97
|
+
facilitator.url,
|
|
98
|
+
facilitator.network,
|
|
99
|
+
ttlMs,
|
|
100
|
+
);
|
|
101
|
+
if (Object.keys(filtered).length === 0) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return filtered;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export async function filterExtensionsForRequirements(
|
|
110
|
+
extensions: Record<string, unknown> | undefined,
|
|
111
|
+
requirements: PaymentRequirementsV2[],
|
|
112
|
+
config: FacilitatorRoutingConfig,
|
|
113
|
+
ttlMs: number = DEFAULT_CAPABILITY_TTL_MS,
|
|
114
|
+
): Promise<Record<string, unknown>> {
|
|
115
|
+
const facilitators = requirements.map((requirement) => ({
|
|
116
|
+
url: resolveFacilitatorUrlFromRequirement(config, requirement),
|
|
117
|
+
network: requirement.network,
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
return filterExtensionsForFacilitators(extensions, facilitators, ttlMs);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function clearFacilitatorCapabilityCache(): void {
|
|
124
|
+
capabilityCache.clear();
|
|
125
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -116,6 +116,12 @@ export {
|
|
|
116
116
|
settlePayment,
|
|
117
117
|
verifyPayment,
|
|
118
118
|
} from "./payment-client";
|
|
119
|
+
export {
|
|
120
|
+
clearFacilitatorCapabilityCache,
|
|
121
|
+
filterExtensionsForFacilitator,
|
|
122
|
+
filterExtensionsForFacilitators,
|
|
123
|
+
filterExtensionsForRequirements,
|
|
124
|
+
} from "./facilitator-capabilities";
|
|
119
125
|
export type {
|
|
120
126
|
PaymentConfig,
|
|
121
127
|
ResolvedRequirementsConfig,
|
|
@@ -124,6 +130,7 @@ export {
|
|
|
124
130
|
createPaymentRequirements,
|
|
125
131
|
findRequirementByAccepted,
|
|
126
132
|
findRequirementByNetwork,
|
|
133
|
+
resolveFacilitatorUrlFromRequirement,
|
|
127
134
|
} from "./payment-requirements";
|
|
128
135
|
// ============================================
|
|
129
136
|
// Protocol utilities (shared across all client packages)
|
|
@@ -36,6 +36,12 @@ export interface ResolvedRequirementsConfig {
|
|
|
36
36
|
error?: ValidationError;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
export interface FacilitatorRoutingConfig {
|
|
40
|
+
facilitatorUrl?: string;
|
|
41
|
+
facilitatorUrlByChain?: Record<string, string>;
|
|
42
|
+
facilitatorUrlByToken?: Record<string, Record<string, string>>;
|
|
43
|
+
}
|
|
44
|
+
|
|
39
45
|
const DEFAULT_NETWORKS: NetworkId[] = [
|
|
40
46
|
"ethereum",
|
|
41
47
|
"base",
|
|
@@ -141,6 +147,54 @@ function resolveFacilitatorUrl(
|
|
|
141
147
|
return config.facilitatorUrl;
|
|
142
148
|
}
|
|
143
149
|
|
|
150
|
+
export function resolveFacilitatorUrlFromRequirement(
|
|
151
|
+
config: FacilitatorRoutingConfig,
|
|
152
|
+
requirement: Pick<PaymentRequirementsV2, "network" | "asset">,
|
|
153
|
+
): string | undefined {
|
|
154
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
155
|
+
const assetAddress = requirement.asset.toLowerCase();
|
|
156
|
+
|
|
157
|
+
if (config.facilitatorUrlByToken) {
|
|
158
|
+
for (const [chainKey, tokenMap] of Object.entries(
|
|
159
|
+
config.facilitatorUrlByToken,
|
|
160
|
+
)) {
|
|
161
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
162
|
+
if (
|
|
163
|
+
isValidationError(resolvedChain) ||
|
|
164
|
+
resolvedChain.config.chainId !== chainId
|
|
165
|
+
) {
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
for (const [tokenKey, url] of Object.entries(tokenMap)) {
|
|
170
|
+
const resolvedToken = resolveToken(tokenKey, resolvedChain);
|
|
171
|
+
if (
|
|
172
|
+
!isValidationError(resolvedToken) &&
|
|
173
|
+
resolvedToken.config.contractAddress.toLowerCase() === assetAddress
|
|
174
|
+
) {
|
|
175
|
+
return url;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (config.facilitatorUrlByChain) {
|
|
182
|
+
for (const [chainKey, url] of Object.entries(
|
|
183
|
+
config.facilitatorUrlByChain,
|
|
184
|
+
)) {
|
|
185
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
186
|
+
if (
|
|
187
|
+
!isValidationError(resolvedChain) &&
|
|
188
|
+
resolvedChain.config.chainId === chainId
|
|
189
|
+
) {
|
|
190
|
+
return url;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return config.facilitatorUrl;
|
|
196
|
+
}
|
|
197
|
+
|
|
144
198
|
function resolveNetworks(chainInputs: NetworkId[] | undefined): {
|
|
145
199
|
networks: ResolvedNetwork[];
|
|
146
200
|
error?: ValidationError;
|