@armory-sh/base 0.2.28 → 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/client-hooks-runtime.d.ts +9 -0
- package/dist/encoding/x402.d.ts +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/facilitator-capabilities.d.ts +9 -0
- package/dist/index.d.ts +30 -22
- package/dist/index.js +1198 -650
- package/dist/payment-client.d.ts +1 -1
- package/dist/payment-requirements.d.ts +35 -0
- package/dist/protocol.d.ts +33 -0
- package/dist/types/api.d.ts +1 -1
- package/dist/types/hooks.d.ts +17 -1
- package/dist/types/protocol.d.ts +1 -1
- package/dist/types/wallet.d.ts +24 -0
- package/dist/utils/base64.d.ts +4 -0
- package/dist/utils/x402.d.ts +1 -1
- package/dist/validation.d.ts +1 -1
- package/package.json +15 -2
- package/src/abi/erc20.ts +84 -0
- package/src/client-hooks-runtime.ts +153 -0
- package/src/data/tokens.ts +199 -0
- package/src/eip712.ts +108 -0
- package/src/encoding/x402.ts +205 -0
- package/src/encoding.ts +98 -0
- package/src/errors.ts +23 -0
- package/src/facilitator-capabilities.ts +125 -0
- package/src/index.ts +330 -0
- package/src/payment-client.ts +201 -0
- package/src/payment-requirements.ts +354 -0
- package/src/protocol.ts +57 -0
- package/src/types/api.ts +304 -0
- package/src/types/hooks.ts +85 -0
- package/src/types/networks.ts +175 -0
- package/src/types/protocol.ts +182 -0
- package/src/types/v2.ts +282 -0
- package/src/types/wallet.ts +30 -0
- package/src/types/x402.ts +151 -0
- package/src/utils/base64.ts +48 -0
- package/src/utils/routes.ts +240 -0
- package/src/utils/utils/index.ts +7 -0
- package/src/utils/utils/mock-facilitator.ts +184 -0
- package/src/utils/x402.ts +147 -0
- package/src/validation.ts +654 -0
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NetworkId,
|
|
3
|
+
ResolvedNetwork,
|
|
4
|
+
ResolvedToken,
|
|
5
|
+
TokenId,
|
|
6
|
+
ValidationError,
|
|
7
|
+
} from "./types/api";
|
|
8
|
+
import { getNetworkConfig } from "./types/networks";
|
|
9
|
+
import type { Address, PaymentRequirementsV2 } from "./types/v2";
|
|
10
|
+
import { toAtomicUnits } from "./utils/x402";
|
|
11
|
+
import {
|
|
12
|
+
normalizeNetworkName,
|
|
13
|
+
resolveNetwork,
|
|
14
|
+
resolveToken,
|
|
15
|
+
} from "./validation";
|
|
16
|
+
|
|
17
|
+
export interface PaymentConfig {
|
|
18
|
+
payTo: Address | string;
|
|
19
|
+
chains?: NetworkId[];
|
|
20
|
+
chain?: NetworkId;
|
|
21
|
+
tokens?: TokenId[];
|
|
22
|
+
token?: TokenId;
|
|
23
|
+
amount?: string;
|
|
24
|
+
maxTimeoutSeconds?: number;
|
|
25
|
+
|
|
26
|
+
payToByChain?: Record<string, Address | string>;
|
|
27
|
+
payToByToken?: Record<string, Record<string, Address | string>>;
|
|
28
|
+
|
|
29
|
+
facilitatorUrl?: string;
|
|
30
|
+
facilitatorUrlByChain?: Record<string, string>;
|
|
31
|
+
facilitatorUrlByToken?: Record<string, Record<string, string>>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ResolvedRequirementsConfig {
|
|
35
|
+
requirements: PaymentRequirementsV2[];
|
|
36
|
+
error?: ValidationError;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface FacilitatorRoutingConfig {
|
|
40
|
+
facilitatorUrl?: string;
|
|
41
|
+
facilitatorUrlByChain?: Record<string, string>;
|
|
42
|
+
facilitatorUrlByToken?: Record<string, Record<string, string>>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const DEFAULT_NETWORKS: NetworkId[] = [
|
|
46
|
+
"ethereum",
|
|
47
|
+
"base",
|
|
48
|
+
"base-sepolia",
|
|
49
|
+
"skale-base",
|
|
50
|
+
"skale-base-sepolia",
|
|
51
|
+
"ethereum-sepolia",
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
const DEFAULT_TOKENS: TokenId[] = ["usdc"];
|
|
55
|
+
|
|
56
|
+
const isValidationError = (value: unknown): value is ValidationError => {
|
|
57
|
+
return typeof value === "object" && value !== null && "code" in value;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
function resolvePayTo(
|
|
61
|
+
config: PaymentConfig,
|
|
62
|
+
network: ResolvedNetwork,
|
|
63
|
+
token: ResolvedToken,
|
|
64
|
+
): Address | string {
|
|
65
|
+
const chainId = network.config.chainId;
|
|
66
|
+
|
|
67
|
+
if (config.payToByToken) {
|
|
68
|
+
for (const [chainKey, tokenMap] of Object.entries(config.payToByToken)) {
|
|
69
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
70
|
+
if (
|
|
71
|
+
!isValidationError(resolvedChain) &&
|
|
72
|
+
resolvedChain.config.chainId === chainId
|
|
73
|
+
) {
|
|
74
|
+
for (const [tokenKey, address] of Object.entries(tokenMap)) {
|
|
75
|
+
const resolvedToken = resolveToken(tokenKey, network);
|
|
76
|
+
if (
|
|
77
|
+
!isValidationError(resolvedToken) &&
|
|
78
|
+
resolvedToken.config.contractAddress.toLowerCase() ===
|
|
79
|
+
token.config.contractAddress.toLowerCase()
|
|
80
|
+
) {
|
|
81
|
+
return address;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (config.payToByChain) {
|
|
89
|
+
for (const [chainKey, address] of Object.entries(config.payToByChain)) {
|
|
90
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
91
|
+
if (
|
|
92
|
+
!isValidationError(resolvedChain) &&
|
|
93
|
+
resolvedChain.config.chainId === chainId
|
|
94
|
+
) {
|
|
95
|
+
return address;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return config.payTo;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function resolveFacilitatorUrl(
|
|
104
|
+
config: PaymentConfig,
|
|
105
|
+
network: ResolvedNetwork,
|
|
106
|
+
token: ResolvedToken,
|
|
107
|
+
): string | undefined {
|
|
108
|
+
const chainId = network.config.chainId;
|
|
109
|
+
|
|
110
|
+
if (config.facilitatorUrlByToken) {
|
|
111
|
+
for (const [chainKey, tokenMap] of Object.entries(
|
|
112
|
+
config.facilitatorUrlByToken,
|
|
113
|
+
)) {
|
|
114
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
115
|
+
if (
|
|
116
|
+
!isValidationError(resolvedChain) &&
|
|
117
|
+
resolvedChain.config.chainId === chainId
|
|
118
|
+
) {
|
|
119
|
+
for (const [tokenKey, url] of Object.entries(tokenMap)) {
|
|
120
|
+
const resolvedToken = resolveToken(tokenKey, network);
|
|
121
|
+
if (
|
|
122
|
+
!isValidationError(resolvedToken) &&
|
|
123
|
+
resolvedToken.config.contractAddress.toLowerCase() ===
|
|
124
|
+
token.config.contractAddress.toLowerCase()
|
|
125
|
+
) {
|
|
126
|
+
return url;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (config.facilitatorUrlByChain) {
|
|
134
|
+
for (const [chainKey, url] of Object.entries(
|
|
135
|
+
config.facilitatorUrlByChain,
|
|
136
|
+
)) {
|
|
137
|
+
const resolvedChain = resolveNetwork(chainKey);
|
|
138
|
+
if (
|
|
139
|
+
!isValidationError(resolvedChain) &&
|
|
140
|
+
resolvedChain.config.chainId === chainId
|
|
141
|
+
) {
|
|
142
|
+
return url;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return config.facilitatorUrl;
|
|
148
|
+
}
|
|
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
|
+
|
|
198
|
+
function resolveNetworks(chainInputs: NetworkId[] | undefined): {
|
|
199
|
+
networks: ResolvedNetwork[];
|
|
200
|
+
error?: ValidationError;
|
|
201
|
+
} {
|
|
202
|
+
const resolvedNetworks: ResolvedNetwork[] = [];
|
|
203
|
+
const errors: string[] = [];
|
|
204
|
+
|
|
205
|
+
const networks = chainInputs?.length ? chainInputs : DEFAULT_NETWORKS;
|
|
206
|
+
|
|
207
|
+
for (const networkId of networks) {
|
|
208
|
+
const resolved = resolveNetwork(networkId);
|
|
209
|
+
if (isValidationError(resolved)) {
|
|
210
|
+
errors.push(`Network "${networkId}": ${resolved.message}`);
|
|
211
|
+
} else {
|
|
212
|
+
resolvedNetworks.push(resolved);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (errors.length > 0) {
|
|
217
|
+
return {
|
|
218
|
+
networks: resolvedNetworks,
|
|
219
|
+
error: {
|
|
220
|
+
code: "VALIDATION_FAILED",
|
|
221
|
+
message: errors.join("; "),
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return { networks: resolvedNetworks };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function createPaymentRequirements(
|
|
230
|
+
config: PaymentConfig,
|
|
231
|
+
): ResolvedRequirementsConfig {
|
|
232
|
+
const {
|
|
233
|
+
payTo,
|
|
234
|
+
chains,
|
|
235
|
+
chain,
|
|
236
|
+
tokens,
|
|
237
|
+
token,
|
|
238
|
+
amount = "1.0",
|
|
239
|
+
maxTimeoutSeconds = 300,
|
|
240
|
+
} = config;
|
|
241
|
+
|
|
242
|
+
const chainInputs = chain ? [chain] : chains;
|
|
243
|
+
const tokenInputs = token ? [token] : tokens;
|
|
244
|
+
|
|
245
|
+
const { networks, error: networksError } = resolveNetworks(chainInputs);
|
|
246
|
+
if (networksError) {
|
|
247
|
+
return { requirements: [], error: networksError };
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const requirements: PaymentRequirementsV2[] = [];
|
|
251
|
+
|
|
252
|
+
for (const network of networks) {
|
|
253
|
+
const tokensToResolve = tokenInputs?.length ? tokenInputs : DEFAULT_TOKENS;
|
|
254
|
+
|
|
255
|
+
for (const tokenId of tokensToResolve) {
|
|
256
|
+
const resolvedToken = resolveToken(tokenId, network);
|
|
257
|
+
if (isValidationError(resolvedToken)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const atomicAmount = toAtomicUnits(amount);
|
|
262
|
+
const tokenConfig = resolvedToken.config;
|
|
263
|
+
|
|
264
|
+
const resolvedPayTo = resolvePayTo(config, network, resolvedToken);
|
|
265
|
+
const resolvedFacilitatorUrl = resolveFacilitatorUrl(
|
|
266
|
+
config,
|
|
267
|
+
network,
|
|
268
|
+
resolvedToken,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
const requirement: PaymentRequirementsV2 = {
|
|
272
|
+
scheme: "exact",
|
|
273
|
+
network: network.caip2,
|
|
274
|
+
amount: atomicAmount,
|
|
275
|
+
asset: tokenConfig.contractAddress,
|
|
276
|
+
payTo: resolvedPayTo as `0x${string}`,
|
|
277
|
+
maxTimeoutSeconds,
|
|
278
|
+
extra: {
|
|
279
|
+
name: tokenConfig.name,
|
|
280
|
+
version: tokenConfig.version,
|
|
281
|
+
},
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
requirements.push(requirement);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
if (requirements.length === 0) {
|
|
289
|
+
return {
|
|
290
|
+
requirements: [],
|
|
291
|
+
error: {
|
|
292
|
+
code: "VALIDATION_FAILED",
|
|
293
|
+
message: "No valid network/token combinations found",
|
|
294
|
+
},
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return { requirements };
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function findRequirementByNetwork(
|
|
302
|
+
requirements: PaymentRequirementsV2[],
|
|
303
|
+
network: string,
|
|
304
|
+
): PaymentRequirementsV2 | undefined {
|
|
305
|
+
const normalized = normalizeNetworkName(network);
|
|
306
|
+
const netConfig = getNetworkConfig(normalized);
|
|
307
|
+
if (!netConfig) return undefined;
|
|
308
|
+
|
|
309
|
+
return requirements.find((r) => r.network === netConfig.caip2Id);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function findRequirementByAccepted(
|
|
313
|
+
requirements: PaymentRequirementsV2[],
|
|
314
|
+
accepted: {
|
|
315
|
+
scheme: string;
|
|
316
|
+
network: string;
|
|
317
|
+
amount: string;
|
|
318
|
+
asset: string;
|
|
319
|
+
payTo: string;
|
|
320
|
+
},
|
|
321
|
+
): PaymentRequirementsV2 | undefined {
|
|
322
|
+
const acceptedPayTo =
|
|
323
|
+
typeof accepted.payTo === "string" ? accepted.payTo.toLowerCase() : "";
|
|
324
|
+
const exactMatch = requirements.find(
|
|
325
|
+
(requirement) =>
|
|
326
|
+
requirement.scheme === accepted.scheme &&
|
|
327
|
+
requirement.network === accepted.network &&
|
|
328
|
+
requirement.amount === accepted.amount &&
|
|
329
|
+
requirement.asset.toLowerCase() === accepted.asset.toLowerCase() &&
|
|
330
|
+
typeof requirement.payTo === "string" &&
|
|
331
|
+
requirement.payTo.toLowerCase() === acceptedPayTo,
|
|
332
|
+
);
|
|
333
|
+
if (exactMatch) {
|
|
334
|
+
return exactMatch;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const payToAwareMatch = requirements.find(
|
|
338
|
+
(requirement) =>
|
|
339
|
+
requirement.scheme === accepted.scheme &&
|
|
340
|
+
requirement.network === accepted.network &&
|
|
341
|
+
acceptedPayTo.length > 0 &&
|
|
342
|
+
typeof requirement.payTo === "string" &&
|
|
343
|
+
requirement.payTo.toLowerCase() === acceptedPayTo,
|
|
344
|
+
);
|
|
345
|
+
if (payToAwareMatch) {
|
|
346
|
+
return payToAwareMatch;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return requirements.find(
|
|
350
|
+
(requirement) =>
|
|
351
|
+
requirement.scheme === accepted.scheme &&
|
|
352
|
+
requirement.network === accepted.network,
|
|
353
|
+
);
|
|
354
|
+
}
|
package/src/protocol.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* X402 Protocol Utilities (V2 Only)
|
|
3
|
+
*
|
|
4
|
+
* Shared protocol utilities for x402 client implementations.
|
|
5
|
+
* These functions are protocol-level and don't depend on wallet libraries.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { V2_HEADERS } from "./types/v2";
|
|
9
|
+
import { decodeBase64ToUtf8, normalizeBase64Url } from "./utils/base64";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Parse JSON or Base64-encoded JSON
|
|
13
|
+
* Handles both raw JSON and Base64URL-encoded JSON
|
|
14
|
+
*/
|
|
15
|
+
export function parseJsonOrBase64(value: string): unknown {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(value);
|
|
18
|
+
} catch {
|
|
19
|
+
const normalized = normalizeBase64Url(value);
|
|
20
|
+
return JSON.parse(decodeBase64ToUtf8(normalized));
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Detect x402 protocol version from response
|
|
26
|
+
* V2-only: Always returns 2
|
|
27
|
+
*/
|
|
28
|
+
export function detectX402Version(_response: Response): 2 {
|
|
29
|
+
return 2;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Get payment header name for protocol version
|
|
34
|
+
* V2-only: Returns PAYMENT-SIGNATURE header name
|
|
35
|
+
*/
|
|
36
|
+
export function getPaymentHeaderName(_version: 2): string {
|
|
37
|
+
return V2_HEADERS.PAYMENT_SIGNATURE;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate a timestamp-based nonce as hex string
|
|
42
|
+
* Matches Coinbase SDK format: "0x" + 64 hex characters
|
|
43
|
+
* Uses timestamp for reproducibility (vs random)
|
|
44
|
+
*/
|
|
45
|
+
export function generateNonce(): `0x${string}` {
|
|
46
|
+
const now = Math.floor(Date.now() / 1000);
|
|
47
|
+
return `0x${(now * 1000).toString(16).padStart(64, "0")}` as `0x${string}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Calculate expiry timestamp
|
|
52
|
+
* @param expirySeconds - Seconds from now (default: 3600 = 1 hour)
|
|
53
|
+
* @returns Unix timestamp
|
|
54
|
+
*/
|
|
55
|
+
export function calculateValidBefore(expirySeconds: number = 3600): number {
|
|
56
|
+
return Math.floor(Date.now() / 1000) + expirySeconds;
|
|
57
|
+
}
|
package/src/types/api.ts
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simple, developer-friendly API types for Armory
|
|
3
|
+
* Focuses on DX/UX - "everything just magically works"
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CustomToken, NetworkConfig } from "./networks";
|
|
7
|
+
import type { Address, CAIPAssetId } from "./v2";
|
|
8
|
+
|
|
9
|
+
// ═══════════════════════════════════════════════════════════════
|
|
10
|
+
// Simple API Types
|
|
11
|
+
// ═══════════════════════════════════════════════════════════════
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Network identifier - flexible input that resolves to a chain
|
|
15
|
+
* - Network name: "base", "ethereum", "skale-base"
|
|
16
|
+
* - CAIP-2: "eip155:8453"
|
|
17
|
+
* - Chain ID: 8453
|
|
18
|
+
*/
|
|
19
|
+
export type NetworkId = string | number;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Token identifier - flexible input that resolves to a token
|
|
23
|
+
* - Symbol: "usdc", "eurc", "usdt" (case-insensitive)
|
|
24
|
+
* - CAIP Asset ID: "eip155:8453/erc20:0x..."
|
|
25
|
+
* - Full token config object
|
|
26
|
+
*/
|
|
27
|
+
export type TokenId = string | CustomToken;
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Facilitator configuration
|
|
31
|
+
*/
|
|
32
|
+
export interface FacilitatorConfig {
|
|
33
|
+
/** Facilitator URL for verification/settlement */
|
|
34
|
+
url: string;
|
|
35
|
+
/** Optional authentication headers */
|
|
36
|
+
headers?: () => Record<string, string>;
|
|
37
|
+
/** Networks this facilitator supports (auto-detected if not provided) */
|
|
38
|
+
networks?: NetworkId[];
|
|
39
|
+
/** Tokens this facilitator supports (optional) */
|
|
40
|
+
tokens?: TokenId[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Result from facilitator verification
|
|
45
|
+
*/
|
|
46
|
+
export interface FacilitatorVerifyResult {
|
|
47
|
+
success: boolean;
|
|
48
|
+
payerAddress?: string;
|
|
49
|
+
balance?: string;
|
|
50
|
+
requiredAmount?: string;
|
|
51
|
+
error?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Result from facilitator settlement
|
|
56
|
+
*/
|
|
57
|
+
export interface FacilitatorSettleResult {
|
|
58
|
+
success: boolean;
|
|
59
|
+
txHash?: string;
|
|
60
|
+
error?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Settlement mode - verify only, settle only, or both
|
|
65
|
+
*/
|
|
66
|
+
export type SettlementMode = "verify" | "settle" | "async";
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* CAIP-2 chain ID type (e.g., eip155:8453)
|
|
70
|
+
*/
|
|
71
|
+
export type CAIP2ChainId = `eip155:${string}`;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* CAIP-2 asset ID type (e.g., eip155:8453/erc20:0xa0b8691...)
|
|
75
|
+
*/
|
|
76
|
+
export type CAIP2AssetId = `eip155:${string}/erc20:${string}`;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Payment destination - address, CAIP-2 chain ID, or CAIP asset ID
|
|
80
|
+
*/
|
|
81
|
+
export type PayToAddress = `0x${string}` | CAIP2ChainId | CAIP2AssetId;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Pricing configuration for a specific network/token/facilitator combination
|
|
85
|
+
*/
|
|
86
|
+
export interface PricingConfig {
|
|
87
|
+
/** Network for this pricing */
|
|
88
|
+
network?: NetworkId;
|
|
89
|
+
/** Token for this pricing */
|
|
90
|
+
token?: TokenId;
|
|
91
|
+
/** Facilitator URL for this pricing (optional - applies to all facilitators if not specified) */
|
|
92
|
+
facilitator?: string;
|
|
93
|
+
/** Amount to charge */
|
|
94
|
+
amount: string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Payment accept options for merchants (v2 multi-support)
|
|
99
|
+
*/
|
|
100
|
+
export interface AcceptPaymentOptions {
|
|
101
|
+
/** Networks to accept payments on */
|
|
102
|
+
networks?: NetworkId[];
|
|
103
|
+
/** Tokens to accept (defaults to ["usdc"]) */
|
|
104
|
+
tokens?: TokenId[];
|
|
105
|
+
/** Facilitator(s) for payment verification/settlement */
|
|
106
|
+
facilitators?: FacilitatorConfig | FacilitatorConfig[];
|
|
107
|
+
/** Protocol version (V2 only) */
|
|
108
|
+
version?: 2;
|
|
109
|
+
/** Pricing configurations - if not provided, uses amount from top-level config */
|
|
110
|
+
pricing?: PricingConfig[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Result of a payment attempt
|
|
115
|
+
*/
|
|
116
|
+
export interface PaymentResult<T = unknown> {
|
|
117
|
+
/** Payment successful */
|
|
118
|
+
success: true;
|
|
119
|
+
/** Response data */
|
|
120
|
+
data: T;
|
|
121
|
+
/** Transaction hash (if settled) */
|
|
122
|
+
txHash?: string;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Payment error result
|
|
127
|
+
*/
|
|
128
|
+
export interface PaymentError {
|
|
129
|
+
/** Payment failed */
|
|
130
|
+
success: false;
|
|
131
|
+
/** Error code */
|
|
132
|
+
code: PaymentErrorCode;
|
|
133
|
+
/** Human-readable error message */
|
|
134
|
+
message: string;
|
|
135
|
+
/** Underlying error details */
|
|
136
|
+
details?: unknown;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Payment error codes
|
|
141
|
+
*/
|
|
142
|
+
export type PaymentErrorCode =
|
|
143
|
+
| "UNKNOWN_NETWORK"
|
|
144
|
+
| "UNKNOWN_TOKEN"
|
|
145
|
+
| "TOKEN_NOT_ON_NETWORK"
|
|
146
|
+
| "FACILITATOR_UNSUPPORTED"
|
|
147
|
+
| "FACILITATOR_NO_NETWORK_SUPPORT"
|
|
148
|
+
| "FACILITATOR_NO_TOKEN_SUPPORT"
|
|
149
|
+
| "INVALID_CAIP_FORMAT"
|
|
150
|
+
| "CHAIN_MISMATCH"
|
|
151
|
+
| "PAYMENT_REQUIRED"
|
|
152
|
+
| "PAYMENT_DECLINED"
|
|
153
|
+
| "SIGNING_FAILED"
|
|
154
|
+
| "NETWORK_ERROR"
|
|
155
|
+
| "VALIDATION_FAILED";
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Combined payment result type
|
|
159
|
+
*/
|
|
160
|
+
export type ArmoryPaymentResult<T = unknown> = PaymentResult<T> | PaymentError;
|
|
161
|
+
|
|
162
|
+
// ═══════════════════════════════════════════════════════════════
|
|
163
|
+
// Resolved Configuration (internal use)
|
|
164
|
+
// ═══════════════════════════════════════════════════════════════
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Fully resolved network configuration
|
|
168
|
+
*/
|
|
169
|
+
export interface ResolvedNetwork {
|
|
170
|
+
/** Original input */
|
|
171
|
+
input: NetworkId;
|
|
172
|
+
/** Network config */
|
|
173
|
+
config: NetworkConfig;
|
|
174
|
+
/** CAIP-2 ID */
|
|
175
|
+
caip2: `eip155:${string}`;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Fully resolved token configuration
|
|
180
|
+
*/
|
|
181
|
+
export interface ResolvedToken {
|
|
182
|
+
/** Original input */
|
|
183
|
+
input: TokenId;
|
|
184
|
+
/** Token config */
|
|
185
|
+
config: CustomToken;
|
|
186
|
+
/** CAIP Asset ID */
|
|
187
|
+
caipAsset: CAIPAssetId;
|
|
188
|
+
/** Network this token is on */
|
|
189
|
+
network: ResolvedNetwork;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Fully resolved facilitator configuration
|
|
194
|
+
*/
|
|
195
|
+
export interface ResolvedFacilitator {
|
|
196
|
+
/** Original input */
|
|
197
|
+
input: FacilitatorConfig;
|
|
198
|
+
/** URL */
|
|
199
|
+
url: string;
|
|
200
|
+
/** Supported networks (resolved) */
|
|
201
|
+
networks: ResolvedNetwork[];
|
|
202
|
+
/** Supported tokens (resolved) */
|
|
203
|
+
tokens: ResolvedToken[];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Fully resolved payment configuration
|
|
208
|
+
*/
|
|
209
|
+
export interface ResolvedPaymentConfig {
|
|
210
|
+
/** Network to use */
|
|
211
|
+
network: ResolvedNetwork;
|
|
212
|
+
/** Token to use */
|
|
213
|
+
token: ResolvedToken;
|
|
214
|
+
/** Facilitator(s) to use */
|
|
215
|
+
facilitators: ResolvedFacilitator[];
|
|
216
|
+
/** Protocol version (V2 only) */
|
|
217
|
+
version: 2;
|
|
218
|
+
/** Recipient address */
|
|
219
|
+
payTo: Address;
|
|
220
|
+
/** Amount to charge */
|
|
221
|
+
amount: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Validation error details
|
|
226
|
+
*/
|
|
227
|
+
export interface ValidationError {
|
|
228
|
+
/** Error code */
|
|
229
|
+
code: PaymentErrorCode;
|
|
230
|
+
/** Error message */
|
|
231
|
+
message: string;
|
|
232
|
+
/** Path to the invalid value */
|
|
233
|
+
path?: string;
|
|
234
|
+
/** Invalid value */
|
|
235
|
+
value?: unknown;
|
|
236
|
+
/** Valid options */
|
|
237
|
+
validOptions?: string[];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ═══════════════════════════════════════════════════════════════
|
|
241
|
+
// Route Configuration Types
|
|
242
|
+
// ═══════════════════════════════════════════════════════════════
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Route pattern type for matching paths
|
|
246
|
+
*/
|
|
247
|
+
export type RoutePattern = string;
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Route matcher function type
|
|
251
|
+
*/
|
|
252
|
+
export type RouteMatcher = (path: string) => boolean;
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Route configuration with associated config data
|
|
256
|
+
*/
|
|
257
|
+
export interface RouteConfig<T = unknown> {
|
|
258
|
+
/** Route pattern (e.g., "/api/users", "/api/*", "/api/users/:id") */
|
|
259
|
+
pattern: RoutePattern;
|
|
260
|
+
/** Configuration associated with this route */
|
|
261
|
+
config: T;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Parsed route pattern information
|
|
266
|
+
*/
|
|
267
|
+
export interface ParsedPattern {
|
|
268
|
+
/** Route segments split by "/" */
|
|
269
|
+
segments: string[];
|
|
270
|
+
/** Whether this pattern contains a wildcard */
|
|
271
|
+
isWildcard: boolean;
|
|
272
|
+
/** Whether this pattern contains parameters (e.g., :id) */
|
|
273
|
+
isParametrized: boolean;
|
|
274
|
+
/** Parameter names extracted from the pattern */
|
|
275
|
+
paramNames: string[];
|
|
276
|
+
/** Match priority (higher = more specific) */
|
|
277
|
+
priority: number;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Route input configuration for middleware
|
|
282
|
+
*/
|
|
283
|
+
export interface RouteInputConfig {
|
|
284
|
+
/** Single exact route (no wildcards allowed) */
|
|
285
|
+
route?: string;
|
|
286
|
+
/** Multiple routes (allows wildcards) */
|
|
287
|
+
routes?: string[];
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Route-specific validation error details
|
|
292
|
+
*/
|
|
293
|
+
export interface RouteValidationError {
|
|
294
|
+
/** Error code (custom string, not PaymentErrorCode) */
|
|
295
|
+
code: string;
|
|
296
|
+
/** Error message */
|
|
297
|
+
message: string;
|
|
298
|
+
/** Path to the invalid value */
|
|
299
|
+
path?: string;
|
|
300
|
+
/** Invalid value */
|
|
301
|
+
value?: unknown;
|
|
302
|
+
/** Valid options */
|
|
303
|
+
validOptions?: string[];
|
|
304
|
+
}
|