@armory-sh/base 0.2.29 → 0.2.31
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 +163 -1
- package/dist/payment-requirements.d.ts +8 -0
- package/package.json +1 -1
- package/src/facilitator-capabilities.ts +125 -0
- package/src/index.ts +9 -0
- package/src/payment-requirements.ts +129 -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, enrichPaymentRequirement, enrichPaymentRequirements, 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
|
@@ -1380,6 +1380,50 @@ var DEFAULT_TOKENS = ["usdc"];
|
|
|
1380
1380
|
var isValidationError2 = (value) => {
|
|
1381
1381
|
return typeof value === "object" && value !== null && "code" in value;
|
|
1382
1382
|
};
|
|
1383
|
+
var coerceMetadata = (requirement) => {
|
|
1384
|
+
if (typeof requirement.name === "string" && typeof requirement.version === "string") {
|
|
1385
|
+
return { name: requirement.name, version: requirement.version };
|
|
1386
|
+
}
|
|
1387
|
+
const extraName = requirement.extra && typeof requirement.extra === "object" && typeof requirement.extra.name === "string" ? requirement.extra.name : void 0;
|
|
1388
|
+
const extraVersion = requirement.extra && typeof requirement.extra === "object" && typeof requirement.extra.version === "string" ? requirement.extra.version : void 0;
|
|
1389
|
+
return { name: extraName, version: extraVersion };
|
|
1390
|
+
};
|
|
1391
|
+
var resolveTokenMetadata = (requirement) => {
|
|
1392
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
1393
|
+
if (!Number.isFinite(chainId) || chainId <= 0) {
|
|
1394
|
+
return {};
|
|
1395
|
+
}
|
|
1396
|
+
const token = getToken(chainId, requirement.asset);
|
|
1397
|
+
if (!token) {
|
|
1398
|
+
return {};
|
|
1399
|
+
}
|
|
1400
|
+
return {
|
|
1401
|
+
name: token.name,
|
|
1402
|
+
version: token.version
|
|
1403
|
+
};
|
|
1404
|
+
};
|
|
1405
|
+
function enrichPaymentRequirement(requirement) {
|
|
1406
|
+
const metadata = coerceMetadata(requirement);
|
|
1407
|
+
const fallback = resolveTokenMetadata(requirement);
|
|
1408
|
+
const name = metadata.name ?? fallback.name;
|
|
1409
|
+
const version = metadata.version ?? fallback.version;
|
|
1410
|
+
if (!name || !version) {
|
|
1411
|
+
return requirement;
|
|
1412
|
+
}
|
|
1413
|
+
return {
|
|
1414
|
+
...requirement,
|
|
1415
|
+
name,
|
|
1416
|
+
version,
|
|
1417
|
+
extra: {
|
|
1418
|
+
...requirement.extra ?? {},
|
|
1419
|
+
name,
|
|
1420
|
+
version
|
|
1421
|
+
}
|
|
1422
|
+
};
|
|
1423
|
+
}
|
|
1424
|
+
function enrichPaymentRequirements(requirements) {
|
|
1425
|
+
return requirements.map(enrichPaymentRequirement);
|
|
1426
|
+
}
|
|
1383
1427
|
function resolvePayTo(config, network, token) {
|
|
1384
1428
|
const chainId = network.config.chainId;
|
|
1385
1429
|
if (config.payToByToken) {
|
|
@@ -1434,6 +1478,37 @@ function resolveFacilitatorUrl(config, network, token) {
|
|
|
1434
1478
|
}
|
|
1435
1479
|
return config.facilitatorUrl;
|
|
1436
1480
|
}
|
|
1481
|
+
function resolveFacilitatorUrlFromRequirement(config, requirement) {
|
|
1482
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
1483
|
+
const assetAddress = requirement.asset.toLowerCase();
|
|
1484
|
+
if (config.facilitatorUrlByToken) {
|
|
1485
|
+
for (const [chainKey, tokenMap] of Object.entries(
|
|
1486
|
+
config.facilitatorUrlByToken
|
|
1487
|
+
)) {
|
|
1488
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
1489
|
+
if (isValidationError2(resolvedChain) || resolvedChain.config.chainId !== chainId) {
|
|
1490
|
+
continue;
|
|
1491
|
+
}
|
|
1492
|
+
for (const [tokenKey2, url] of Object.entries(tokenMap)) {
|
|
1493
|
+
const resolvedToken = resolveToken(tokenKey2, resolvedChain);
|
|
1494
|
+
if (!isValidationError2(resolvedToken) && resolvedToken.config.contractAddress.toLowerCase() === assetAddress) {
|
|
1495
|
+
return url;
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
if (config.facilitatorUrlByChain) {
|
|
1501
|
+
for (const [chainKey, url] of Object.entries(
|
|
1502
|
+
config.facilitatorUrlByChain
|
|
1503
|
+
)) {
|
|
1504
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
1505
|
+
if (!isValidationError2(resolvedChain) && resolvedChain.config.chainId === chainId) {
|
|
1506
|
+
return url;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
return config.facilitatorUrl;
|
|
1511
|
+
}
|
|
1437
1512
|
function resolveNetworks(chainInputs) {
|
|
1438
1513
|
const resolvedNetworks = [];
|
|
1439
1514
|
const errors = [];
|
|
@@ -1496,6 +1571,8 @@ function createPaymentRequirements(config) {
|
|
|
1496
1571
|
asset: tokenConfig.contractAddress,
|
|
1497
1572
|
payTo: resolvedPayTo,
|
|
1498
1573
|
maxTimeoutSeconds,
|
|
1574
|
+
name: tokenConfig.name,
|
|
1575
|
+
version: tokenConfig.version,
|
|
1499
1576
|
extra: {
|
|
1500
1577
|
name: tokenConfig.name,
|
|
1501
1578
|
version: tokenConfig.version
|
|
@@ -1540,6 +1617,91 @@ function findRequirementByAccepted(requirements, accepted) {
|
|
|
1540
1617
|
);
|
|
1541
1618
|
}
|
|
1542
1619
|
|
|
1620
|
+
// src/facilitator-capabilities.ts
|
|
1621
|
+
var DEFAULT_CAPABILITY_TTL_MS = 5 * 60 * 1e3;
|
|
1622
|
+
var capabilityCache = /* @__PURE__ */ new Map();
|
|
1623
|
+
var normalizeNetwork = (network) => {
|
|
1624
|
+
return network.toLowerCase();
|
|
1625
|
+
};
|
|
1626
|
+
async function getExtensionKeysForFacilitator(url, network, ttlMs) {
|
|
1627
|
+
const cacheKey = `${url}|${normalizeNetwork(network)}`;
|
|
1628
|
+
const now = Date.now();
|
|
1629
|
+
const cached = capabilityCache.get(cacheKey);
|
|
1630
|
+
if (cached && cached.expiresAt > now) {
|
|
1631
|
+
return cached.keys;
|
|
1632
|
+
}
|
|
1633
|
+
try {
|
|
1634
|
+
const supported = await getSupported({ url });
|
|
1635
|
+
const extensionKeys = /* @__PURE__ */ new Set();
|
|
1636
|
+
for (const kind of supported.kinds) {
|
|
1637
|
+
if (normalizeNetwork(kind.network) !== normalizeNetwork(network)) {
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
if (kind.extra && typeof kind.extra === "object") {
|
|
1641
|
+
for (const key of Object.keys(kind.extra)) {
|
|
1642
|
+
extensionKeys.add(key);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
capabilityCache.set(cacheKey, {
|
|
1647
|
+
expiresAt: now + ttlMs,
|
|
1648
|
+
keys: extensionKeys
|
|
1649
|
+
});
|
|
1650
|
+
return extensionKeys;
|
|
1651
|
+
} catch {
|
|
1652
|
+
capabilityCache.set(cacheKey, {
|
|
1653
|
+
expiresAt: now + ttlMs,
|
|
1654
|
+
keys: /* @__PURE__ */ new Set()
|
|
1655
|
+
});
|
|
1656
|
+
return /* @__PURE__ */ new Set();
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
async function filterExtensionsForFacilitator(extensions, facilitatorUrl, network, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1660
|
+
if (!extensions || !facilitatorUrl) {
|
|
1661
|
+
return extensions ?? {};
|
|
1662
|
+
}
|
|
1663
|
+
const supportedKeys = await getExtensionKeysForFacilitator(
|
|
1664
|
+
facilitatorUrl,
|
|
1665
|
+
network,
|
|
1666
|
+
ttlMs
|
|
1667
|
+
);
|
|
1668
|
+
const filtered = {};
|
|
1669
|
+
for (const [key, value] of Object.entries(extensions)) {
|
|
1670
|
+
if (supportedKeys.has(key)) {
|
|
1671
|
+
filtered[key] = value;
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
return filtered;
|
|
1675
|
+
}
|
|
1676
|
+
async function filterExtensionsForFacilitators(extensions, facilitators, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1677
|
+
if (!extensions) {
|
|
1678
|
+
return {};
|
|
1679
|
+
}
|
|
1680
|
+
let filtered = { ...extensions };
|
|
1681
|
+
for (const facilitator of facilitators) {
|
|
1682
|
+
filtered = await filterExtensionsForFacilitator(
|
|
1683
|
+
filtered,
|
|
1684
|
+
facilitator.url,
|
|
1685
|
+
facilitator.network,
|
|
1686
|
+
ttlMs
|
|
1687
|
+
);
|
|
1688
|
+
if (Object.keys(filtered).length === 0) {
|
|
1689
|
+
return {};
|
|
1690
|
+
}
|
|
1691
|
+
}
|
|
1692
|
+
return filtered;
|
|
1693
|
+
}
|
|
1694
|
+
async function filterExtensionsForRequirements(extensions, requirements, config, ttlMs = DEFAULT_CAPABILITY_TTL_MS) {
|
|
1695
|
+
const facilitators = requirements.map((requirement) => ({
|
|
1696
|
+
url: resolveFacilitatorUrlFromRequirement(config, requirement),
|
|
1697
|
+
network: requirement.network
|
|
1698
|
+
}));
|
|
1699
|
+
return filterExtensionsForFacilitators(extensions, facilitators, ttlMs);
|
|
1700
|
+
}
|
|
1701
|
+
function clearFacilitatorCapabilityCache() {
|
|
1702
|
+
capabilityCache.clear();
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1543
1705
|
// src/protocol.ts
|
|
1544
1706
|
function parseJsonOrBase64(value) {
|
|
1545
1707
|
try {
|
|
@@ -1753,4 +1915,4 @@ function validateRouteConfig(config) {
|
|
|
1753
1915
|
return null;
|
|
1754
1916
|
}
|
|
1755
1917
|
|
|
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 };
|
|
1918
|
+
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, enrichPaymentRequirement, enrichPaymentRequirements, 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,14 @@ 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 enrichPaymentRequirement(requirement: PaymentRequirementsV2): PaymentRequirementsV2;
|
|
27
|
+
export declare function enrichPaymentRequirements(requirements: PaymentRequirementsV2[]): PaymentRequirementsV2[];
|
|
28
|
+
export declare function resolveFacilitatorUrlFromRequirement(config: FacilitatorRoutingConfig, requirement: Pick<PaymentRequirementsV2, "network" | "asset">): string | undefined;
|
|
21
29
|
export declare function createPaymentRequirements(config: PaymentConfig): ResolvedRequirementsConfig;
|
|
22
30
|
export declare function findRequirementByNetwork(requirements: PaymentRequirementsV2[], network: string): PaymentRequirementsV2 | undefined;
|
|
23
31
|
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,14 +116,23 @@ 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,
|
|
122
128
|
} from "./payment-requirements";
|
|
123
129
|
export {
|
|
124
130
|
createPaymentRequirements,
|
|
131
|
+
enrichPaymentRequirement,
|
|
132
|
+
enrichPaymentRequirements,
|
|
125
133
|
findRequirementByAccepted,
|
|
126
134
|
findRequirementByNetwork,
|
|
135
|
+
resolveFacilitatorUrlFromRequirement,
|
|
127
136
|
} from "./payment-requirements";
|
|
128
137
|
// ============================================
|
|
129
138
|
// Protocol utilities (shared across all client packages)
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
ValidationError,
|
|
7
7
|
} from "./types/api";
|
|
8
8
|
import { getNetworkConfig } from "./types/networks";
|
|
9
|
+
import { getToken } from "./data/tokens";
|
|
9
10
|
import type { Address, PaymentRequirementsV2 } from "./types/v2";
|
|
10
11
|
import { toAtomicUnits } from "./utils/x402";
|
|
11
12
|
import {
|
|
@@ -36,6 +37,12 @@ export interface ResolvedRequirementsConfig {
|
|
|
36
37
|
error?: ValidationError;
|
|
37
38
|
}
|
|
38
39
|
|
|
40
|
+
export interface FacilitatorRoutingConfig {
|
|
41
|
+
facilitatorUrl?: string;
|
|
42
|
+
facilitatorUrlByChain?: Record<string, string>;
|
|
43
|
+
facilitatorUrlByToken?: Record<string, Record<string, string>>;
|
|
44
|
+
}
|
|
45
|
+
|
|
39
46
|
const DEFAULT_NETWORKS: NetworkId[] = [
|
|
40
47
|
"ethereum",
|
|
41
48
|
"base",
|
|
@@ -51,6 +58,78 @@ const isValidationError = (value: unknown): value is ValidationError => {
|
|
|
51
58
|
return typeof value === "object" && value !== null && "code" in value;
|
|
52
59
|
};
|
|
53
60
|
|
|
61
|
+
const coerceMetadata = (
|
|
62
|
+
requirement: PaymentRequirementsV2,
|
|
63
|
+
): { name?: string; version?: string } => {
|
|
64
|
+
if (typeof requirement.name === "string" && typeof requirement.version === "string") {
|
|
65
|
+
return { name: requirement.name, version: requirement.version };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const extraName =
|
|
69
|
+
requirement.extra &&
|
|
70
|
+
typeof requirement.extra === "object" &&
|
|
71
|
+
typeof requirement.extra.name === "string"
|
|
72
|
+
? requirement.extra.name
|
|
73
|
+
: undefined;
|
|
74
|
+
const extraVersion =
|
|
75
|
+
requirement.extra &&
|
|
76
|
+
typeof requirement.extra === "object" &&
|
|
77
|
+
typeof requirement.extra.version === "string"
|
|
78
|
+
? requirement.extra.version
|
|
79
|
+
: undefined;
|
|
80
|
+
|
|
81
|
+
return { name: extraName, version: extraVersion };
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const resolveTokenMetadata = (
|
|
85
|
+
requirement: PaymentRequirementsV2,
|
|
86
|
+
): { name?: string; version?: string } => {
|
|
87
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
88
|
+
if (!Number.isFinite(chainId) || chainId <= 0) {
|
|
89
|
+
return {};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const token = getToken(chainId, requirement.asset);
|
|
93
|
+
if (!token) {
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
name: token.name,
|
|
99
|
+
version: token.version,
|
|
100
|
+
};
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export function enrichPaymentRequirement(
|
|
104
|
+
requirement: PaymentRequirementsV2,
|
|
105
|
+
): PaymentRequirementsV2 {
|
|
106
|
+
const metadata = coerceMetadata(requirement);
|
|
107
|
+
const fallback = resolveTokenMetadata(requirement);
|
|
108
|
+
const name = metadata.name ?? fallback.name;
|
|
109
|
+
const version = metadata.version ?? fallback.version;
|
|
110
|
+
|
|
111
|
+
if (!name || !version) {
|
|
112
|
+
return requirement;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
...requirement,
|
|
117
|
+
name,
|
|
118
|
+
version,
|
|
119
|
+
extra: {
|
|
120
|
+
...(requirement.extra ?? {}),
|
|
121
|
+
name,
|
|
122
|
+
version,
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function enrichPaymentRequirements(
|
|
128
|
+
requirements: PaymentRequirementsV2[],
|
|
129
|
+
): PaymentRequirementsV2[] {
|
|
130
|
+
return requirements.map(enrichPaymentRequirement);
|
|
131
|
+
}
|
|
132
|
+
|
|
54
133
|
function resolvePayTo(
|
|
55
134
|
config: PaymentConfig,
|
|
56
135
|
network: ResolvedNetwork,
|
|
@@ -141,6 +220,54 @@ function resolveFacilitatorUrl(
|
|
|
141
220
|
return config.facilitatorUrl;
|
|
142
221
|
}
|
|
143
222
|
|
|
223
|
+
export function resolveFacilitatorUrlFromRequirement(
|
|
224
|
+
config: FacilitatorRoutingConfig,
|
|
225
|
+
requirement: Pick<PaymentRequirementsV2, "network" | "asset">,
|
|
226
|
+
): string | undefined {
|
|
227
|
+
const chainId = parseInt(requirement.network.split(":")[1] || "0", 10);
|
|
228
|
+
const assetAddress = requirement.asset.toLowerCase();
|
|
229
|
+
|
|
230
|
+
if (config.facilitatorUrlByToken) {
|
|
231
|
+
for (const [chainKey, tokenMap] of Object.entries(
|
|
232
|
+
config.facilitatorUrlByToken,
|
|
233
|
+
)) {
|
|
234
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
235
|
+
if (
|
|
236
|
+
isValidationError(resolvedChain) ||
|
|
237
|
+
resolvedChain.config.chainId !== chainId
|
|
238
|
+
) {
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
for (const [tokenKey, url] of Object.entries(tokenMap)) {
|
|
243
|
+
const resolvedToken = resolveToken(tokenKey, resolvedChain);
|
|
244
|
+
if (
|
|
245
|
+
!isValidationError(resolvedToken) &&
|
|
246
|
+
resolvedToken.config.contractAddress.toLowerCase() === assetAddress
|
|
247
|
+
) {
|
|
248
|
+
return url;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
if (config.facilitatorUrlByChain) {
|
|
255
|
+
for (const [chainKey, url] of Object.entries(
|
|
256
|
+
config.facilitatorUrlByChain,
|
|
257
|
+
)) {
|
|
258
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
259
|
+
if (
|
|
260
|
+
!isValidationError(resolvedChain) &&
|
|
261
|
+
resolvedChain.config.chainId === chainId
|
|
262
|
+
) {
|
|
263
|
+
return url;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return config.facilitatorUrl;
|
|
269
|
+
}
|
|
270
|
+
|
|
144
271
|
function resolveNetworks(chainInputs: NetworkId[] | undefined): {
|
|
145
272
|
networks: ResolvedNetwork[];
|
|
146
273
|
error?: ValidationError;
|
|
@@ -221,6 +348,8 @@ export function createPaymentRequirements(
|
|
|
221
348
|
asset: tokenConfig.contractAddress,
|
|
222
349
|
payTo: resolvedPayTo as `0x${string}`,
|
|
223
350
|
maxTimeoutSeconds,
|
|
351
|
+
name: tokenConfig.name,
|
|
352
|
+
version: tokenConfig.version,
|
|
224
353
|
extra: {
|
|
225
354
|
name: tokenConfig.name,
|
|
226
355
|
version: tokenConfig.version,
|