uvd-x402-sdk 2.6.0 → 2.10.1
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 +380 -3
- package/dist/adapters/index.d.mts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +94 -1
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +94 -1
- package/dist/adapters/index.mjs.map +1 -1
- package/dist/backend/index.d.mts +1036 -0
- package/dist/backend/index.d.ts +1036 -0
- package/dist/backend/index.js +1738 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/index.mjs +1720 -0
- package/dist/backend/index.mjs.map +1 -0
- package/dist/{index-fwbSkart.d.ts → index-C60c_e5z.d.mts} +13 -4
- package/dist/{index-BR1o8JZQ.d.mts → index-D-dO_FoP.d.mts} +38 -4
- package/dist/{index-BR1o8JZQ.d.ts → index-D-dO_FoP.d.ts} +38 -4
- package/dist/{index-DKbWiaJ9.d.mts → index-VIOUicmO.d.ts} +13 -4
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +109 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +108 -2
- package/dist/index.mjs.map +1 -1
- package/dist/providers/algorand/index.d.mts +86 -0
- package/dist/providers/algorand/index.d.ts +86 -0
- package/dist/providers/algorand/index.js +919 -0
- package/dist/providers/algorand/index.js.map +1 -0
- package/dist/providers/algorand/index.mjs +914 -0
- package/dist/providers/algorand/index.mjs.map +1 -0
- package/dist/providers/evm/index.d.mts +1 -1
- package/dist/providers/evm/index.d.ts +1 -1
- package/dist/providers/evm/index.js +94 -1
- package/dist/providers/evm/index.js.map +1 -1
- package/dist/providers/evm/index.mjs +94 -1
- package/dist/providers/evm/index.mjs.map +1 -1
- package/dist/providers/near/index.d.mts +1 -1
- package/dist/providers/near/index.d.ts +1 -1
- package/dist/providers/near/index.js +94 -1
- package/dist/providers/near/index.js.map +1 -1
- package/dist/providers/near/index.mjs +94 -1
- package/dist/providers/near/index.mjs.map +1 -1
- package/dist/providers/solana/index.d.mts +1 -1
- package/dist/providers/solana/index.d.ts +1 -1
- package/dist/providers/solana/index.js +94 -1
- package/dist/providers/solana/index.js.map +1 -1
- package/dist/providers/solana/index.mjs +94 -1
- package/dist/providers/solana/index.mjs.map +1 -1
- package/dist/providers/stellar/index.d.mts +1 -1
- package/dist/providers/stellar/index.d.ts +1 -1
- package/dist/providers/stellar/index.js +94 -1
- package/dist/providers/stellar/index.js.map +1 -1
- package/dist/providers/stellar/index.mjs +94 -1
- package/dist/providers/stellar/index.mjs.map +1 -1
- package/dist/react/index.d.mts +3 -3
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +94 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +94 -1
- package/dist/react/index.mjs.map +1 -1
- package/dist/utils/index.d.mts +1 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +94 -1
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/index.mjs +94 -1
- package/dist/utils/index.mjs.map +1 -1
- package/package.json +24 -3
- package/src/backend/index.ts +2131 -0
- package/src/chains/index.ts +108 -2
- package/src/index.ts +19 -1
- package/src/providers/algorand/index.ts +356 -0
- package/src/types/index.ts +44 -3
|
@@ -0,0 +1,1738 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/types/index.ts
|
|
4
|
+
var CAIP2_IDENTIFIERS = {
|
|
5
|
+
// EVM chains
|
|
6
|
+
base: "eip155:8453",
|
|
7
|
+
ethereum: "eip155:1",
|
|
8
|
+
polygon: "eip155:137",
|
|
9
|
+
arbitrum: "eip155:42161",
|
|
10
|
+
optimism: "eip155:10",
|
|
11
|
+
avalanche: "eip155:43114",
|
|
12
|
+
celo: "eip155:42220",
|
|
13
|
+
hyperevm: "eip155:999",
|
|
14
|
+
unichain: "eip155:130",
|
|
15
|
+
monad: "eip155:143",
|
|
16
|
+
// SVM chains
|
|
17
|
+
solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
18
|
+
fogo: "svm:fogo",
|
|
19
|
+
// Stellar
|
|
20
|
+
stellar: "stellar:pubnet",
|
|
21
|
+
// NEAR
|
|
22
|
+
near: "near:mainnet",
|
|
23
|
+
// Algorand
|
|
24
|
+
algorand: "algorand:mainnet",
|
|
25
|
+
"algorand-testnet": "algorand:testnet"
|
|
26
|
+
};
|
|
27
|
+
Object.fromEntries(
|
|
28
|
+
Object.entries(CAIP2_IDENTIFIERS).map(([k, v]) => [v, k])
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
// src/chains/index.ts
|
|
32
|
+
var DEFAULT_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz";
|
|
33
|
+
var SUPPORTED_CHAINS = {
|
|
34
|
+
// ============================================================================
|
|
35
|
+
// EVM CHAINS (10 networks)
|
|
36
|
+
// ============================================================================
|
|
37
|
+
base: {
|
|
38
|
+
chainId: 8453,
|
|
39
|
+
chainIdHex: "0x2105",
|
|
40
|
+
name: "base",
|
|
41
|
+
displayName: "Base",
|
|
42
|
+
networkType: "evm",
|
|
43
|
+
rpcUrl: "https://mainnet.base.org",
|
|
44
|
+
explorerUrl: "https://basescan.org",
|
|
45
|
+
nativeCurrency: {
|
|
46
|
+
name: "Ethereum",
|
|
47
|
+
symbol: "ETH",
|
|
48
|
+
decimals: 18
|
|
49
|
+
},
|
|
50
|
+
usdc: {
|
|
51
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
52
|
+
decimals: 6,
|
|
53
|
+
name: "USD Coin",
|
|
54
|
+
version: "2"
|
|
55
|
+
},
|
|
56
|
+
tokens: {
|
|
57
|
+
usdc: {
|
|
58
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
59
|
+
decimals: 6,
|
|
60
|
+
name: "USD Coin",
|
|
61
|
+
version: "2"
|
|
62
|
+
},
|
|
63
|
+
eurc: {
|
|
64
|
+
address: "0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42",
|
|
65
|
+
decimals: 6,
|
|
66
|
+
name: "EURC",
|
|
67
|
+
version: "2"
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
x402: {
|
|
71
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
72
|
+
enabled: true
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
avalanche: {
|
|
76
|
+
chainId: 43114,
|
|
77
|
+
chainIdHex: "0xa86a",
|
|
78
|
+
name: "avalanche",
|
|
79
|
+
displayName: "Avalanche C-Chain",
|
|
80
|
+
networkType: "evm",
|
|
81
|
+
rpcUrl: "https://avalanche-c-chain-rpc.publicnode.com",
|
|
82
|
+
explorerUrl: "https://snowtrace.io",
|
|
83
|
+
nativeCurrency: {
|
|
84
|
+
name: "Avalanche",
|
|
85
|
+
symbol: "AVAX",
|
|
86
|
+
decimals: 18
|
|
87
|
+
},
|
|
88
|
+
usdc: {
|
|
89
|
+
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
90
|
+
decimals: 6,
|
|
91
|
+
name: "USD Coin",
|
|
92
|
+
version: "2"
|
|
93
|
+
},
|
|
94
|
+
tokens: {
|
|
95
|
+
usdc: {
|
|
96
|
+
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
97
|
+
decimals: 6,
|
|
98
|
+
name: "USD Coin",
|
|
99
|
+
version: "2"
|
|
100
|
+
},
|
|
101
|
+
eurc: {
|
|
102
|
+
address: "0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD",
|
|
103
|
+
decimals: 6,
|
|
104
|
+
name: "EURC",
|
|
105
|
+
version: "2"
|
|
106
|
+
},
|
|
107
|
+
ausd: {
|
|
108
|
+
address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
|
|
109
|
+
decimals: 6,
|
|
110
|
+
name: "Agora Dollar",
|
|
111
|
+
version: "1"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
x402: {
|
|
115
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
116
|
+
enabled: true
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
ethereum: {
|
|
120
|
+
chainId: 1,
|
|
121
|
+
chainIdHex: "0x1",
|
|
122
|
+
name: "ethereum",
|
|
123
|
+
displayName: "Ethereum",
|
|
124
|
+
networkType: "evm",
|
|
125
|
+
rpcUrl: "https://eth.llamarpc.com",
|
|
126
|
+
explorerUrl: "https://etherscan.io",
|
|
127
|
+
nativeCurrency: {
|
|
128
|
+
name: "Ethereum",
|
|
129
|
+
symbol: "ETH",
|
|
130
|
+
decimals: 18
|
|
131
|
+
},
|
|
132
|
+
usdc: {
|
|
133
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
134
|
+
decimals: 6,
|
|
135
|
+
name: "USD Coin",
|
|
136
|
+
version: "2"
|
|
137
|
+
},
|
|
138
|
+
tokens: {
|
|
139
|
+
usdc: {
|
|
140
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
141
|
+
decimals: 6,
|
|
142
|
+
name: "USD Coin",
|
|
143
|
+
version: "2"
|
|
144
|
+
},
|
|
145
|
+
eurc: {
|
|
146
|
+
address: "0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c",
|
|
147
|
+
decimals: 6,
|
|
148
|
+
name: "Euro Coin",
|
|
149
|
+
version: "2"
|
|
150
|
+
},
|
|
151
|
+
ausd: {
|
|
152
|
+
address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
|
|
153
|
+
decimals: 6,
|
|
154
|
+
name: "Agora Dollar",
|
|
155
|
+
version: "1"
|
|
156
|
+
},
|
|
157
|
+
pyusd: {
|
|
158
|
+
address: "0x6c3ea9036406852006290770BEdFcAbA0e23A0e8",
|
|
159
|
+
decimals: 6,
|
|
160
|
+
name: "PayPal USD",
|
|
161
|
+
version: "1"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
x402: {
|
|
165
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
166
|
+
enabled: true
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
polygon: {
|
|
170
|
+
chainId: 137,
|
|
171
|
+
chainIdHex: "0x89",
|
|
172
|
+
name: "polygon",
|
|
173
|
+
displayName: "Polygon",
|
|
174
|
+
networkType: "evm",
|
|
175
|
+
rpcUrl: "https://polygon-rpc.com",
|
|
176
|
+
explorerUrl: "https://polygonscan.com",
|
|
177
|
+
nativeCurrency: {
|
|
178
|
+
name: "Polygon",
|
|
179
|
+
symbol: "POL",
|
|
180
|
+
decimals: 18
|
|
181
|
+
},
|
|
182
|
+
usdc: {
|
|
183
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
184
|
+
decimals: 6,
|
|
185
|
+
name: "USD Coin",
|
|
186
|
+
version: "2"
|
|
187
|
+
},
|
|
188
|
+
tokens: {
|
|
189
|
+
usdc: {
|
|
190
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
191
|
+
decimals: 6,
|
|
192
|
+
name: "USD Coin",
|
|
193
|
+
version: "2"
|
|
194
|
+
},
|
|
195
|
+
ausd: {
|
|
196
|
+
address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
|
|
197
|
+
decimals: 6,
|
|
198
|
+
name: "Agora Dollar",
|
|
199
|
+
version: "1"
|
|
200
|
+
}
|
|
201
|
+
},
|
|
202
|
+
x402: {
|
|
203
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
204
|
+
enabled: true
|
|
205
|
+
}
|
|
206
|
+
},
|
|
207
|
+
arbitrum: {
|
|
208
|
+
chainId: 42161,
|
|
209
|
+
chainIdHex: "0xa4b1",
|
|
210
|
+
name: "arbitrum",
|
|
211
|
+
displayName: "Arbitrum One",
|
|
212
|
+
networkType: "evm",
|
|
213
|
+
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
|
214
|
+
explorerUrl: "https://arbiscan.io",
|
|
215
|
+
nativeCurrency: {
|
|
216
|
+
name: "Ethereum",
|
|
217
|
+
symbol: "ETH",
|
|
218
|
+
decimals: 18
|
|
219
|
+
},
|
|
220
|
+
usdc: {
|
|
221
|
+
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
222
|
+
decimals: 6,
|
|
223
|
+
name: "USD Coin",
|
|
224
|
+
version: "2"
|
|
225
|
+
},
|
|
226
|
+
tokens: {
|
|
227
|
+
usdc: {
|
|
228
|
+
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
229
|
+
decimals: 6,
|
|
230
|
+
name: "USD Coin",
|
|
231
|
+
version: "2"
|
|
232
|
+
},
|
|
233
|
+
ausd: {
|
|
234
|
+
address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
|
|
235
|
+
decimals: 6,
|
|
236
|
+
name: "Agora Dollar",
|
|
237
|
+
version: "1"
|
|
238
|
+
},
|
|
239
|
+
usdt: {
|
|
240
|
+
address: "0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9",
|
|
241
|
+
decimals: 6,
|
|
242
|
+
name: "USD\u20AE0",
|
|
243
|
+
version: "1"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
x402: {
|
|
247
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
248
|
+
enabled: true
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
optimism: {
|
|
252
|
+
chainId: 10,
|
|
253
|
+
chainIdHex: "0xa",
|
|
254
|
+
name: "optimism",
|
|
255
|
+
displayName: "Optimism",
|
|
256
|
+
networkType: "evm",
|
|
257
|
+
rpcUrl: "https://mainnet.optimism.io",
|
|
258
|
+
explorerUrl: "https://optimistic.etherscan.io",
|
|
259
|
+
nativeCurrency: {
|
|
260
|
+
name: "Ethereum",
|
|
261
|
+
symbol: "ETH",
|
|
262
|
+
decimals: 18
|
|
263
|
+
},
|
|
264
|
+
usdc: {
|
|
265
|
+
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
266
|
+
decimals: 6,
|
|
267
|
+
name: "USD Coin",
|
|
268
|
+
version: "2"
|
|
269
|
+
},
|
|
270
|
+
tokens: {
|
|
271
|
+
usdc: {
|
|
272
|
+
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
273
|
+
decimals: 6,
|
|
274
|
+
name: "USD Coin",
|
|
275
|
+
version: "2"
|
|
276
|
+
},
|
|
277
|
+
usdt: {
|
|
278
|
+
address: "0x01bff41798a0bcf287b996046ca68b395dbc1071",
|
|
279
|
+
decimals: 6,
|
|
280
|
+
name: "USD\u20AE0",
|
|
281
|
+
version: "1"
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
x402: {
|
|
285
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
286
|
+
enabled: true
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
celo: {
|
|
290
|
+
chainId: 42220,
|
|
291
|
+
chainIdHex: "0xa4ec",
|
|
292
|
+
name: "celo",
|
|
293
|
+
displayName: "Celo",
|
|
294
|
+
networkType: "evm",
|
|
295
|
+
rpcUrl: "https://forno.celo.org",
|
|
296
|
+
explorerUrl: "https://celoscan.io",
|
|
297
|
+
nativeCurrency: {
|
|
298
|
+
name: "Celo",
|
|
299
|
+
symbol: "CELO",
|
|
300
|
+
decimals: 18
|
|
301
|
+
},
|
|
302
|
+
usdc: {
|
|
303
|
+
address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
|
|
304
|
+
decimals: 6,
|
|
305
|
+
name: "USDC",
|
|
306
|
+
// Celo uses "USDC" not "USD Coin" for EIP-712
|
|
307
|
+
version: "2"
|
|
308
|
+
},
|
|
309
|
+
tokens: {
|
|
310
|
+
usdc: {
|
|
311
|
+
address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
|
|
312
|
+
decimals: 6,
|
|
313
|
+
name: "USDC",
|
|
314
|
+
// Celo uses "USDC" not "USD Coin" for EIP-712
|
|
315
|
+
version: "2"
|
|
316
|
+
},
|
|
317
|
+
usdt: {
|
|
318
|
+
address: "0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e",
|
|
319
|
+
decimals: 6,
|
|
320
|
+
name: "Tether USD",
|
|
321
|
+
// Celo USDT uses "Tether USD" for EIP-712
|
|
322
|
+
version: "1"
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
x402: {
|
|
326
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
327
|
+
enabled: true
|
|
328
|
+
}
|
|
329
|
+
},
|
|
330
|
+
hyperevm: {
|
|
331
|
+
chainId: 999,
|
|
332
|
+
chainIdHex: "0x3e7",
|
|
333
|
+
name: "hyperevm",
|
|
334
|
+
displayName: "HyperEVM",
|
|
335
|
+
networkType: "evm",
|
|
336
|
+
rpcUrl: "https://rpc.hyperliquid.xyz/evm",
|
|
337
|
+
explorerUrl: "https://hyperevmscan.io",
|
|
338
|
+
nativeCurrency: {
|
|
339
|
+
name: "Ethereum",
|
|
340
|
+
symbol: "ETH",
|
|
341
|
+
decimals: 18
|
|
342
|
+
},
|
|
343
|
+
usdc: {
|
|
344
|
+
address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
345
|
+
decimals: 6,
|
|
346
|
+
name: "USDC",
|
|
347
|
+
// HyperEVM uses "USDC" not "USD Coin"
|
|
348
|
+
version: "2"
|
|
349
|
+
},
|
|
350
|
+
tokens: {
|
|
351
|
+
usdc: {
|
|
352
|
+
address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
353
|
+
decimals: 6,
|
|
354
|
+
name: "USDC",
|
|
355
|
+
// HyperEVM uses "USDC" not "USD Coin"
|
|
356
|
+
version: "2"
|
|
357
|
+
}
|
|
358
|
+
},
|
|
359
|
+
x402: {
|
|
360
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
361
|
+
enabled: true
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
unichain: {
|
|
365
|
+
chainId: 130,
|
|
366
|
+
chainIdHex: "0x82",
|
|
367
|
+
name: "unichain",
|
|
368
|
+
displayName: "Unichain",
|
|
369
|
+
networkType: "evm",
|
|
370
|
+
rpcUrl: "https://unichain-rpc.publicnode.com",
|
|
371
|
+
explorerUrl: "https://uniscan.xyz",
|
|
372
|
+
nativeCurrency: {
|
|
373
|
+
name: "Ethereum",
|
|
374
|
+
symbol: "ETH",
|
|
375
|
+
decimals: 18
|
|
376
|
+
},
|
|
377
|
+
usdc: {
|
|
378
|
+
address: "0x078d782b760474a361dda0af3839290b0ef57ad6",
|
|
379
|
+
decimals: 6,
|
|
380
|
+
name: "USDC",
|
|
381
|
+
// Unichain uses "USDC" not "USD Coin"
|
|
382
|
+
version: "2"
|
|
383
|
+
},
|
|
384
|
+
tokens: {
|
|
385
|
+
usdc: {
|
|
386
|
+
address: "0x078d782b760474a361dda0af3839290b0ef57ad6",
|
|
387
|
+
decimals: 6,
|
|
388
|
+
name: "USDC",
|
|
389
|
+
// Unichain uses "USDC" not "USD Coin"
|
|
390
|
+
version: "2"
|
|
391
|
+
}
|
|
392
|
+
},
|
|
393
|
+
x402: {
|
|
394
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
395
|
+
enabled: true
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
monad: {
|
|
399
|
+
chainId: 143,
|
|
400
|
+
chainIdHex: "0x8f",
|
|
401
|
+
name: "monad",
|
|
402
|
+
displayName: "Monad",
|
|
403
|
+
networkType: "evm",
|
|
404
|
+
rpcUrl: "https://rpc.monad.xyz",
|
|
405
|
+
explorerUrl: "https://monad.socialscan.io",
|
|
406
|
+
nativeCurrency: {
|
|
407
|
+
name: "Monad",
|
|
408
|
+
symbol: "MON",
|
|
409
|
+
decimals: 18
|
|
410
|
+
},
|
|
411
|
+
usdc: {
|
|
412
|
+
address: "0x754704bc059f8c67012fed69bc8a327a5aafb603",
|
|
413
|
+
decimals: 6,
|
|
414
|
+
name: "USDC",
|
|
415
|
+
// Monad uses "USDC" not "USD Coin"
|
|
416
|
+
version: "2"
|
|
417
|
+
},
|
|
418
|
+
tokens: {
|
|
419
|
+
usdc: {
|
|
420
|
+
address: "0x754704bc059f8c67012fed69bc8a327a5aafb603",
|
|
421
|
+
decimals: 6,
|
|
422
|
+
name: "USDC",
|
|
423
|
+
// Monad uses "USDC" not "USD Coin"
|
|
424
|
+
version: "2"
|
|
425
|
+
},
|
|
426
|
+
ausd: {
|
|
427
|
+
address: "0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a",
|
|
428
|
+
decimals: 6,
|
|
429
|
+
name: "Agora Dollar",
|
|
430
|
+
version: "1"
|
|
431
|
+
}
|
|
432
|
+
},
|
|
433
|
+
x402: {
|
|
434
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
435
|
+
enabled: true
|
|
436
|
+
}
|
|
437
|
+
},
|
|
438
|
+
// ============================================================================
|
|
439
|
+
// SVM CHAINS (2 networks) - Solana Virtual Machine
|
|
440
|
+
// ============================================================================
|
|
441
|
+
solana: {
|
|
442
|
+
chainId: 0,
|
|
443
|
+
// Non-EVM
|
|
444
|
+
chainIdHex: "0x0",
|
|
445
|
+
name: "solana",
|
|
446
|
+
displayName: "Solana",
|
|
447
|
+
networkType: "svm",
|
|
448
|
+
rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
449
|
+
explorerUrl: "https://solscan.io",
|
|
450
|
+
nativeCurrency: {
|
|
451
|
+
name: "Solana",
|
|
452
|
+
symbol: "SOL",
|
|
453
|
+
decimals: 9
|
|
454
|
+
},
|
|
455
|
+
usdc: {
|
|
456
|
+
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
457
|
+
// USDC SPL token mint
|
|
458
|
+
decimals: 6,
|
|
459
|
+
name: "USD Coin",
|
|
460
|
+
version: "1"
|
|
461
|
+
},
|
|
462
|
+
tokens: {
|
|
463
|
+
usdc: {
|
|
464
|
+
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
465
|
+
// USDC SPL token mint
|
|
466
|
+
decimals: 6,
|
|
467
|
+
name: "USD Coin",
|
|
468
|
+
version: "1"
|
|
469
|
+
},
|
|
470
|
+
ausd: {
|
|
471
|
+
address: "AUSD1jCcCyPLybk1YnvPWsHQSrZ46dxwoMniN4N2UEB9",
|
|
472
|
+
// AUSD Token2022 mint
|
|
473
|
+
decimals: 6,
|
|
474
|
+
name: "Agora Dollar",
|
|
475
|
+
version: "1"
|
|
476
|
+
}
|
|
477
|
+
},
|
|
478
|
+
x402: {
|
|
479
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
480
|
+
enabled: true
|
|
481
|
+
}
|
|
482
|
+
},
|
|
483
|
+
fogo: {
|
|
484
|
+
chainId: 0,
|
|
485
|
+
// Non-EVM (SVM)
|
|
486
|
+
chainIdHex: "0x0",
|
|
487
|
+
name: "fogo",
|
|
488
|
+
displayName: "Fogo",
|
|
489
|
+
networkType: "svm",
|
|
490
|
+
rpcUrl: "https://rpc.fogo.nightly.app/",
|
|
491
|
+
explorerUrl: "https://explorer.fogo.nightly.app",
|
|
492
|
+
nativeCurrency: {
|
|
493
|
+
name: "Fogo",
|
|
494
|
+
symbol: "FOGO",
|
|
495
|
+
decimals: 9
|
|
496
|
+
},
|
|
497
|
+
usdc: {
|
|
498
|
+
address: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
|
|
499
|
+
// Fogo USDC mint
|
|
500
|
+
decimals: 6,
|
|
501
|
+
name: "USDC",
|
|
502
|
+
version: "1"
|
|
503
|
+
},
|
|
504
|
+
x402: {
|
|
505
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
506
|
+
enabled: true
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
// ============================================================================
|
|
510
|
+
// STELLAR (1 network)
|
|
511
|
+
// ============================================================================
|
|
512
|
+
stellar: {
|
|
513
|
+
chainId: 0,
|
|
514
|
+
// Non-EVM
|
|
515
|
+
chainIdHex: "0x0",
|
|
516
|
+
name: "stellar",
|
|
517
|
+
displayName: "Stellar",
|
|
518
|
+
networkType: "stellar",
|
|
519
|
+
rpcUrl: "https://horizon.stellar.org",
|
|
520
|
+
explorerUrl: "https://stellar.expert/explorer/public",
|
|
521
|
+
nativeCurrency: {
|
|
522
|
+
name: "Lumens",
|
|
523
|
+
symbol: "XLM",
|
|
524
|
+
decimals: 7
|
|
525
|
+
// Stellar uses 7 decimals (stroops)
|
|
526
|
+
},
|
|
527
|
+
usdc: {
|
|
528
|
+
address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
|
|
529
|
+
// Soroban Asset Contract
|
|
530
|
+
decimals: 7,
|
|
531
|
+
// Stellar USDC uses 7 decimals
|
|
532
|
+
name: "USDC",
|
|
533
|
+
version: "1"
|
|
534
|
+
},
|
|
535
|
+
x402: {
|
|
536
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
537
|
+
enabled: true
|
|
538
|
+
}
|
|
539
|
+
},
|
|
540
|
+
// ============================================================================
|
|
541
|
+
// NEAR (1 network) - Uses NEP-366 meta-transactions
|
|
542
|
+
// ============================================================================
|
|
543
|
+
near: {
|
|
544
|
+
chainId: 0,
|
|
545
|
+
// Non-EVM
|
|
546
|
+
chainIdHex: "0x0",
|
|
547
|
+
name: "near",
|
|
548
|
+
displayName: "NEAR Protocol",
|
|
549
|
+
networkType: "near",
|
|
550
|
+
rpcUrl: "https://rpc.mainnet.near.org",
|
|
551
|
+
explorerUrl: "https://nearblocks.io",
|
|
552
|
+
nativeCurrency: {
|
|
553
|
+
name: "NEAR",
|
|
554
|
+
symbol: "NEAR",
|
|
555
|
+
decimals: 24
|
|
556
|
+
// NEAR uses 24 decimals (yoctoNEAR)
|
|
557
|
+
},
|
|
558
|
+
usdc: {
|
|
559
|
+
address: "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
|
|
560
|
+
// Native Circle USDC
|
|
561
|
+
decimals: 6,
|
|
562
|
+
name: "USDC",
|
|
563
|
+
version: "1"
|
|
564
|
+
},
|
|
565
|
+
x402: {
|
|
566
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
567
|
+
enabled: true
|
|
568
|
+
// NEP-366 meta-transactions supported
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
// ============================================================================
|
|
572
|
+
// ALGORAND (2 networks) - Uses ASA transfers with atomic transaction groups
|
|
573
|
+
// ============================================================================
|
|
574
|
+
algorand: {
|
|
575
|
+
chainId: 0,
|
|
576
|
+
// Non-EVM (Algorand uses genesis hash for network identification)
|
|
577
|
+
chainIdHex: "0x0",
|
|
578
|
+
name: "algorand",
|
|
579
|
+
displayName: "Algorand",
|
|
580
|
+
networkType: "algorand",
|
|
581
|
+
rpcUrl: "https://mainnet-api.algonode.cloud",
|
|
582
|
+
explorerUrl: "https://allo.info",
|
|
583
|
+
nativeCurrency: {
|
|
584
|
+
name: "Algo",
|
|
585
|
+
symbol: "ALGO",
|
|
586
|
+
decimals: 6
|
|
587
|
+
// Algorand uses 6 decimals (microAlgos)
|
|
588
|
+
},
|
|
589
|
+
usdc: {
|
|
590
|
+
address: "31566704",
|
|
591
|
+
// USDC ASA ID on Algorand mainnet
|
|
592
|
+
decimals: 6,
|
|
593
|
+
name: "USDC",
|
|
594
|
+
version: "1"
|
|
595
|
+
},
|
|
596
|
+
tokens: {
|
|
597
|
+
usdc: {
|
|
598
|
+
address: "31566704",
|
|
599
|
+
// USDC ASA ID on Algorand mainnet
|
|
600
|
+
decimals: 6,
|
|
601
|
+
name: "USDC",
|
|
602
|
+
version: "1"
|
|
603
|
+
}
|
|
604
|
+
},
|
|
605
|
+
x402: {
|
|
606
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
607
|
+
enabled: true
|
|
608
|
+
}
|
|
609
|
+
},
|
|
610
|
+
"algorand-testnet": {
|
|
611
|
+
chainId: 0,
|
|
612
|
+
// Non-EVM
|
|
613
|
+
chainIdHex: "0x0",
|
|
614
|
+
name: "algorand-testnet",
|
|
615
|
+
displayName: "Algorand Testnet",
|
|
616
|
+
networkType: "algorand",
|
|
617
|
+
rpcUrl: "https://testnet-api.algonode.cloud",
|
|
618
|
+
explorerUrl: "https://testnet.allo.info",
|
|
619
|
+
nativeCurrency: {
|
|
620
|
+
name: "Algo",
|
|
621
|
+
symbol: "ALGO",
|
|
622
|
+
decimals: 6
|
|
623
|
+
},
|
|
624
|
+
usdc: {
|
|
625
|
+
address: "10458941",
|
|
626
|
+
// USDC ASA ID on Algorand testnet
|
|
627
|
+
decimals: 6,
|
|
628
|
+
name: "USDC",
|
|
629
|
+
version: "1"
|
|
630
|
+
},
|
|
631
|
+
tokens: {
|
|
632
|
+
usdc: {
|
|
633
|
+
address: "10458941",
|
|
634
|
+
// USDC ASA ID on Algorand testnet
|
|
635
|
+
decimals: 6,
|
|
636
|
+
name: "USDC",
|
|
637
|
+
version: "1"
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
x402: {
|
|
641
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
642
|
+
enabled: true
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
function getChainByName(name) {
|
|
647
|
+
return SUPPORTED_CHAINS[name.toLowerCase()];
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// src/utils/x402.ts
|
|
651
|
+
function chainToCAIP2(chainName) {
|
|
652
|
+
const caip2 = CAIP2_IDENTIFIERS[chainName.toLowerCase()];
|
|
653
|
+
if (caip2) {
|
|
654
|
+
return caip2;
|
|
655
|
+
}
|
|
656
|
+
const chain = getChainByName(chainName);
|
|
657
|
+
if (chain) {
|
|
658
|
+
if (chain.networkType === "evm") {
|
|
659
|
+
return `eip155:${chain.chainId}`;
|
|
660
|
+
}
|
|
661
|
+
return `${chain.networkType}:${chainName}`;
|
|
662
|
+
}
|
|
663
|
+
return chainName;
|
|
664
|
+
}
|
|
665
|
+
function decodeX402Header(encoded) {
|
|
666
|
+
const json = atob(encoded);
|
|
667
|
+
return JSON.parse(json);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// src/backend/index.ts
|
|
671
|
+
function parsePaymentHeader(headerValue) {
|
|
672
|
+
if (!headerValue) {
|
|
673
|
+
return null;
|
|
674
|
+
}
|
|
675
|
+
try {
|
|
676
|
+
return decodeX402Header(headerValue);
|
|
677
|
+
} catch {
|
|
678
|
+
return null;
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
function extractPaymentFromHeaders(headers) {
|
|
682
|
+
const normalizedHeaders = {};
|
|
683
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
684
|
+
if (typeof value === "string") {
|
|
685
|
+
normalizedHeaders[key.toLowerCase()] = value;
|
|
686
|
+
} else if (Array.isArray(value) && value.length > 0) {
|
|
687
|
+
normalizedHeaders[key.toLowerCase()] = value[0];
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
const headerValue = normalizedHeaders["x-payment"] || normalizedHeaders["payment-signature"];
|
|
691
|
+
return parsePaymentHeader(headerValue);
|
|
692
|
+
}
|
|
693
|
+
function buildPaymentRequirements(options) {
|
|
694
|
+
const {
|
|
695
|
+
amount,
|
|
696
|
+
recipient,
|
|
697
|
+
resource,
|
|
698
|
+
chainName = "base",
|
|
699
|
+
description = "Payment for resource access",
|
|
700
|
+
mimeType = "application/json",
|
|
701
|
+
timeoutSeconds = 300,
|
|
702
|
+
x402Version = 1
|
|
703
|
+
} = options;
|
|
704
|
+
const chain = getChainByName(chainName);
|
|
705
|
+
if (!chain) {
|
|
706
|
+
throw new Error(`Unsupported chain: ${chainName}`);
|
|
707
|
+
}
|
|
708
|
+
const atomicAmount = Math.floor(
|
|
709
|
+
parseFloat(amount) * Math.pow(10, chain.usdc.decimals)
|
|
710
|
+
).toString();
|
|
711
|
+
const network = x402Version === 2 ? chainToCAIP2(chainName) : chainName;
|
|
712
|
+
return {
|
|
713
|
+
scheme: "exact",
|
|
714
|
+
network,
|
|
715
|
+
maxAmountRequired: atomicAmount,
|
|
716
|
+
resource,
|
|
717
|
+
description,
|
|
718
|
+
mimeType,
|
|
719
|
+
payTo: recipient,
|
|
720
|
+
maxTimeoutSeconds: timeoutSeconds,
|
|
721
|
+
asset: chain.usdc.address
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
function buildVerifyRequest(paymentHeader, requirements) {
|
|
725
|
+
return {
|
|
726
|
+
x402Version: paymentHeader.x402Version,
|
|
727
|
+
paymentPayload: paymentHeader,
|
|
728
|
+
paymentRequirements: requirements
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
function buildSettleRequest(paymentHeader, requirements) {
|
|
732
|
+
return {
|
|
733
|
+
x402Version: paymentHeader.x402Version,
|
|
734
|
+
paymentPayload: paymentHeader,
|
|
735
|
+
paymentRequirements: requirements
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
var X402_CORS_HEADERS = {
|
|
739
|
+
"Access-Control-Allow-Headers": "Content-Type, X-PAYMENT, PAYMENT-SIGNATURE, Authorization",
|
|
740
|
+
"Access-Control-Expose-Headers": "X-PAYMENT-RESPONSE, PAYMENT-RESPONSE, PAYMENT-REQUIRED",
|
|
741
|
+
"Access-Control-Allow-Methods": "GET, POST, OPTIONS"
|
|
742
|
+
};
|
|
743
|
+
var X402_HEADER_NAMES = [
|
|
744
|
+
"X-PAYMENT",
|
|
745
|
+
"PAYMENT-SIGNATURE",
|
|
746
|
+
"X-PAYMENT-RESPONSE",
|
|
747
|
+
"PAYMENT-RESPONSE",
|
|
748
|
+
"PAYMENT-REQUIRED"
|
|
749
|
+
];
|
|
750
|
+
function getCorsHeaders(origin = "*") {
|
|
751
|
+
return {
|
|
752
|
+
"Access-Control-Allow-Origin": origin,
|
|
753
|
+
...X402_CORS_HEADERS
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
var FacilitatorClient = class {
|
|
757
|
+
baseUrl;
|
|
758
|
+
timeout;
|
|
759
|
+
constructor(options = {}) {
|
|
760
|
+
this.baseUrl = options.baseUrl || "https://facilitator.ultravioletadao.xyz";
|
|
761
|
+
this.timeout = options.timeout || 3e4;
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* Verify a payment with the facilitator
|
|
765
|
+
*
|
|
766
|
+
* Call this before providing the paid resource to validate the payment.
|
|
767
|
+
*
|
|
768
|
+
* @param paymentHeader - Parsed x402 payment header
|
|
769
|
+
* @param requirements - Payment requirements
|
|
770
|
+
* @returns Verification result
|
|
771
|
+
*/
|
|
772
|
+
async verify(paymentHeader, requirements) {
|
|
773
|
+
const body = buildVerifyRequest(paymentHeader, requirements);
|
|
774
|
+
const controller = new AbortController();
|
|
775
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
776
|
+
try {
|
|
777
|
+
const response = await fetch(`${this.baseUrl}/verify`, {
|
|
778
|
+
method: "POST",
|
|
779
|
+
headers: { "Content-Type": "application/json" },
|
|
780
|
+
body: JSON.stringify(body),
|
|
781
|
+
signal: controller.signal
|
|
782
|
+
});
|
|
783
|
+
clearTimeout(timeoutId);
|
|
784
|
+
if (!response.ok) {
|
|
785
|
+
const errorText = await response.text();
|
|
786
|
+
return {
|
|
787
|
+
isValid: false,
|
|
788
|
+
invalidReason: `Facilitator error: ${response.status} - ${errorText}`
|
|
789
|
+
};
|
|
790
|
+
}
|
|
791
|
+
return await response.json();
|
|
792
|
+
} catch (error) {
|
|
793
|
+
clearTimeout(timeoutId);
|
|
794
|
+
return {
|
|
795
|
+
isValid: false,
|
|
796
|
+
invalidReason: error instanceof Error ? error.message : "Unknown error"
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* Settle a payment with the facilitator
|
|
802
|
+
*
|
|
803
|
+
* Call this after providing the paid resource to execute the on-chain transfer.
|
|
804
|
+
*
|
|
805
|
+
* @param paymentHeader - Parsed x402 payment header
|
|
806
|
+
* @param requirements - Payment requirements
|
|
807
|
+
* @returns Settlement result with transaction hash
|
|
808
|
+
*/
|
|
809
|
+
async settle(paymentHeader, requirements) {
|
|
810
|
+
const body = buildSettleRequest(paymentHeader, requirements);
|
|
811
|
+
const controller = new AbortController();
|
|
812
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
813
|
+
try {
|
|
814
|
+
const response = await fetch(`${this.baseUrl}/settle`, {
|
|
815
|
+
method: "POST",
|
|
816
|
+
headers: { "Content-Type": "application/json" },
|
|
817
|
+
body: JSON.stringify(body),
|
|
818
|
+
signal: controller.signal
|
|
819
|
+
});
|
|
820
|
+
clearTimeout(timeoutId);
|
|
821
|
+
if (!response.ok) {
|
|
822
|
+
const errorText = await response.text();
|
|
823
|
+
return {
|
|
824
|
+
success: false,
|
|
825
|
+
error: `Facilitator error: ${response.status} - ${errorText}`
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
const result = await response.json();
|
|
829
|
+
return {
|
|
830
|
+
success: true,
|
|
831
|
+
transactionHash: result.transactionHash || result.transaction_hash,
|
|
832
|
+
network: result.network
|
|
833
|
+
};
|
|
834
|
+
} catch (error) {
|
|
835
|
+
clearTimeout(timeoutId);
|
|
836
|
+
return {
|
|
837
|
+
success: false,
|
|
838
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
839
|
+
};
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
/**
|
|
843
|
+
* Verify and settle atomically
|
|
844
|
+
*
|
|
845
|
+
* Convenience method that verifies first, then settles if valid.
|
|
846
|
+
* Use this for simple payment flows where you don't need custom logic between verify and settle.
|
|
847
|
+
*
|
|
848
|
+
* @param paymentHeader - Parsed x402 payment header
|
|
849
|
+
* @param requirements - Payment requirements
|
|
850
|
+
* @returns Combined result with verify and settle status
|
|
851
|
+
*/
|
|
852
|
+
async verifyAndSettle(paymentHeader, requirements) {
|
|
853
|
+
const verifyResult = await this.verify(paymentHeader, requirements);
|
|
854
|
+
if (!verifyResult.isValid) {
|
|
855
|
+
return {
|
|
856
|
+
verified: false,
|
|
857
|
+
settled: false,
|
|
858
|
+
error: verifyResult.invalidReason
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
const settleResult = await this.settle(paymentHeader, requirements);
|
|
862
|
+
return {
|
|
863
|
+
verified: true,
|
|
864
|
+
settled: settleResult.success,
|
|
865
|
+
transactionHash: settleResult.transactionHash,
|
|
866
|
+
error: settleResult.error
|
|
867
|
+
};
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Check if the facilitator is healthy
|
|
871
|
+
*
|
|
872
|
+
* @returns True if the facilitator is responding
|
|
873
|
+
*/
|
|
874
|
+
async healthCheck() {
|
|
875
|
+
try {
|
|
876
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
877
|
+
method: "GET"
|
|
878
|
+
});
|
|
879
|
+
return response.ok;
|
|
880
|
+
} catch {
|
|
881
|
+
return false;
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
function create402Response(requirements, options = {}) {
|
|
886
|
+
const reqs = buildPaymentRequirements(requirements);
|
|
887
|
+
const body = {
|
|
888
|
+
x402Version: requirements.x402Version || 1,
|
|
889
|
+
...reqs
|
|
890
|
+
};
|
|
891
|
+
if (options.accepts) {
|
|
892
|
+
body.accepts = options.accepts;
|
|
893
|
+
}
|
|
894
|
+
return {
|
|
895
|
+
status: 402,
|
|
896
|
+
headers: {
|
|
897
|
+
"Content-Type": "application/json",
|
|
898
|
+
...X402_CORS_HEADERS
|
|
899
|
+
},
|
|
900
|
+
body
|
|
901
|
+
};
|
|
902
|
+
}
|
|
903
|
+
function createPaymentMiddleware(getRequirements, options = {}) {
|
|
904
|
+
const client = new FacilitatorClient(options);
|
|
905
|
+
return async (req, res, next) => {
|
|
906
|
+
const payment = extractPaymentFromHeaders(req.headers);
|
|
907
|
+
if (!payment) {
|
|
908
|
+
const reqOptions2 = getRequirements(req);
|
|
909
|
+
const { status, headers, body } = create402Response(reqOptions2);
|
|
910
|
+
res.status(status).set(headers).json(body);
|
|
911
|
+
return;
|
|
912
|
+
}
|
|
913
|
+
const reqOptions = getRequirements(req);
|
|
914
|
+
const requirements = buildPaymentRequirements(reqOptions);
|
|
915
|
+
const verifyResult = await client.verify(payment, requirements);
|
|
916
|
+
if (!verifyResult.isValid) {
|
|
917
|
+
res.status(402).json({
|
|
918
|
+
error: "Payment verification failed",
|
|
919
|
+
reason: verifyResult.invalidReason
|
|
920
|
+
});
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
next();
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
var BazaarClient = class {
|
|
927
|
+
baseUrl;
|
|
928
|
+
apiKey;
|
|
929
|
+
timeout;
|
|
930
|
+
constructor(options = {}) {
|
|
931
|
+
this.baseUrl = options.baseUrl || "https://bazaar.ultravioletadao.xyz";
|
|
932
|
+
this.apiKey = options.apiKey;
|
|
933
|
+
this.timeout = options.timeout || 3e4;
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Discover x402-enabled resources
|
|
937
|
+
*
|
|
938
|
+
* @param options - Discovery filters
|
|
939
|
+
* @returns Paginated list of matching resources
|
|
940
|
+
*
|
|
941
|
+
* @example
|
|
942
|
+
* ```ts
|
|
943
|
+
* // Find AI APIs on Base with USDC under $0.10
|
|
944
|
+
* const results = await bazaar.discover({
|
|
945
|
+
* category: 'ai',
|
|
946
|
+
* network: 'base',
|
|
947
|
+
* token: 'USDC',
|
|
948
|
+
* maxPrice: '0.10',
|
|
949
|
+
* });
|
|
950
|
+
*
|
|
951
|
+
* for (const resource of results.resources) {
|
|
952
|
+
* console.log(`${resource.name}: ${resource.url}`);
|
|
953
|
+
* }
|
|
954
|
+
* ```
|
|
955
|
+
*/
|
|
956
|
+
async discover(options = {}) {
|
|
957
|
+
const params = new URLSearchParams();
|
|
958
|
+
if (options.category) params.set("category", options.category);
|
|
959
|
+
if (options.network) params.set("network", options.network);
|
|
960
|
+
if (options.token) params.set("token", options.token);
|
|
961
|
+
if (options.provider) params.set("provider", options.provider);
|
|
962
|
+
if (options.tags?.length) params.set("tags", options.tags.join(","));
|
|
963
|
+
if (options.query) params.set("query", options.query);
|
|
964
|
+
if (options.maxPrice) params.set("maxPrice", options.maxPrice);
|
|
965
|
+
if (options.page) params.set("page", options.page.toString());
|
|
966
|
+
if (options.limit) params.set("limit", options.limit.toString());
|
|
967
|
+
if (options.sortBy) params.set("sortBy", options.sortBy);
|
|
968
|
+
if (options.sortOrder) params.set("sortOrder", options.sortOrder);
|
|
969
|
+
const url = `${this.baseUrl}/resources${params.toString() ? `?${params}` : ""}`;
|
|
970
|
+
const controller = new AbortController();
|
|
971
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
972
|
+
try {
|
|
973
|
+
const response = await fetch(url, {
|
|
974
|
+
method: "GET",
|
|
975
|
+
headers: { "Accept": "application/json" },
|
|
976
|
+
signal: controller.signal
|
|
977
|
+
});
|
|
978
|
+
clearTimeout(timeoutId);
|
|
979
|
+
if (!response.ok) {
|
|
980
|
+
const errorText = await response.text();
|
|
981
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
982
|
+
}
|
|
983
|
+
return await response.json();
|
|
984
|
+
} catch (error) {
|
|
985
|
+
clearTimeout(timeoutId);
|
|
986
|
+
throw error;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Get a specific resource by ID
|
|
991
|
+
*
|
|
992
|
+
* @param resourceId - Resource ID
|
|
993
|
+
* @returns Resource details
|
|
994
|
+
*/
|
|
995
|
+
async getResource(resourceId) {
|
|
996
|
+
const url = `${this.baseUrl}/resources/${encodeURIComponent(resourceId)}`;
|
|
997
|
+
const controller = new AbortController();
|
|
998
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
999
|
+
try {
|
|
1000
|
+
const response = await fetch(url, {
|
|
1001
|
+
method: "GET",
|
|
1002
|
+
headers: { "Accept": "application/json" },
|
|
1003
|
+
signal: controller.signal
|
|
1004
|
+
});
|
|
1005
|
+
clearTimeout(timeoutId);
|
|
1006
|
+
if (!response.ok) {
|
|
1007
|
+
const errorText = await response.text();
|
|
1008
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1009
|
+
}
|
|
1010
|
+
return await response.json();
|
|
1011
|
+
} catch (error) {
|
|
1012
|
+
clearTimeout(timeoutId);
|
|
1013
|
+
throw error;
|
|
1014
|
+
}
|
|
1015
|
+
}
|
|
1016
|
+
/**
|
|
1017
|
+
* Get a resource by its URL
|
|
1018
|
+
*
|
|
1019
|
+
* @param resourceUrl - Resource URL
|
|
1020
|
+
* @returns Resource details
|
|
1021
|
+
*/
|
|
1022
|
+
async getResourceByUrl(resourceUrl) {
|
|
1023
|
+
const url = `${this.baseUrl}/resources/by-url?url=${encodeURIComponent(resourceUrl)}`;
|
|
1024
|
+
const controller = new AbortController();
|
|
1025
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1026
|
+
try {
|
|
1027
|
+
const response = await fetch(url, {
|
|
1028
|
+
method: "GET",
|
|
1029
|
+
headers: { "Accept": "application/json" },
|
|
1030
|
+
signal: controller.signal
|
|
1031
|
+
});
|
|
1032
|
+
clearTimeout(timeoutId);
|
|
1033
|
+
if (!response.ok) {
|
|
1034
|
+
const errorText = await response.text();
|
|
1035
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1036
|
+
}
|
|
1037
|
+
return await response.json();
|
|
1038
|
+
} catch (error) {
|
|
1039
|
+
clearTimeout(timeoutId);
|
|
1040
|
+
throw error;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
/**
|
|
1044
|
+
* Register a new resource in the Bazaar
|
|
1045
|
+
*
|
|
1046
|
+
* Requires API key authentication.
|
|
1047
|
+
*
|
|
1048
|
+
* @param options - Resource registration options
|
|
1049
|
+
* @returns Registered resource
|
|
1050
|
+
*
|
|
1051
|
+
* @example
|
|
1052
|
+
* ```ts
|
|
1053
|
+
* const resource = await bazaar.register({
|
|
1054
|
+
* url: 'https://api.example.com/v1/generate',
|
|
1055
|
+
* name: 'Image Generator API',
|
|
1056
|
+
* description: 'Generate images with AI',
|
|
1057
|
+
* category: 'ai',
|
|
1058
|
+
* networks: ['base', 'ethereum', 'polygon'],
|
|
1059
|
+
* price: '0.05',
|
|
1060
|
+
* payTo: '0x1234...',
|
|
1061
|
+
* tags: ['ai', 'image', 'generator'],
|
|
1062
|
+
* });
|
|
1063
|
+
* ```
|
|
1064
|
+
*/
|
|
1065
|
+
async register(options) {
|
|
1066
|
+
if (!this.apiKey) {
|
|
1067
|
+
throw new Error("API key required for resource registration");
|
|
1068
|
+
}
|
|
1069
|
+
const url = `${this.baseUrl}/resources`;
|
|
1070
|
+
const controller = new AbortController();
|
|
1071
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1072
|
+
try {
|
|
1073
|
+
const response = await fetch(url, {
|
|
1074
|
+
method: "POST",
|
|
1075
|
+
headers: {
|
|
1076
|
+
"Content-Type": "application/json",
|
|
1077
|
+
"Accept": "application/json",
|
|
1078
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1079
|
+
},
|
|
1080
|
+
body: JSON.stringify({
|
|
1081
|
+
url: options.url,
|
|
1082
|
+
name: options.name,
|
|
1083
|
+
description: options.description,
|
|
1084
|
+
category: options.category,
|
|
1085
|
+
networks: options.networks,
|
|
1086
|
+
tokens: options.tokens || ["USDC"],
|
|
1087
|
+
price: options.price,
|
|
1088
|
+
priceCurrency: options.priceCurrency || "USDC",
|
|
1089
|
+
payTo: options.payTo,
|
|
1090
|
+
mimeType: options.mimeType || "application/json",
|
|
1091
|
+
outputSchema: options.outputSchema,
|
|
1092
|
+
tags: options.tags
|
|
1093
|
+
}),
|
|
1094
|
+
signal: controller.signal
|
|
1095
|
+
});
|
|
1096
|
+
clearTimeout(timeoutId);
|
|
1097
|
+
if (!response.ok) {
|
|
1098
|
+
const errorText = await response.text();
|
|
1099
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1100
|
+
}
|
|
1101
|
+
return await response.json();
|
|
1102
|
+
} catch (error) {
|
|
1103
|
+
clearTimeout(timeoutId);
|
|
1104
|
+
throw error;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Update an existing resource
|
|
1109
|
+
*
|
|
1110
|
+
* Requires API key authentication. Only the owner can update.
|
|
1111
|
+
*
|
|
1112
|
+
* @param resourceId - Resource ID to update
|
|
1113
|
+
* @param updates - Partial update options
|
|
1114
|
+
* @returns Updated resource
|
|
1115
|
+
*/
|
|
1116
|
+
async update(resourceId, updates) {
|
|
1117
|
+
if (!this.apiKey) {
|
|
1118
|
+
throw new Error("API key required for resource update");
|
|
1119
|
+
}
|
|
1120
|
+
const url = `${this.baseUrl}/resources/${encodeURIComponent(resourceId)}`;
|
|
1121
|
+
const controller = new AbortController();
|
|
1122
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1123
|
+
try {
|
|
1124
|
+
const response = await fetch(url, {
|
|
1125
|
+
method: "PATCH",
|
|
1126
|
+
headers: {
|
|
1127
|
+
"Content-Type": "application/json",
|
|
1128
|
+
"Accept": "application/json",
|
|
1129
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1130
|
+
},
|
|
1131
|
+
body: JSON.stringify(updates),
|
|
1132
|
+
signal: controller.signal
|
|
1133
|
+
});
|
|
1134
|
+
clearTimeout(timeoutId);
|
|
1135
|
+
if (!response.ok) {
|
|
1136
|
+
const errorText = await response.text();
|
|
1137
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1138
|
+
}
|
|
1139
|
+
return await response.json();
|
|
1140
|
+
} catch (error) {
|
|
1141
|
+
clearTimeout(timeoutId);
|
|
1142
|
+
throw error;
|
|
1143
|
+
}
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Delete a resource from the Bazaar
|
|
1147
|
+
*
|
|
1148
|
+
* Requires API key authentication. Only the owner can delete.
|
|
1149
|
+
*
|
|
1150
|
+
* @param resourceId - Resource ID to delete
|
|
1151
|
+
*/
|
|
1152
|
+
async delete(resourceId) {
|
|
1153
|
+
if (!this.apiKey) {
|
|
1154
|
+
throw new Error("API key required for resource deletion");
|
|
1155
|
+
}
|
|
1156
|
+
const url = `${this.baseUrl}/resources/${encodeURIComponent(resourceId)}`;
|
|
1157
|
+
const controller = new AbortController();
|
|
1158
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1159
|
+
try {
|
|
1160
|
+
const response = await fetch(url, {
|
|
1161
|
+
method: "DELETE",
|
|
1162
|
+
headers: {
|
|
1163
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1164
|
+
},
|
|
1165
|
+
signal: controller.signal
|
|
1166
|
+
});
|
|
1167
|
+
clearTimeout(timeoutId);
|
|
1168
|
+
if (!response.ok) {
|
|
1169
|
+
const errorText = await response.text();
|
|
1170
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1171
|
+
}
|
|
1172
|
+
} catch (error) {
|
|
1173
|
+
clearTimeout(timeoutId);
|
|
1174
|
+
throw error;
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
/**
|
|
1178
|
+
* Deactivate a resource (soft delete)
|
|
1179
|
+
*
|
|
1180
|
+
* Requires API key authentication. Only the owner can deactivate.
|
|
1181
|
+
*
|
|
1182
|
+
* @param resourceId - Resource ID to deactivate
|
|
1183
|
+
* @returns Updated resource with isActive: false
|
|
1184
|
+
*/
|
|
1185
|
+
async deactivate(resourceId) {
|
|
1186
|
+
if (!this.apiKey) {
|
|
1187
|
+
throw new Error("API key required for resource deactivation");
|
|
1188
|
+
}
|
|
1189
|
+
const url = `${this.baseUrl}/resources/${encodeURIComponent(resourceId)}/deactivate`;
|
|
1190
|
+
const controller = new AbortController();
|
|
1191
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1192
|
+
try {
|
|
1193
|
+
const response = await fetch(url, {
|
|
1194
|
+
method: "POST",
|
|
1195
|
+
headers: {
|
|
1196
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1197
|
+
},
|
|
1198
|
+
signal: controller.signal
|
|
1199
|
+
});
|
|
1200
|
+
clearTimeout(timeoutId);
|
|
1201
|
+
if (!response.ok) {
|
|
1202
|
+
const errorText = await response.text();
|
|
1203
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1204
|
+
}
|
|
1205
|
+
return await response.json();
|
|
1206
|
+
} catch (error) {
|
|
1207
|
+
clearTimeout(timeoutId);
|
|
1208
|
+
throw error;
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Reactivate a deactivated resource
|
|
1213
|
+
*
|
|
1214
|
+
* Requires API key authentication. Only the owner can reactivate.
|
|
1215
|
+
*
|
|
1216
|
+
* @param resourceId - Resource ID to reactivate
|
|
1217
|
+
* @returns Updated resource with isActive: true
|
|
1218
|
+
*/
|
|
1219
|
+
async reactivate(resourceId) {
|
|
1220
|
+
if (!this.apiKey) {
|
|
1221
|
+
throw new Error("API key required for resource reactivation");
|
|
1222
|
+
}
|
|
1223
|
+
const url = `${this.baseUrl}/resources/${encodeURIComponent(resourceId)}/reactivate`;
|
|
1224
|
+
const controller = new AbortController();
|
|
1225
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1226
|
+
try {
|
|
1227
|
+
const response = await fetch(url, {
|
|
1228
|
+
method: "POST",
|
|
1229
|
+
headers: {
|
|
1230
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1231
|
+
},
|
|
1232
|
+
signal: controller.signal
|
|
1233
|
+
});
|
|
1234
|
+
clearTimeout(timeoutId);
|
|
1235
|
+
if (!response.ok) {
|
|
1236
|
+
const errorText = await response.text();
|
|
1237
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1238
|
+
}
|
|
1239
|
+
return await response.json();
|
|
1240
|
+
} catch (error) {
|
|
1241
|
+
clearTimeout(timeoutId);
|
|
1242
|
+
throw error;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* List all resources owned by the authenticated user
|
|
1247
|
+
*
|
|
1248
|
+
* Requires API key authentication.
|
|
1249
|
+
*
|
|
1250
|
+
* @param options - Pagination options
|
|
1251
|
+
* @returns Paginated list of owned resources
|
|
1252
|
+
*/
|
|
1253
|
+
async listMyResources(options = {}) {
|
|
1254
|
+
if (!this.apiKey) {
|
|
1255
|
+
throw new Error("API key required to list owned resources");
|
|
1256
|
+
}
|
|
1257
|
+
const params = new URLSearchParams();
|
|
1258
|
+
if (options.page) params.set("page", options.page.toString());
|
|
1259
|
+
if (options.limit) params.set("limit", options.limit.toString());
|
|
1260
|
+
if (options.includeInactive) params.set("includeInactive", "true");
|
|
1261
|
+
const url = `${this.baseUrl}/resources/mine${params.toString() ? `?${params}` : ""}`;
|
|
1262
|
+
const controller = new AbortController();
|
|
1263
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1264
|
+
try {
|
|
1265
|
+
const response = await fetch(url, {
|
|
1266
|
+
method: "GET",
|
|
1267
|
+
headers: {
|
|
1268
|
+
"Accept": "application/json",
|
|
1269
|
+
"Authorization": `Bearer ${this.apiKey}`
|
|
1270
|
+
},
|
|
1271
|
+
signal: controller.signal
|
|
1272
|
+
});
|
|
1273
|
+
clearTimeout(timeoutId);
|
|
1274
|
+
if (!response.ok) {
|
|
1275
|
+
const errorText = await response.text();
|
|
1276
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1277
|
+
}
|
|
1278
|
+
return await response.json();
|
|
1279
|
+
} catch (error) {
|
|
1280
|
+
clearTimeout(timeoutId);
|
|
1281
|
+
throw error;
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* Get Bazaar API health status
|
|
1286
|
+
*
|
|
1287
|
+
* @returns True if the Bazaar API is healthy
|
|
1288
|
+
*/
|
|
1289
|
+
async healthCheck() {
|
|
1290
|
+
try {
|
|
1291
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
1292
|
+
method: "GET"
|
|
1293
|
+
});
|
|
1294
|
+
return response.ok;
|
|
1295
|
+
} catch {
|
|
1296
|
+
return false;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
/**
|
|
1300
|
+
* Get Bazaar statistics
|
|
1301
|
+
*
|
|
1302
|
+
* @returns Global statistics about the Bazaar
|
|
1303
|
+
*/
|
|
1304
|
+
async getStats() {
|
|
1305
|
+
const url = `${this.baseUrl}/stats`;
|
|
1306
|
+
const controller = new AbortController();
|
|
1307
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1308
|
+
try {
|
|
1309
|
+
const response = await fetch(url, {
|
|
1310
|
+
method: "GET",
|
|
1311
|
+
headers: { "Accept": "application/json" },
|
|
1312
|
+
signal: controller.signal
|
|
1313
|
+
});
|
|
1314
|
+
clearTimeout(timeoutId);
|
|
1315
|
+
if (!response.ok) {
|
|
1316
|
+
const errorText = await response.text();
|
|
1317
|
+
throw new Error(`Bazaar API error: ${response.status} - ${errorText}`);
|
|
1318
|
+
}
|
|
1319
|
+
return await response.json();
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
clearTimeout(timeoutId);
|
|
1322
|
+
throw error;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
};
|
|
1326
|
+
var EscrowClient = class {
|
|
1327
|
+
baseUrl;
|
|
1328
|
+
apiKey;
|
|
1329
|
+
timeout;
|
|
1330
|
+
constructor(options = {}) {
|
|
1331
|
+
this.baseUrl = options.baseUrl || "https://escrow.ultravioletadao.xyz";
|
|
1332
|
+
this.apiKey = options.apiKey;
|
|
1333
|
+
this.timeout = options.timeout || 3e4;
|
|
1334
|
+
}
|
|
1335
|
+
getHeaders(authenticated = false) {
|
|
1336
|
+
const headers = {
|
|
1337
|
+
"Content-Type": "application/json",
|
|
1338
|
+
"Accept": "application/json"
|
|
1339
|
+
};
|
|
1340
|
+
if (authenticated && this.apiKey) {
|
|
1341
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
1342
|
+
}
|
|
1343
|
+
return headers;
|
|
1344
|
+
}
|
|
1345
|
+
/**
|
|
1346
|
+
* Create an escrow payment
|
|
1347
|
+
*
|
|
1348
|
+
* Holds the payment in escrow until released or refunded.
|
|
1349
|
+
*
|
|
1350
|
+
* @param options - Escrow creation options
|
|
1351
|
+
* @returns Created escrow payment
|
|
1352
|
+
*/
|
|
1353
|
+
async createEscrow(options) {
|
|
1354
|
+
const url = `${this.baseUrl}/escrow`;
|
|
1355
|
+
const controller = new AbortController();
|
|
1356
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1357
|
+
try {
|
|
1358
|
+
const response = await fetch(url, {
|
|
1359
|
+
method: "POST",
|
|
1360
|
+
headers: this.getHeaders(true),
|
|
1361
|
+
body: JSON.stringify({
|
|
1362
|
+
paymentHeader: options.paymentHeader,
|
|
1363
|
+
paymentRequirements: options.requirements,
|
|
1364
|
+
escrowDuration: options.escrowDuration || 86400,
|
|
1365
|
+
releaseConditions: options.releaseConditions
|
|
1366
|
+
}),
|
|
1367
|
+
signal: controller.signal
|
|
1368
|
+
});
|
|
1369
|
+
clearTimeout(timeoutId);
|
|
1370
|
+
if (!response.ok) {
|
|
1371
|
+
const errorText = await response.text();
|
|
1372
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1373
|
+
}
|
|
1374
|
+
return await response.json();
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
clearTimeout(timeoutId);
|
|
1377
|
+
throw error;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* Get escrow payment by ID
|
|
1382
|
+
*
|
|
1383
|
+
* @param escrowId - Escrow payment ID
|
|
1384
|
+
* @returns Escrow payment details
|
|
1385
|
+
*/
|
|
1386
|
+
async getEscrow(escrowId) {
|
|
1387
|
+
const url = `${this.baseUrl}/escrow/${encodeURIComponent(escrowId)}`;
|
|
1388
|
+
const controller = new AbortController();
|
|
1389
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1390
|
+
try {
|
|
1391
|
+
const response = await fetch(url, {
|
|
1392
|
+
method: "GET",
|
|
1393
|
+
headers: this.getHeaders(),
|
|
1394
|
+
signal: controller.signal
|
|
1395
|
+
});
|
|
1396
|
+
clearTimeout(timeoutId);
|
|
1397
|
+
if (!response.ok) {
|
|
1398
|
+
const errorText = await response.text();
|
|
1399
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1400
|
+
}
|
|
1401
|
+
return await response.json();
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
clearTimeout(timeoutId);
|
|
1404
|
+
throw error;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Release escrow funds to recipient
|
|
1409
|
+
*
|
|
1410
|
+
* Call this after service has been successfully provided.
|
|
1411
|
+
*
|
|
1412
|
+
* @param escrowId - Escrow payment ID
|
|
1413
|
+
* @returns Updated escrow payment with transaction hash
|
|
1414
|
+
*/
|
|
1415
|
+
async release(escrowId) {
|
|
1416
|
+
const url = `${this.baseUrl}/escrow/${encodeURIComponent(escrowId)}/release`;
|
|
1417
|
+
const controller = new AbortController();
|
|
1418
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1419
|
+
try {
|
|
1420
|
+
const response = await fetch(url, {
|
|
1421
|
+
method: "POST",
|
|
1422
|
+
headers: this.getHeaders(true),
|
|
1423
|
+
signal: controller.signal
|
|
1424
|
+
});
|
|
1425
|
+
clearTimeout(timeoutId);
|
|
1426
|
+
if (!response.ok) {
|
|
1427
|
+
const errorText = await response.text();
|
|
1428
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1429
|
+
}
|
|
1430
|
+
return await response.json();
|
|
1431
|
+
} catch (error) {
|
|
1432
|
+
clearTimeout(timeoutId);
|
|
1433
|
+
throw error;
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
/**
|
|
1437
|
+
* Request a refund for an escrow payment
|
|
1438
|
+
*
|
|
1439
|
+
* Initiates a refund request that must be approved.
|
|
1440
|
+
*
|
|
1441
|
+
* @param options - Refund request options
|
|
1442
|
+
* @returns Created refund request
|
|
1443
|
+
*/
|
|
1444
|
+
async requestRefund(options) {
|
|
1445
|
+
const url = `${this.baseUrl}/escrow/${encodeURIComponent(options.escrowId)}/refund`;
|
|
1446
|
+
const controller = new AbortController();
|
|
1447
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1448
|
+
try {
|
|
1449
|
+
const response = await fetch(url, {
|
|
1450
|
+
method: "POST",
|
|
1451
|
+
headers: this.getHeaders(true),
|
|
1452
|
+
body: JSON.stringify({
|
|
1453
|
+
reason: options.reason,
|
|
1454
|
+
amount: options.amount,
|
|
1455
|
+
evidence: options.evidence
|
|
1456
|
+
}),
|
|
1457
|
+
signal: controller.signal
|
|
1458
|
+
});
|
|
1459
|
+
clearTimeout(timeoutId);
|
|
1460
|
+
if (!response.ok) {
|
|
1461
|
+
const errorText = await response.text();
|
|
1462
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1463
|
+
}
|
|
1464
|
+
return await response.json();
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
clearTimeout(timeoutId);
|
|
1467
|
+
throw error;
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Approve a refund request (for recipients)
|
|
1472
|
+
*
|
|
1473
|
+
* @param refundId - Refund request ID
|
|
1474
|
+
* @param amount - Amount to approve (may be less than requested)
|
|
1475
|
+
* @returns Updated refund request
|
|
1476
|
+
*/
|
|
1477
|
+
async approveRefund(refundId, amount) {
|
|
1478
|
+
const url = `${this.baseUrl}/refund/${encodeURIComponent(refundId)}/approve`;
|
|
1479
|
+
const controller = new AbortController();
|
|
1480
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1481
|
+
try {
|
|
1482
|
+
const response = await fetch(url, {
|
|
1483
|
+
method: "POST",
|
|
1484
|
+
headers: this.getHeaders(true),
|
|
1485
|
+
body: JSON.stringify({ amount }),
|
|
1486
|
+
signal: controller.signal
|
|
1487
|
+
});
|
|
1488
|
+
clearTimeout(timeoutId);
|
|
1489
|
+
if (!response.ok) {
|
|
1490
|
+
const errorText = await response.text();
|
|
1491
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1492
|
+
}
|
|
1493
|
+
return await response.json();
|
|
1494
|
+
} catch (error) {
|
|
1495
|
+
clearTimeout(timeoutId);
|
|
1496
|
+
throw error;
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
/**
|
|
1500
|
+
* Reject a refund request (for recipients)
|
|
1501
|
+
*
|
|
1502
|
+
* @param refundId - Refund request ID
|
|
1503
|
+
* @param reason - Reason for rejection
|
|
1504
|
+
* @returns Updated refund request
|
|
1505
|
+
*/
|
|
1506
|
+
async rejectRefund(refundId, reason) {
|
|
1507
|
+
const url = `${this.baseUrl}/refund/${encodeURIComponent(refundId)}/reject`;
|
|
1508
|
+
const controller = new AbortController();
|
|
1509
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1510
|
+
try {
|
|
1511
|
+
const response = await fetch(url, {
|
|
1512
|
+
method: "POST",
|
|
1513
|
+
headers: this.getHeaders(true),
|
|
1514
|
+
body: JSON.stringify({ reason }),
|
|
1515
|
+
signal: controller.signal
|
|
1516
|
+
});
|
|
1517
|
+
clearTimeout(timeoutId);
|
|
1518
|
+
if (!response.ok) {
|
|
1519
|
+
const errorText = await response.text();
|
|
1520
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1521
|
+
}
|
|
1522
|
+
return await response.json();
|
|
1523
|
+
} catch (error) {
|
|
1524
|
+
clearTimeout(timeoutId);
|
|
1525
|
+
throw error;
|
|
1526
|
+
}
|
|
1527
|
+
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Get refund request by ID
|
|
1530
|
+
*
|
|
1531
|
+
* @param refundId - Refund request ID
|
|
1532
|
+
* @returns Refund request details
|
|
1533
|
+
*/
|
|
1534
|
+
async getRefund(refundId) {
|
|
1535
|
+
const url = `${this.baseUrl}/refund/${encodeURIComponent(refundId)}`;
|
|
1536
|
+
const controller = new AbortController();
|
|
1537
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1538
|
+
try {
|
|
1539
|
+
const response = await fetch(url, {
|
|
1540
|
+
method: "GET",
|
|
1541
|
+
headers: this.getHeaders(),
|
|
1542
|
+
signal: controller.signal
|
|
1543
|
+
});
|
|
1544
|
+
clearTimeout(timeoutId);
|
|
1545
|
+
if (!response.ok) {
|
|
1546
|
+
const errorText = await response.text();
|
|
1547
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1548
|
+
}
|
|
1549
|
+
return await response.json();
|
|
1550
|
+
} catch (error) {
|
|
1551
|
+
clearTimeout(timeoutId);
|
|
1552
|
+
throw error;
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
/**
|
|
1556
|
+
* Open a dispute for an escrow payment
|
|
1557
|
+
*
|
|
1558
|
+
* Initiates arbitration when payer and recipient disagree.
|
|
1559
|
+
*
|
|
1560
|
+
* @param escrowId - Escrow payment ID
|
|
1561
|
+
* @param reason - Reason for dispute
|
|
1562
|
+
* @param evidence - Supporting evidence
|
|
1563
|
+
* @returns Created dispute
|
|
1564
|
+
*/
|
|
1565
|
+
async openDispute(escrowId, reason, evidence) {
|
|
1566
|
+
const url = `${this.baseUrl}/escrow/${encodeURIComponent(escrowId)}/dispute`;
|
|
1567
|
+
const controller = new AbortController();
|
|
1568
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1569
|
+
try {
|
|
1570
|
+
const response = await fetch(url, {
|
|
1571
|
+
method: "POST",
|
|
1572
|
+
headers: this.getHeaders(true),
|
|
1573
|
+
body: JSON.stringify({ reason, evidence }),
|
|
1574
|
+
signal: controller.signal
|
|
1575
|
+
});
|
|
1576
|
+
clearTimeout(timeoutId);
|
|
1577
|
+
if (!response.ok) {
|
|
1578
|
+
const errorText = await response.text();
|
|
1579
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1580
|
+
}
|
|
1581
|
+
return await response.json();
|
|
1582
|
+
} catch (error) {
|
|
1583
|
+
clearTimeout(timeoutId);
|
|
1584
|
+
throw error;
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
/**
|
|
1588
|
+
* Submit evidence to a dispute
|
|
1589
|
+
*
|
|
1590
|
+
* @param disputeId - Dispute ID
|
|
1591
|
+
* @param evidence - Evidence to submit
|
|
1592
|
+
* @returns Updated dispute
|
|
1593
|
+
*/
|
|
1594
|
+
async submitEvidence(disputeId, evidence) {
|
|
1595
|
+
const url = `${this.baseUrl}/dispute/${encodeURIComponent(disputeId)}/evidence`;
|
|
1596
|
+
const controller = new AbortController();
|
|
1597
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1598
|
+
try {
|
|
1599
|
+
const response = await fetch(url, {
|
|
1600
|
+
method: "POST",
|
|
1601
|
+
headers: this.getHeaders(true),
|
|
1602
|
+
body: JSON.stringify({ evidence }),
|
|
1603
|
+
signal: controller.signal
|
|
1604
|
+
});
|
|
1605
|
+
clearTimeout(timeoutId);
|
|
1606
|
+
if (!response.ok) {
|
|
1607
|
+
const errorText = await response.text();
|
|
1608
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1609
|
+
}
|
|
1610
|
+
return await response.json();
|
|
1611
|
+
} catch (error) {
|
|
1612
|
+
clearTimeout(timeoutId);
|
|
1613
|
+
throw error;
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
/**
|
|
1617
|
+
* Get dispute by ID
|
|
1618
|
+
*
|
|
1619
|
+
* @param disputeId - Dispute ID
|
|
1620
|
+
* @returns Dispute details
|
|
1621
|
+
*/
|
|
1622
|
+
async getDispute(disputeId) {
|
|
1623
|
+
const url = `${this.baseUrl}/dispute/${encodeURIComponent(disputeId)}`;
|
|
1624
|
+
const controller = new AbortController();
|
|
1625
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1626
|
+
try {
|
|
1627
|
+
const response = await fetch(url, {
|
|
1628
|
+
method: "GET",
|
|
1629
|
+
headers: this.getHeaders(),
|
|
1630
|
+
signal: controller.signal
|
|
1631
|
+
});
|
|
1632
|
+
clearTimeout(timeoutId);
|
|
1633
|
+
if (!response.ok) {
|
|
1634
|
+
const errorText = await response.text();
|
|
1635
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1636
|
+
}
|
|
1637
|
+
return await response.json();
|
|
1638
|
+
} catch (error) {
|
|
1639
|
+
clearTimeout(timeoutId);
|
|
1640
|
+
throw error;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
/**
|
|
1644
|
+
* List escrow payments (with filters)
|
|
1645
|
+
*
|
|
1646
|
+
* @param options - Filter and pagination options
|
|
1647
|
+
* @returns Paginated list of escrow payments
|
|
1648
|
+
*/
|
|
1649
|
+
async listEscrows(options = {}) {
|
|
1650
|
+
const params = new URLSearchParams();
|
|
1651
|
+
if (options.status) params.set("status", options.status);
|
|
1652
|
+
if (options.payer) params.set("payer", options.payer);
|
|
1653
|
+
if (options.recipient) params.set("recipient", options.recipient);
|
|
1654
|
+
if (options.page) params.set("page", options.page.toString());
|
|
1655
|
+
if (options.limit) params.set("limit", options.limit.toString());
|
|
1656
|
+
const url = `${this.baseUrl}/escrow${params.toString() ? `?${params}` : ""}`;
|
|
1657
|
+
const controller = new AbortController();
|
|
1658
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
1659
|
+
try {
|
|
1660
|
+
const response = await fetch(url, {
|
|
1661
|
+
method: "GET",
|
|
1662
|
+
headers: this.getHeaders(true),
|
|
1663
|
+
signal: controller.signal
|
|
1664
|
+
});
|
|
1665
|
+
clearTimeout(timeoutId);
|
|
1666
|
+
if (!response.ok) {
|
|
1667
|
+
const errorText = await response.text();
|
|
1668
|
+
throw new Error(`Escrow API error: ${response.status} - ${errorText}`);
|
|
1669
|
+
}
|
|
1670
|
+
return await response.json();
|
|
1671
|
+
} catch (error) {
|
|
1672
|
+
clearTimeout(timeoutId);
|
|
1673
|
+
throw error;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Check Escrow API health
|
|
1678
|
+
*
|
|
1679
|
+
* @returns True if healthy
|
|
1680
|
+
*/
|
|
1681
|
+
async healthCheck() {
|
|
1682
|
+
try {
|
|
1683
|
+
const response = await fetch(`${this.baseUrl}/health`, {
|
|
1684
|
+
method: "GET"
|
|
1685
|
+
});
|
|
1686
|
+
return response.ok;
|
|
1687
|
+
} catch {
|
|
1688
|
+
return false;
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
};
|
|
1692
|
+
function canReleaseEscrow(escrow) {
|
|
1693
|
+
if (escrow.status !== "held") {
|
|
1694
|
+
return false;
|
|
1695
|
+
}
|
|
1696
|
+
if (new Date(escrow.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
1697
|
+
return false;
|
|
1698
|
+
}
|
|
1699
|
+
if (escrow.releaseConditions?.minHoldTime) {
|
|
1700
|
+
const createdAt = new Date(escrow.createdAt);
|
|
1701
|
+
const minReleaseTime = new Date(
|
|
1702
|
+
createdAt.getTime() + escrow.releaseConditions.minHoldTime * 1e3
|
|
1703
|
+
);
|
|
1704
|
+
if (/* @__PURE__ */ new Date() < minReleaseTime) {
|
|
1705
|
+
return false;
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
return true;
|
|
1709
|
+
}
|
|
1710
|
+
function canRefundEscrow(escrow) {
|
|
1711
|
+
return escrow.status === "held" || escrow.status === "pending";
|
|
1712
|
+
}
|
|
1713
|
+
function isEscrowExpired(escrow) {
|
|
1714
|
+
return new Date(escrow.expiresAt) < /* @__PURE__ */ new Date();
|
|
1715
|
+
}
|
|
1716
|
+
function escrowTimeRemaining(escrow) {
|
|
1717
|
+
return new Date(escrow.expiresAt).getTime() - Date.now();
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
exports.BazaarClient = BazaarClient;
|
|
1721
|
+
exports.EscrowClient = EscrowClient;
|
|
1722
|
+
exports.FacilitatorClient = FacilitatorClient;
|
|
1723
|
+
exports.X402_CORS_HEADERS = X402_CORS_HEADERS;
|
|
1724
|
+
exports.X402_HEADER_NAMES = X402_HEADER_NAMES;
|
|
1725
|
+
exports.buildPaymentRequirements = buildPaymentRequirements;
|
|
1726
|
+
exports.buildSettleRequest = buildSettleRequest;
|
|
1727
|
+
exports.buildVerifyRequest = buildVerifyRequest;
|
|
1728
|
+
exports.canRefundEscrow = canRefundEscrow;
|
|
1729
|
+
exports.canReleaseEscrow = canReleaseEscrow;
|
|
1730
|
+
exports.create402Response = create402Response;
|
|
1731
|
+
exports.createPaymentMiddleware = createPaymentMiddleware;
|
|
1732
|
+
exports.escrowTimeRemaining = escrowTimeRemaining;
|
|
1733
|
+
exports.extractPaymentFromHeaders = extractPaymentFromHeaders;
|
|
1734
|
+
exports.getCorsHeaders = getCorsHeaders;
|
|
1735
|
+
exports.isEscrowExpired = isEscrowExpired;
|
|
1736
|
+
exports.parsePaymentHeader = parsePaymentHeader;
|
|
1737
|
+
//# sourceMappingURL=index.js.map
|
|
1738
|
+
//# sourceMappingURL=index.js.map
|