uvd-x402-sdk 2.0.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/LICENSE +21 -0
- package/README.md +782 -0
- package/dist/index-BrBqP1I8.d.ts +199 -0
- package/dist/index-D6Sr4ARD.d.mts +429 -0
- package/dist/index-D6Sr4ARD.d.ts +429 -0
- package/dist/index-DJ4Cvrev.d.mts +199 -0
- package/dist/index.d.mts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1178 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1146 -0
- package/dist/index.mjs.map +1 -0
- package/dist/providers/evm/index.d.mts +84 -0
- package/dist/providers/evm/index.d.ts +84 -0
- package/dist/providers/evm/index.js +740 -0
- package/dist/providers/evm/index.js.map +1 -0
- package/dist/providers/evm/index.mjs +735 -0
- package/dist/providers/evm/index.mjs.map +1 -0
- package/dist/providers/near/index.d.mts +99 -0
- package/dist/providers/near/index.d.ts +99 -0
- package/dist/providers/near/index.js +483 -0
- package/dist/providers/near/index.js.map +1 -0
- package/dist/providers/near/index.mjs +478 -0
- package/dist/providers/near/index.mjs.map +1 -0
- package/dist/providers/solana/index.d.mts +115 -0
- package/dist/providers/solana/index.d.ts +115 -0
- package/dist/providers/solana/index.js +771 -0
- package/dist/providers/solana/index.js.map +1 -0
- package/dist/providers/solana/index.mjs +765 -0
- package/dist/providers/solana/index.mjs.map +1 -0
- package/dist/providers/stellar/index.d.mts +67 -0
- package/dist/providers/stellar/index.d.ts +67 -0
- package/dist/providers/stellar/index.js +306 -0
- package/dist/providers/stellar/index.js.map +1 -0
- package/dist/providers/stellar/index.mjs +301 -0
- package/dist/providers/stellar/index.mjs.map +1 -0
- package/dist/react/index.d.mts +73 -0
- package/dist/react/index.d.ts +73 -0
- package/dist/react/index.js +1218 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/index.mjs +1211 -0
- package/dist/react/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +103 -0
- package/dist/utils/index.d.ts +103 -0
- package/dist/utils/index.js +575 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +562 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +149 -0
- package/src/chains/index.ts +539 -0
- package/src/client/X402Client.ts +663 -0
- package/src/client/index.ts +1 -0
- package/src/index.ts +166 -0
- package/src/providers/evm/index.ts +394 -0
- package/src/providers/near/index.ts +664 -0
- package/src/providers/solana/index.ts +489 -0
- package/src/providers/stellar/index.ts +376 -0
- package/src/react/index.tsx +417 -0
- package/src/types/index.ts +561 -0
- package/src/utils/index.ts +20 -0
- package/src/utils/x402.ts +295 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1146 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
|
|
3
|
+
// src/client/X402Client.ts
|
|
4
|
+
|
|
5
|
+
// src/types/index.ts
|
|
6
|
+
var CAIP2_IDENTIFIERS = {
|
|
7
|
+
// EVM chains
|
|
8
|
+
base: "eip155:8453",
|
|
9
|
+
ethereum: "eip155:1",
|
|
10
|
+
polygon: "eip155:137",
|
|
11
|
+
arbitrum: "eip155:42161",
|
|
12
|
+
optimism: "eip155:10",
|
|
13
|
+
avalanche: "eip155:43114",
|
|
14
|
+
celo: "eip155:42220",
|
|
15
|
+
hyperevm: "eip155:999",
|
|
16
|
+
unichain: "eip155:130",
|
|
17
|
+
monad: "eip155:143",
|
|
18
|
+
bsc: "eip155:56",
|
|
19
|
+
// SVM chains
|
|
20
|
+
solana: "solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp",
|
|
21
|
+
fogo: "svm:fogo",
|
|
22
|
+
// Stellar
|
|
23
|
+
stellar: "stellar:pubnet",
|
|
24
|
+
// NEAR
|
|
25
|
+
near: "near:mainnet"
|
|
26
|
+
};
|
|
27
|
+
var CAIP2_TO_CHAIN = Object.fromEntries(
|
|
28
|
+
Object.entries(CAIP2_IDENTIFIERS).map(([k, v]) => [v, k])
|
|
29
|
+
);
|
|
30
|
+
var DEFAULT_CONFIG = {
|
|
31
|
+
facilitatorUrl: "https://facilitator.ultravioletadao.xyz",
|
|
32
|
+
defaultChain: "base",
|
|
33
|
+
autoConnect: false,
|
|
34
|
+
debug: false,
|
|
35
|
+
x402Version: "auto"
|
|
36
|
+
};
|
|
37
|
+
var X402Error = class _X402Error extends Error {
|
|
38
|
+
code;
|
|
39
|
+
details;
|
|
40
|
+
constructor(message, code, details) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = "X402Error";
|
|
43
|
+
this.code = code;
|
|
44
|
+
this.details = details;
|
|
45
|
+
if (Error.captureStackTrace) {
|
|
46
|
+
Error.captureStackTrace(this, _X402Error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/chains/index.ts
|
|
52
|
+
var DEFAULT_FACILITATOR_URL = "https://facilitator.ultravioletadao.xyz";
|
|
53
|
+
var SUPPORTED_CHAINS = {
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// EVM CHAINS (11 networks)
|
|
56
|
+
// ============================================================================
|
|
57
|
+
base: {
|
|
58
|
+
chainId: 8453,
|
|
59
|
+
chainIdHex: "0x2105",
|
|
60
|
+
name: "base",
|
|
61
|
+
displayName: "Base",
|
|
62
|
+
networkType: "evm",
|
|
63
|
+
rpcUrl: "https://mainnet.base.org",
|
|
64
|
+
explorerUrl: "https://basescan.org",
|
|
65
|
+
nativeCurrency: {
|
|
66
|
+
name: "Ethereum",
|
|
67
|
+
symbol: "ETH",
|
|
68
|
+
decimals: 18
|
|
69
|
+
},
|
|
70
|
+
usdc: {
|
|
71
|
+
address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
72
|
+
decimals: 6,
|
|
73
|
+
name: "USD Coin",
|
|
74
|
+
version: "2"
|
|
75
|
+
},
|
|
76
|
+
x402: {
|
|
77
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
78
|
+
enabled: true
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
avalanche: {
|
|
82
|
+
chainId: 43114,
|
|
83
|
+
chainIdHex: "0xa86a",
|
|
84
|
+
name: "avalanche",
|
|
85
|
+
displayName: "Avalanche C-Chain",
|
|
86
|
+
networkType: "evm",
|
|
87
|
+
rpcUrl: "https://avalanche-c-chain-rpc.publicnode.com",
|
|
88
|
+
explorerUrl: "https://snowtrace.io",
|
|
89
|
+
nativeCurrency: {
|
|
90
|
+
name: "Avalanche",
|
|
91
|
+
symbol: "AVAX",
|
|
92
|
+
decimals: 18
|
|
93
|
+
},
|
|
94
|
+
usdc: {
|
|
95
|
+
address: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
|
|
96
|
+
decimals: 6,
|
|
97
|
+
name: "USD Coin",
|
|
98
|
+
version: "2"
|
|
99
|
+
},
|
|
100
|
+
x402: {
|
|
101
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
102
|
+
enabled: true
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
ethereum: {
|
|
106
|
+
chainId: 1,
|
|
107
|
+
chainIdHex: "0x1",
|
|
108
|
+
name: "ethereum",
|
|
109
|
+
displayName: "Ethereum",
|
|
110
|
+
networkType: "evm",
|
|
111
|
+
rpcUrl: "https://eth.llamarpc.com",
|
|
112
|
+
explorerUrl: "https://etherscan.io",
|
|
113
|
+
nativeCurrency: {
|
|
114
|
+
name: "Ethereum",
|
|
115
|
+
symbol: "ETH",
|
|
116
|
+
decimals: 18
|
|
117
|
+
},
|
|
118
|
+
usdc: {
|
|
119
|
+
address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
|
|
120
|
+
decimals: 6,
|
|
121
|
+
name: "USD Coin",
|
|
122
|
+
version: "2"
|
|
123
|
+
},
|
|
124
|
+
x402: {
|
|
125
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
126
|
+
enabled: true
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
polygon: {
|
|
130
|
+
chainId: 137,
|
|
131
|
+
chainIdHex: "0x89",
|
|
132
|
+
name: "polygon",
|
|
133
|
+
displayName: "Polygon",
|
|
134
|
+
networkType: "evm",
|
|
135
|
+
rpcUrl: "https://polygon-rpc.com",
|
|
136
|
+
explorerUrl: "https://polygonscan.com",
|
|
137
|
+
nativeCurrency: {
|
|
138
|
+
name: "Polygon",
|
|
139
|
+
symbol: "POL",
|
|
140
|
+
decimals: 18
|
|
141
|
+
},
|
|
142
|
+
usdc: {
|
|
143
|
+
address: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
|
|
144
|
+
decimals: 6,
|
|
145
|
+
name: "USD Coin",
|
|
146
|
+
version: "2"
|
|
147
|
+
},
|
|
148
|
+
x402: {
|
|
149
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
150
|
+
enabled: true
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
arbitrum: {
|
|
154
|
+
chainId: 42161,
|
|
155
|
+
chainIdHex: "0xa4b1",
|
|
156
|
+
name: "arbitrum",
|
|
157
|
+
displayName: "Arbitrum One",
|
|
158
|
+
networkType: "evm",
|
|
159
|
+
rpcUrl: "https://arb1.arbitrum.io/rpc",
|
|
160
|
+
explorerUrl: "https://arbiscan.io",
|
|
161
|
+
nativeCurrency: {
|
|
162
|
+
name: "Ethereum",
|
|
163
|
+
symbol: "ETH",
|
|
164
|
+
decimals: 18
|
|
165
|
+
},
|
|
166
|
+
usdc: {
|
|
167
|
+
address: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
|
|
168
|
+
decimals: 6,
|
|
169
|
+
name: "USD Coin",
|
|
170
|
+
version: "2"
|
|
171
|
+
},
|
|
172
|
+
x402: {
|
|
173
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
174
|
+
enabled: true
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
optimism: {
|
|
178
|
+
chainId: 10,
|
|
179
|
+
chainIdHex: "0xa",
|
|
180
|
+
name: "optimism",
|
|
181
|
+
displayName: "Optimism",
|
|
182
|
+
networkType: "evm",
|
|
183
|
+
rpcUrl: "https://mainnet.optimism.io",
|
|
184
|
+
explorerUrl: "https://optimistic.etherscan.io",
|
|
185
|
+
nativeCurrency: {
|
|
186
|
+
name: "Ethereum",
|
|
187
|
+
symbol: "ETH",
|
|
188
|
+
decimals: 18
|
|
189
|
+
},
|
|
190
|
+
usdc: {
|
|
191
|
+
address: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
|
|
192
|
+
decimals: 6,
|
|
193
|
+
name: "USD Coin",
|
|
194
|
+
version: "2"
|
|
195
|
+
},
|
|
196
|
+
x402: {
|
|
197
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
198
|
+
enabled: true
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
celo: {
|
|
202
|
+
chainId: 42220,
|
|
203
|
+
chainIdHex: "0xa4ec",
|
|
204
|
+
name: "celo",
|
|
205
|
+
displayName: "Celo",
|
|
206
|
+
networkType: "evm",
|
|
207
|
+
rpcUrl: "https://forno.celo.org",
|
|
208
|
+
explorerUrl: "https://celoscan.io",
|
|
209
|
+
nativeCurrency: {
|
|
210
|
+
name: "Celo",
|
|
211
|
+
symbol: "CELO",
|
|
212
|
+
decimals: 18
|
|
213
|
+
},
|
|
214
|
+
usdc: {
|
|
215
|
+
address: "0xcebA9300f2b948710d2653dD7B07f33A8B32118C",
|
|
216
|
+
decimals: 6,
|
|
217
|
+
name: "USDC",
|
|
218
|
+
// Celo uses "USDC" not "USD Coin" for EIP-712
|
|
219
|
+
version: "2"
|
|
220
|
+
},
|
|
221
|
+
x402: {
|
|
222
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
223
|
+
enabled: true
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
hyperevm: {
|
|
227
|
+
chainId: 999,
|
|
228
|
+
chainIdHex: "0x3e7",
|
|
229
|
+
name: "hyperevm",
|
|
230
|
+
displayName: "HyperEVM",
|
|
231
|
+
networkType: "evm",
|
|
232
|
+
rpcUrl: "https://rpc.hyperliquid.xyz/evm",
|
|
233
|
+
explorerUrl: "https://hyperevmscan.io",
|
|
234
|
+
nativeCurrency: {
|
|
235
|
+
name: "Ethereum",
|
|
236
|
+
symbol: "ETH",
|
|
237
|
+
decimals: 18
|
|
238
|
+
},
|
|
239
|
+
usdc: {
|
|
240
|
+
address: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
|
|
241
|
+
decimals: 6,
|
|
242
|
+
name: "USDC",
|
|
243
|
+
// HyperEVM uses "USDC" not "USD Coin"
|
|
244
|
+
version: "2"
|
|
245
|
+
},
|
|
246
|
+
x402: {
|
|
247
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
248
|
+
enabled: true
|
|
249
|
+
}
|
|
250
|
+
},
|
|
251
|
+
unichain: {
|
|
252
|
+
chainId: 130,
|
|
253
|
+
chainIdHex: "0x82",
|
|
254
|
+
name: "unichain",
|
|
255
|
+
displayName: "Unichain",
|
|
256
|
+
networkType: "evm",
|
|
257
|
+
rpcUrl: "https://unichain-rpc.publicnode.com",
|
|
258
|
+
explorerUrl: "https://uniscan.xyz",
|
|
259
|
+
nativeCurrency: {
|
|
260
|
+
name: "Ethereum",
|
|
261
|
+
symbol: "ETH",
|
|
262
|
+
decimals: 18
|
|
263
|
+
},
|
|
264
|
+
usdc: {
|
|
265
|
+
address: "0x078d782b760474a361dda0af3839290b0ef57ad6",
|
|
266
|
+
decimals: 6,
|
|
267
|
+
name: "USDC",
|
|
268
|
+
// Unichain uses "USDC" not "USD Coin"
|
|
269
|
+
version: "2"
|
|
270
|
+
},
|
|
271
|
+
x402: {
|
|
272
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
273
|
+
enabled: true
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
monad: {
|
|
277
|
+
chainId: 143,
|
|
278
|
+
chainIdHex: "0x8f",
|
|
279
|
+
name: "monad",
|
|
280
|
+
displayName: "Monad",
|
|
281
|
+
networkType: "evm",
|
|
282
|
+
rpcUrl: "https://rpc.monad.xyz",
|
|
283
|
+
explorerUrl: "https://monad.socialscan.io",
|
|
284
|
+
nativeCurrency: {
|
|
285
|
+
name: "Monad",
|
|
286
|
+
symbol: "MON",
|
|
287
|
+
decimals: 18
|
|
288
|
+
},
|
|
289
|
+
usdc: {
|
|
290
|
+
address: "0x754704bc059f8c67012fed69bc8a327a5aafb603",
|
|
291
|
+
decimals: 6,
|
|
292
|
+
name: "USDC",
|
|
293
|
+
// Monad uses "USDC" not "USD Coin"
|
|
294
|
+
version: "2"
|
|
295
|
+
},
|
|
296
|
+
x402: {
|
|
297
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
298
|
+
enabled: true
|
|
299
|
+
}
|
|
300
|
+
},
|
|
301
|
+
// BSC disabled: USDC doesn't support ERC-3009 transferWithAuthorization
|
|
302
|
+
bsc: {
|
|
303
|
+
chainId: 56,
|
|
304
|
+
chainIdHex: "0x38",
|
|
305
|
+
name: "bsc",
|
|
306
|
+
displayName: "BNB Smart Chain",
|
|
307
|
+
networkType: "evm",
|
|
308
|
+
rpcUrl: "https://binance.llamarpc.com",
|
|
309
|
+
explorerUrl: "https://bscscan.com",
|
|
310
|
+
nativeCurrency: {
|
|
311
|
+
name: "Binance Coin",
|
|
312
|
+
symbol: "BNB",
|
|
313
|
+
decimals: 18
|
|
314
|
+
},
|
|
315
|
+
usdc: {
|
|
316
|
+
address: "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d",
|
|
317
|
+
decimals: 18,
|
|
318
|
+
// BSC USDC uses 18 decimals
|
|
319
|
+
name: "USD Coin",
|
|
320
|
+
version: "2"
|
|
321
|
+
},
|
|
322
|
+
x402: {
|
|
323
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
324
|
+
enabled: false
|
|
325
|
+
// Disabled: BSC USDC doesn't support ERC-3009
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
// ============================================================================
|
|
329
|
+
// SVM CHAINS (2 networks) - Solana Virtual Machine
|
|
330
|
+
// ============================================================================
|
|
331
|
+
solana: {
|
|
332
|
+
chainId: 0,
|
|
333
|
+
// Non-EVM
|
|
334
|
+
chainIdHex: "0x0",
|
|
335
|
+
name: "solana",
|
|
336
|
+
displayName: "Solana",
|
|
337
|
+
networkType: "svm",
|
|
338
|
+
rpcUrl: "https://api.mainnet-beta.solana.com",
|
|
339
|
+
explorerUrl: "https://solscan.io",
|
|
340
|
+
nativeCurrency: {
|
|
341
|
+
name: "Solana",
|
|
342
|
+
symbol: "SOL",
|
|
343
|
+
decimals: 9
|
|
344
|
+
},
|
|
345
|
+
usdc: {
|
|
346
|
+
address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
|
|
347
|
+
// USDC SPL token mint
|
|
348
|
+
decimals: 6,
|
|
349
|
+
name: "USD Coin",
|
|
350
|
+
version: "1"
|
|
351
|
+
},
|
|
352
|
+
x402: {
|
|
353
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
354
|
+
enabled: true
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
fogo: {
|
|
358
|
+
chainId: 0,
|
|
359
|
+
// Non-EVM (SVM)
|
|
360
|
+
chainIdHex: "0x0",
|
|
361
|
+
name: "fogo",
|
|
362
|
+
displayName: "Fogo",
|
|
363
|
+
networkType: "svm",
|
|
364
|
+
rpcUrl: "https://rpc.fogo.nightly.app/",
|
|
365
|
+
explorerUrl: "https://explorer.fogo.nightly.app",
|
|
366
|
+
nativeCurrency: {
|
|
367
|
+
name: "Fogo",
|
|
368
|
+
symbol: "FOGO",
|
|
369
|
+
decimals: 9
|
|
370
|
+
},
|
|
371
|
+
usdc: {
|
|
372
|
+
address: "uSd2czE61Evaf76RNbq4KPpXnkiL3irdzgLFUMe3NoG",
|
|
373
|
+
// Fogo USDC mint
|
|
374
|
+
decimals: 6,
|
|
375
|
+
name: "USDC",
|
|
376
|
+
version: "1"
|
|
377
|
+
},
|
|
378
|
+
x402: {
|
|
379
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
380
|
+
enabled: true
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
// ============================================================================
|
|
384
|
+
// STELLAR (1 network)
|
|
385
|
+
// ============================================================================
|
|
386
|
+
stellar: {
|
|
387
|
+
chainId: 0,
|
|
388
|
+
// Non-EVM
|
|
389
|
+
chainIdHex: "0x0",
|
|
390
|
+
name: "stellar",
|
|
391
|
+
displayName: "Stellar",
|
|
392
|
+
networkType: "stellar",
|
|
393
|
+
rpcUrl: "https://horizon.stellar.org",
|
|
394
|
+
explorerUrl: "https://stellar.expert/explorer/public",
|
|
395
|
+
nativeCurrency: {
|
|
396
|
+
name: "Lumens",
|
|
397
|
+
symbol: "XLM",
|
|
398
|
+
decimals: 7
|
|
399
|
+
// Stellar uses 7 decimals (stroops)
|
|
400
|
+
},
|
|
401
|
+
usdc: {
|
|
402
|
+
address: "CCW67TSZV3SSS2HXMBQ5JFGCKJNXKZM7UQUWUZPUTHXSTZLEO7SJMI75",
|
|
403
|
+
// Soroban Asset Contract
|
|
404
|
+
decimals: 7,
|
|
405
|
+
// Stellar USDC uses 7 decimals
|
|
406
|
+
name: "USDC",
|
|
407
|
+
version: "1"
|
|
408
|
+
},
|
|
409
|
+
x402: {
|
|
410
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
411
|
+
enabled: true
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
// ============================================================================
|
|
415
|
+
// NEAR (1 network) - Uses NEP-366 meta-transactions
|
|
416
|
+
// ============================================================================
|
|
417
|
+
near: {
|
|
418
|
+
chainId: 0,
|
|
419
|
+
// Non-EVM
|
|
420
|
+
chainIdHex: "0x0",
|
|
421
|
+
name: "near",
|
|
422
|
+
displayName: "NEAR Protocol",
|
|
423
|
+
networkType: "near",
|
|
424
|
+
rpcUrl: "https://rpc.mainnet.near.org",
|
|
425
|
+
explorerUrl: "https://nearblocks.io",
|
|
426
|
+
nativeCurrency: {
|
|
427
|
+
name: "NEAR",
|
|
428
|
+
symbol: "NEAR",
|
|
429
|
+
decimals: 24
|
|
430
|
+
// NEAR uses 24 decimals (yoctoNEAR)
|
|
431
|
+
},
|
|
432
|
+
usdc: {
|
|
433
|
+
address: "17208628f84f5d6ad33f0da3bbbeb27ffcb398eac501a31bd6ad2011e36133a1",
|
|
434
|
+
// Native Circle USDC
|
|
435
|
+
decimals: 6,
|
|
436
|
+
name: "USDC",
|
|
437
|
+
version: "1"
|
|
438
|
+
},
|
|
439
|
+
x402: {
|
|
440
|
+
facilitatorUrl: DEFAULT_FACILITATOR_URL,
|
|
441
|
+
enabled: true
|
|
442
|
+
// NEP-366 meta-transactions supported
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
var DEFAULT_CHAIN = "base";
|
|
447
|
+
function getChainById(chainId) {
|
|
448
|
+
return Object.values(SUPPORTED_CHAINS).find((chain) => chain.chainId === chainId);
|
|
449
|
+
}
|
|
450
|
+
function getChainByName(name) {
|
|
451
|
+
return SUPPORTED_CHAINS[name.toLowerCase()];
|
|
452
|
+
}
|
|
453
|
+
function isChainSupported(chainIdOrName) {
|
|
454
|
+
if (typeof chainIdOrName === "number") {
|
|
455
|
+
return Object.values(SUPPORTED_CHAINS).some((chain) => chain.chainId === chainIdOrName);
|
|
456
|
+
}
|
|
457
|
+
return chainIdOrName.toLowerCase() in SUPPORTED_CHAINS;
|
|
458
|
+
}
|
|
459
|
+
function getEnabledChains() {
|
|
460
|
+
return Object.values(SUPPORTED_CHAINS).filter((chain) => chain.x402.enabled);
|
|
461
|
+
}
|
|
462
|
+
function getChainsByNetworkType(networkType) {
|
|
463
|
+
return Object.values(SUPPORTED_CHAINS).filter(
|
|
464
|
+
(chain) => chain.networkType === networkType && chain.x402.enabled
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
function getEVMChainIds() {
|
|
468
|
+
return getChainsByNetworkType("evm").map((chain) => chain.chainId);
|
|
469
|
+
}
|
|
470
|
+
function getSVMChains() {
|
|
471
|
+
return Object.values(SUPPORTED_CHAINS).filter(
|
|
472
|
+
(chain) => chain.networkType === "svm" && chain.x402.enabled
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
function isSVMChain(chainName) {
|
|
476
|
+
const chain = getChainByName(chainName);
|
|
477
|
+
return chain?.networkType === "svm";
|
|
478
|
+
}
|
|
479
|
+
function getNetworkType(chainName) {
|
|
480
|
+
const chain = getChainByName(chainName);
|
|
481
|
+
return chain?.networkType;
|
|
482
|
+
}
|
|
483
|
+
function getExplorerTxUrl(chainName, txHash) {
|
|
484
|
+
const chain = getChainByName(chainName);
|
|
485
|
+
if (!chain) return null;
|
|
486
|
+
switch (chain.networkType) {
|
|
487
|
+
case "evm":
|
|
488
|
+
return `${chain.explorerUrl}/tx/${txHash}`;
|
|
489
|
+
case "svm":
|
|
490
|
+
case "solana":
|
|
491
|
+
return `${chain.explorerUrl}/tx/${txHash}`;
|
|
492
|
+
case "stellar":
|
|
493
|
+
return `${chain.explorerUrl}/tx/${txHash}`;
|
|
494
|
+
case "near":
|
|
495
|
+
return `${chain.explorerUrl}/txns/${txHash}`;
|
|
496
|
+
default:
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
function getExplorerAddressUrl(chainName, address) {
|
|
501
|
+
const chain = getChainByName(chainName);
|
|
502
|
+
if (!chain) return null;
|
|
503
|
+
switch (chain.networkType) {
|
|
504
|
+
case "evm":
|
|
505
|
+
return `${chain.explorerUrl}/address/${address}`;
|
|
506
|
+
case "svm":
|
|
507
|
+
case "solana":
|
|
508
|
+
return `${chain.explorerUrl}/account/${address}`;
|
|
509
|
+
case "stellar":
|
|
510
|
+
return `${chain.explorerUrl}/account/${address}`;
|
|
511
|
+
case "near":
|
|
512
|
+
return `${chain.explorerUrl}/address/${address}`;
|
|
513
|
+
default:
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// src/client/X402Client.ts
|
|
519
|
+
var X402Client = class {
|
|
520
|
+
// Configuration
|
|
521
|
+
config;
|
|
522
|
+
// Wallet state
|
|
523
|
+
provider = null;
|
|
524
|
+
signer = null;
|
|
525
|
+
connectedAddress = null;
|
|
526
|
+
currentChainId = null;
|
|
527
|
+
currentNetwork = null;
|
|
528
|
+
currentChainName = null;
|
|
529
|
+
// Event emitter
|
|
530
|
+
eventHandlers = /* @__PURE__ */ new Map();
|
|
531
|
+
constructor(config = {}) {
|
|
532
|
+
this.config = {
|
|
533
|
+
...DEFAULT_CONFIG,
|
|
534
|
+
...config
|
|
535
|
+
};
|
|
536
|
+
if (config.rpcOverrides) {
|
|
537
|
+
for (const [chainName, rpcUrl] of Object.entries(config.rpcOverrides)) {
|
|
538
|
+
if (SUPPORTED_CHAINS[chainName]) {
|
|
539
|
+
SUPPORTED_CHAINS[chainName].rpcUrl = rpcUrl;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
if (config.customChains) {
|
|
544
|
+
for (const [chainName, chainConfig] of Object.entries(config.customChains)) {
|
|
545
|
+
if (SUPPORTED_CHAINS[chainName]) {
|
|
546
|
+
Object.assign(SUPPORTED_CHAINS[chainName], chainConfig);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
this.log("X402Client initialized", { config: this.config });
|
|
551
|
+
}
|
|
552
|
+
// ============================================================================
|
|
553
|
+
// PUBLIC API - Wallet Connection
|
|
554
|
+
// ============================================================================
|
|
555
|
+
/**
|
|
556
|
+
* Connect to a wallet on the specified chain
|
|
557
|
+
*/
|
|
558
|
+
async connect(chainName) {
|
|
559
|
+
const targetChain = chainName || this.config.defaultChain;
|
|
560
|
+
const chain = getChainByName(targetChain);
|
|
561
|
+
if (!chain) {
|
|
562
|
+
throw new X402Error(`Unsupported chain: ${targetChain}`, "CHAIN_NOT_SUPPORTED");
|
|
563
|
+
}
|
|
564
|
+
if (!chain.x402.enabled) {
|
|
565
|
+
throw new X402Error(`Chain ${targetChain} is not enabled for x402 payments`, "CHAIN_NOT_SUPPORTED");
|
|
566
|
+
}
|
|
567
|
+
this.log(`Connecting wallet on ${chain.displayName}...`);
|
|
568
|
+
switch (chain.networkType) {
|
|
569
|
+
case "evm":
|
|
570
|
+
return this.connectEVMWallet(chain);
|
|
571
|
+
case "solana":
|
|
572
|
+
throw new X402Error(
|
|
573
|
+
'Solana support requires importing from "uvd-x402-sdk/solana"',
|
|
574
|
+
"CHAIN_NOT_SUPPORTED"
|
|
575
|
+
);
|
|
576
|
+
case "stellar":
|
|
577
|
+
throw new X402Error(
|
|
578
|
+
'Stellar support requires importing from "uvd-x402-sdk/stellar"',
|
|
579
|
+
"CHAIN_NOT_SUPPORTED"
|
|
580
|
+
);
|
|
581
|
+
case "near":
|
|
582
|
+
throw new X402Error("NEAR is not yet supported by the facilitator", "CHAIN_NOT_SUPPORTED");
|
|
583
|
+
default:
|
|
584
|
+
throw new X402Error(`Unknown network type for chain ${targetChain}`, "CHAIN_NOT_SUPPORTED");
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Disconnect the current wallet
|
|
589
|
+
*/
|
|
590
|
+
async disconnect() {
|
|
591
|
+
this.provider = null;
|
|
592
|
+
this.signer = null;
|
|
593
|
+
this.connectedAddress = null;
|
|
594
|
+
this.currentChainId = null;
|
|
595
|
+
this.currentNetwork = null;
|
|
596
|
+
this.currentChainName = null;
|
|
597
|
+
this.emit("disconnect", void 0);
|
|
598
|
+
this.log("Wallet disconnected");
|
|
599
|
+
}
|
|
600
|
+
/**
|
|
601
|
+
* Switch to a different chain (EVM only)
|
|
602
|
+
*/
|
|
603
|
+
async switchChain(chainName) {
|
|
604
|
+
const chain = getChainByName(chainName);
|
|
605
|
+
if (!chain) {
|
|
606
|
+
throw new X402Error(`Unsupported chain: ${chainName}`, "CHAIN_NOT_SUPPORTED");
|
|
607
|
+
}
|
|
608
|
+
if (chain.networkType !== "evm") {
|
|
609
|
+
throw new X402Error(
|
|
610
|
+
"switchChain is only supported for EVM networks. Reconnect with connect() for other networks.",
|
|
611
|
+
"CHAIN_NOT_SUPPORTED"
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
if (!this.provider) {
|
|
615
|
+
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|
|
616
|
+
}
|
|
617
|
+
await this.switchEVMChain(chain);
|
|
618
|
+
}
|
|
619
|
+
// ============================================================================
|
|
620
|
+
// PUBLIC API - Payment Creation
|
|
621
|
+
// ============================================================================
|
|
622
|
+
/**
|
|
623
|
+
* Create a payment authorization
|
|
624
|
+
*
|
|
625
|
+
* @param paymentInfo - Payment information from 402 response
|
|
626
|
+
* @returns Payment result with encoded X-PAYMENT header
|
|
627
|
+
*/
|
|
628
|
+
async createPayment(paymentInfo) {
|
|
629
|
+
if (!this.connectedAddress) {
|
|
630
|
+
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|
|
631
|
+
}
|
|
632
|
+
if (!this.currentChainName) {
|
|
633
|
+
throw new X402Error("Chain not set", "CHAIN_NOT_SUPPORTED");
|
|
634
|
+
}
|
|
635
|
+
const chain = getChainByName(this.currentChainName);
|
|
636
|
+
if (!chain) {
|
|
637
|
+
throw new X402Error(`Chain ${this.currentChainName} not found`, "CHAIN_NOT_SUPPORTED");
|
|
638
|
+
}
|
|
639
|
+
this.emit("paymentStarted", { amount: paymentInfo.amount, network: chain.name });
|
|
640
|
+
this.log("Creating payment...", { paymentInfo, chain: chain.name });
|
|
641
|
+
try {
|
|
642
|
+
switch (chain.networkType) {
|
|
643
|
+
case "evm":
|
|
644
|
+
return await this.createEVMPayment(paymentInfo, chain);
|
|
645
|
+
default:
|
|
646
|
+
throw new X402Error(
|
|
647
|
+
`Payment creation for ${chain.networkType} requires the appropriate provider`,
|
|
648
|
+
"CHAIN_NOT_SUPPORTED"
|
|
649
|
+
);
|
|
650
|
+
}
|
|
651
|
+
} catch (error) {
|
|
652
|
+
const x402Error = error instanceof X402Error ? error : new X402Error(
|
|
653
|
+
error instanceof Error ? error.message : "Unknown error",
|
|
654
|
+
"PAYMENT_FAILED",
|
|
655
|
+
error
|
|
656
|
+
);
|
|
657
|
+
this.emit("paymentFailed", { error: x402Error.message, code: x402Error.code });
|
|
658
|
+
throw x402Error;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Check USDC balance on current chain
|
|
663
|
+
*/
|
|
664
|
+
async getBalance() {
|
|
665
|
+
if (!this.connectedAddress || !this.currentChainName) {
|
|
666
|
+
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|
|
667
|
+
}
|
|
668
|
+
const chain = getChainByName(this.currentChainName);
|
|
669
|
+
if (!chain) {
|
|
670
|
+
throw new X402Error(`Chain ${this.currentChainName} not found`, "CHAIN_NOT_SUPPORTED");
|
|
671
|
+
}
|
|
672
|
+
switch (chain.networkType) {
|
|
673
|
+
case "evm":
|
|
674
|
+
return this.getEVMBalance(chain);
|
|
675
|
+
default:
|
|
676
|
+
throw new X402Error(
|
|
677
|
+
`Balance check for ${chain.networkType} requires the appropriate provider`,
|
|
678
|
+
"CHAIN_NOT_SUPPORTED"
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
// ============================================================================
|
|
683
|
+
// PUBLIC API - Getters
|
|
684
|
+
// ============================================================================
|
|
685
|
+
/**
|
|
686
|
+
* Get current wallet state
|
|
687
|
+
*/
|
|
688
|
+
getState() {
|
|
689
|
+
return {
|
|
690
|
+
connected: this.connectedAddress !== null,
|
|
691
|
+
address: this.connectedAddress,
|
|
692
|
+
chainId: this.currentChainId,
|
|
693
|
+
network: this.currentChainName,
|
|
694
|
+
networkType: this.currentNetwork,
|
|
695
|
+
balance: null
|
|
696
|
+
// Call getBalance() separately
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
/**
|
|
700
|
+
* Get connected wallet address
|
|
701
|
+
*/
|
|
702
|
+
getAddress() {
|
|
703
|
+
return this.connectedAddress;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* Get current chain ID
|
|
707
|
+
*/
|
|
708
|
+
getChainId() {
|
|
709
|
+
return this.currentChainId;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Get current chain name
|
|
713
|
+
*/
|
|
714
|
+
getChainName() {
|
|
715
|
+
return this.currentChainName;
|
|
716
|
+
}
|
|
717
|
+
/**
|
|
718
|
+
* Get current chain display name
|
|
719
|
+
*/
|
|
720
|
+
getChainDisplayName() {
|
|
721
|
+
if (!this.currentChainName) return null;
|
|
722
|
+
const chain = getChainByName(this.currentChainName);
|
|
723
|
+
return chain?.displayName ?? null;
|
|
724
|
+
}
|
|
725
|
+
/**
|
|
726
|
+
* Check if wallet is connected
|
|
727
|
+
*/
|
|
728
|
+
isConnected() {
|
|
729
|
+
return this.connectedAddress !== null;
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Get list of enabled chains
|
|
733
|
+
*/
|
|
734
|
+
getEnabledChains() {
|
|
735
|
+
return getEnabledChains();
|
|
736
|
+
}
|
|
737
|
+
/**
|
|
738
|
+
* Get chain config by name
|
|
739
|
+
*/
|
|
740
|
+
getChain(name) {
|
|
741
|
+
return getChainByName(name);
|
|
742
|
+
}
|
|
743
|
+
// ============================================================================
|
|
744
|
+
// PUBLIC API - Events
|
|
745
|
+
// ============================================================================
|
|
746
|
+
/**
|
|
747
|
+
* Subscribe to an event
|
|
748
|
+
*/
|
|
749
|
+
on(event, handler) {
|
|
750
|
+
if (!this.eventHandlers.has(event)) {
|
|
751
|
+
this.eventHandlers.set(event, /* @__PURE__ */ new Set());
|
|
752
|
+
}
|
|
753
|
+
this.eventHandlers.get(event).add(handler);
|
|
754
|
+
return () => this.off(event, handler);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Unsubscribe from an event
|
|
758
|
+
*/
|
|
759
|
+
off(event, handler) {
|
|
760
|
+
this.eventHandlers.get(event)?.delete(handler);
|
|
761
|
+
}
|
|
762
|
+
// ============================================================================
|
|
763
|
+
// PRIVATE - EVM Wallet Connection
|
|
764
|
+
// ============================================================================
|
|
765
|
+
async connectEVMWallet(chain) {
|
|
766
|
+
if (typeof window === "undefined" || !window.ethereum) {
|
|
767
|
+
throw new X402Error(
|
|
768
|
+
"No Ethereum wallet found. Please install MetaMask or another EVM wallet.",
|
|
769
|
+
"WALLET_NOT_FOUND"
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
try {
|
|
773
|
+
this.provider = new ethers.BrowserProvider(window.ethereum);
|
|
774
|
+
await this.provider.send("eth_requestAccounts", []);
|
|
775
|
+
await this.switchEVMChain(chain);
|
|
776
|
+
this.signer = await this.provider.getSigner();
|
|
777
|
+
this.connectedAddress = await this.signer.getAddress();
|
|
778
|
+
this.currentChainId = chain.chainId;
|
|
779
|
+
this.currentNetwork = "evm";
|
|
780
|
+
this.currentChainName = chain.name;
|
|
781
|
+
this.setupEVMEventListeners();
|
|
782
|
+
const state = this.getState();
|
|
783
|
+
this.emit("connect", state);
|
|
784
|
+
this.log("EVM wallet connected", { address: this.connectedAddress, chain: chain.name });
|
|
785
|
+
return this.connectedAddress;
|
|
786
|
+
} catch (error) {
|
|
787
|
+
if (error instanceof Error) {
|
|
788
|
+
if (error.message.includes("User rejected") || error.code === 4001) {
|
|
789
|
+
throw new X402Error("Connection rejected by user", "WALLET_CONNECTION_REJECTED");
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
throw new X402Error(
|
|
793
|
+
`Failed to connect wallet: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
794
|
+
"UNKNOWN_ERROR",
|
|
795
|
+
error
|
|
796
|
+
);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
async switchEVMChain(chain) {
|
|
800
|
+
if (!this.provider) {
|
|
801
|
+
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
await this.provider.send("wallet_switchEthereumChain", [{ chainId: chain.chainIdHex }]);
|
|
805
|
+
} catch (switchError) {
|
|
806
|
+
if (switchError.code === 4902) {
|
|
807
|
+
try {
|
|
808
|
+
await this.provider.send("wallet_addEthereumChain", [
|
|
809
|
+
{
|
|
810
|
+
chainId: chain.chainIdHex,
|
|
811
|
+
chainName: chain.displayName,
|
|
812
|
+
nativeCurrency: chain.nativeCurrency,
|
|
813
|
+
rpcUrls: [chain.rpcUrl],
|
|
814
|
+
blockExplorerUrls: [chain.explorerUrl]
|
|
815
|
+
}
|
|
816
|
+
]);
|
|
817
|
+
} catch (addError) {
|
|
818
|
+
throw new X402Error(
|
|
819
|
+
`Failed to add ${chain.displayName} network`,
|
|
820
|
+
"CHAIN_SWITCH_REJECTED",
|
|
821
|
+
addError
|
|
822
|
+
);
|
|
823
|
+
}
|
|
824
|
+
} else if (switchError.code === 4001) {
|
|
825
|
+
throw new X402Error("Network switch rejected by user", "CHAIN_SWITCH_REJECTED");
|
|
826
|
+
} else {
|
|
827
|
+
throw new X402Error(
|
|
828
|
+
`Failed to switch to ${chain.displayName}`,
|
|
829
|
+
"CHAIN_SWITCH_REJECTED",
|
|
830
|
+
switchError
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
this.currentChainId = chain.chainId;
|
|
835
|
+
this.currentChainName = chain.name;
|
|
836
|
+
this.emit("chainChanged", { chainId: chain.chainId, chainName: chain.name });
|
|
837
|
+
}
|
|
838
|
+
setupEVMEventListeners() {
|
|
839
|
+
if (typeof window === "undefined" || !window.ethereum) return;
|
|
840
|
+
window.ethereum.on?.("accountsChanged", ((...args) => {
|
|
841
|
+
const accounts = args[0];
|
|
842
|
+
if (accounts.length === 0) {
|
|
843
|
+
this.disconnect();
|
|
844
|
+
} else if (accounts[0] !== this.connectedAddress) {
|
|
845
|
+
this.connectedAddress = accounts[0];
|
|
846
|
+
this.emit("accountChanged", { address: accounts[0] });
|
|
847
|
+
}
|
|
848
|
+
}));
|
|
849
|
+
window.ethereum.on?.("chainChanged", ((...args) => {
|
|
850
|
+
const chainIdHex = args[0];
|
|
851
|
+
const chainId = parseInt(chainIdHex, 16);
|
|
852
|
+
const chain = getChainById(chainId);
|
|
853
|
+
if (chain) {
|
|
854
|
+
this.currentChainId = chainId;
|
|
855
|
+
this.currentChainName = chain.name;
|
|
856
|
+
this.emit("chainChanged", { chainId, chainName: chain.name });
|
|
857
|
+
}
|
|
858
|
+
}));
|
|
859
|
+
}
|
|
860
|
+
// ============================================================================
|
|
861
|
+
// PRIVATE - EVM Payment Creation
|
|
862
|
+
// ============================================================================
|
|
863
|
+
async createEVMPayment(paymentInfo, chain) {
|
|
864
|
+
if (!this.signer) {
|
|
865
|
+
throw new X402Error("Wallet not connected", "WALLET_NOT_CONNECTED");
|
|
866
|
+
}
|
|
867
|
+
const recipient = this.getRecipientForNetwork(paymentInfo, "evm");
|
|
868
|
+
const nonceBytes = new Uint8Array(32);
|
|
869
|
+
if (typeof window !== "undefined" && window.crypto) {
|
|
870
|
+
window.crypto.getRandomValues(nonceBytes);
|
|
871
|
+
} else {
|
|
872
|
+
for (let i = 0; i < 32; i++) {
|
|
873
|
+
nonceBytes[i] = Math.floor(Math.random() * 256);
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
const nonce = ethers.hexlify(nonceBytes);
|
|
877
|
+
const validAfter = 0;
|
|
878
|
+
const validityWindowSeconds = chain.name === "base" ? 300 : 60;
|
|
879
|
+
const validBefore = Math.floor(Date.now() / 1e3) + validityWindowSeconds;
|
|
880
|
+
const domain = {
|
|
881
|
+
name: chain.usdc.name,
|
|
882
|
+
version: chain.usdc.version,
|
|
883
|
+
chainId: chain.chainId,
|
|
884
|
+
verifyingContract: chain.usdc.address
|
|
885
|
+
};
|
|
886
|
+
const types = {
|
|
887
|
+
TransferWithAuthorization: [
|
|
888
|
+
{ name: "from", type: "address" },
|
|
889
|
+
{ name: "to", type: "address" },
|
|
890
|
+
{ name: "value", type: "uint256" },
|
|
891
|
+
{ name: "validAfter", type: "uint256" },
|
|
892
|
+
{ name: "validBefore", type: "uint256" },
|
|
893
|
+
{ name: "nonce", type: "bytes32" }
|
|
894
|
+
]
|
|
895
|
+
};
|
|
896
|
+
const value = ethers.parseUnits(paymentInfo.amount, chain.usdc.decimals);
|
|
897
|
+
const from = await this.signer.getAddress();
|
|
898
|
+
const to = ethers.getAddress(recipient);
|
|
899
|
+
const message = {
|
|
900
|
+
from,
|
|
901
|
+
to,
|
|
902
|
+
value,
|
|
903
|
+
validAfter,
|
|
904
|
+
validBefore,
|
|
905
|
+
nonce
|
|
906
|
+
};
|
|
907
|
+
this.log("Signing EIP-712 message...", { domain, message });
|
|
908
|
+
let signature;
|
|
909
|
+
try {
|
|
910
|
+
signature = await this.signer.signTypedData(domain, types, message);
|
|
911
|
+
} catch (error) {
|
|
912
|
+
if (error instanceof Error && (error.message.includes("User rejected") || error.code === 4001)) {
|
|
913
|
+
throw new X402Error("Signature rejected by user", "SIGNATURE_REJECTED");
|
|
914
|
+
}
|
|
915
|
+
throw new X402Error(
|
|
916
|
+
`Failed to sign payment: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
917
|
+
"PAYMENT_FAILED",
|
|
918
|
+
error
|
|
919
|
+
);
|
|
920
|
+
}
|
|
921
|
+
const sig = ethers.Signature.from(signature);
|
|
922
|
+
const payload = {
|
|
923
|
+
from,
|
|
924
|
+
to,
|
|
925
|
+
value: value.toString(),
|
|
926
|
+
validAfter,
|
|
927
|
+
validBefore,
|
|
928
|
+
nonce,
|
|
929
|
+
v: sig.v,
|
|
930
|
+
r: sig.r,
|
|
931
|
+
s: sig.s,
|
|
932
|
+
chainId: chain.chainId,
|
|
933
|
+
token: chain.usdc.address
|
|
934
|
+
};
|
|
935
|
+
const paymentHeader = this.encodeEVMPaymentHeader(payload, chain);
|
|
936
|
+
this.emit("paymentSigned", { paymentHeader });
|
|
937
|
+
const result = {
|
|
938
|
+
success: true,
|
|
939
|
+
paymentHeader,
|
|
940
|
+
network: chain.name,
|
|
941
|
+
payer: from
|
|
942
|
+
};
|
|
943
|
+
this.emit("paymentCompleted", result);
|
|
944
|
+
this.log("Payment created successfully", { network: chain.name, from });
|
|
945
|
+
return result;
|
|
946
|
+
}
|
|
947
|
+
encodeEVMPaymentHeader(payload, chain) {
|
|
948
|
+
const fullSignature = payload.r + payload.s.slice(2) + payload.v.toString(16).padStart(2, "0");
|
|
949
|
+
const x402Payload = {
|
|
950
|
+
x402Version: 1,
|
|
951
|
+
scheme: "exact",
|
|
952
|
+
network: chain.name,
|
|
953
|
+
payload: {
|
|
954
|
+
signature: fullSignature,
|
|
955
|
+
authorization: {
|
|
956
|
+
from: payload.from,
|
|
957
|
+
to: payload.to,
|
|
958
|
+
value: payload.value,
|
|
959
|
+
validAfter: payload.validAfter.toString(),
|
|
960
|
+
validBefore: payload.validBefore.toString(),
|
|
961
|
+
nonce: payload.nonce
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
};
|
|
965
|
+
const jsonString = JSON.stringify(x402Payload);
|
|
966
|
+
return btoa(jsonString);
|
|
967
|
+
}
|
|
968
|
+
// ============================================================================
|
|
969
|
+
// PRIVATE - EVM Balance Check
|
|
970
|
+
// ============================================================================
|
|
971
|
+
async getEVMBalance(chain) {
|
|
972
|
+
const publicProvider = new ethers.JsonRpcProvider(chain.rpcUrl);
|
|
973
|
+
const usdcAbi = ["function balanceOf(address owner) view returns (uint256)"];
|
|
974
|
+
const usdcContract = new ethers.Contract(chain.usdc.address, usdcAbi, publicProvider);
|
|
975
|
+
try {
|
|
976
|
+
const balance = await usdcContract.balanceOf(this.connectedAddress);
|
|
977
|
+
const formatted = ethers.formatUnits(balance, chain.usdc.decimals);
|
|
978
|
+
return parseFloat(formatted).toFixed(2);
|
|
979
|
+
} catch {
|
|
980
|
+
return "0.00";
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
// ============================================================================
|
|
984
|
+
// PRIVATE - Utilities
|
|
985
|
+
// ============================================================================
|
|
986
|
+
getRecipientForNetwork(paymentInfo, network) {
|
|
987
|
+
const lookupNetwork = network === "svm" ? "solana" : network;
|
|
988
|
+
const recipients = paymentInfo.recipients;
|
|
989
|
+
if (recipients?.[lookupNetwork]) {
|
|
990
|
+
return recipients[lookupNetwork];
|
|
991
|
+
}
|
|
992
|
+
return paymentInfo.recipient;
|
|
993
|
+
}
|
|
994
|
+
emit(event, data) {
|
|
995
|
+
this.eventHandlers.get(event)?.forEach((handler) => {
|
|
996
|
+
try {
|
|
997
|
+
handler(data);
|
|
998
|
+
} catch (error) {
|
|
999
|
+
console.error(`Error in ${event} handler:`, error);
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
log(message, data) {
|
|
1004
|
+
if (this.config.debug) {
|
|
1005
|
+
console.log(`[X402Client] ${message}`, data ?? "");
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
};
|
|
1009
|
+
|
|
1010
|
+
// src/utils/x402.ts
|
|
1011
|
+
function detectX402Version(data) {
|
|
1012
|
+
if (typeof data !== "object" || data === null) {
|
|
1013
|
+
return 1;
|
|
1014
|
+
}
|
|
1015
|
+
const obj = data;
|
|
1016
|
+
if (obj.x402Version === 2) {
|
|
1017
|
+
return 2;
|
|
1018
|
+
}
|
|
1019
|
+
if (obj.accepts && Array.isArray(obj.accepts)) {
|
|
1020
|
+
return 2;
|
|
1021
|
+
}
|
|
1022
|
+
if (typeof obj.network === "string") {
|
|
1023
|
+
if (obj.network.includes(":")) {
|
|
1024
|
+
return 2;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
return 1;
|
|
1028
|
+
}
|
|
1029
|
+
function chainToCAIP2(chainName) {
|
|
1030
|
+
const caip2 = CAIP2_IDENTIFIERS[chainName.toLowerCase()];
|
|
1031
|
+
if (caip2) {
|
|
1032
|
+
return caip2;
|
|
1033
|
+
}
|
|
1034
|
+
const chain = getChainByName(chainName);
|
|
1035
|
+
if (chain) {
|
|
1036
|
+
if (chain.networkType === "evm") {
|
|
1037
|
+
return `eip155:${chain.chainId}`;
|
|
1038
|
+
}
|
|
1039
|
+
return `${chain.networkType}:${chainName}`;
|
|
1040
|
+
}
|
|
1041
|
+
return chainName;
|
|
1042
|
+
}
|
|
1043
|
+
function caip2ToChain(caip2) {
|
|
1044
|
+
if (CAIP2_TO_CHAIN[caip2]) {
|
|
1045
|
+
return CAIP2_TO_CHAIN[caip2];
|
|
1046
|
+
}
|
|
1047
|
+
const match = caip2.match(/^eip155:(\d+)$/);
|
|
1048
|
+
if (match) {
|
|
1049
|
+
const chainId = parseInt(match[1], 10);
|
|
1050
|
+
for (const [name, _config] of Object.entries(CAIP2_IDENTIFIERS)) {
|
|
1051
|
+
const chain = getChainByName(name);
|
|
1052
|
+
if (chain?.chainId === chainId) {
|
|
1053
|
+
return name;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
const parts = caip2.split(":");
|
|
1058
|
+
if (parts.length === 2) {
|
|
1059
|
+
const networkName = parts[1];
|
|
1060
|
+
if (getChainByName(networkName)) {
|
|
1061
|
+
return networkName;
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
return null;
|
|
1065
|
+
}
|
|
1066
|
+
function parseNetworkIdentifier(network) {
|
|
1067
|
+
if (network.includes(":")) {
|
|
1068
|
+
return caip2ToChain(network) || network;
|
|
1069
|
+
}
|
|
1070
|
+
return network.toLowerCase();
|
|
1071
|
+
}
|
|
1072
|
+
function encodeX402Header(header) {
|
|
1073
|
+
return btoa(JSON.stringify(header));
|
|
1074
|
+
}
|
|
1075
|
+
function decodeX402Header(encoded) {
|
|
1076
|
+
const json = atob(encoded);
|
|
1077
|
+
return JSON.parse(json);
|
|
1078
|
+
}
|
|
1079
|
+
function createX402V1Header(network, payload) {
|
|
1080
|
+
return {
|
|
1081
|
+
x402Version: 1,
|
|
1082
|
+
scheme: "exact",
|
|
1083
|
+
network,
|
|
1084
|
+
payload
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
function createX402V2Header(network, payload, accepts) {
|
|
1088
|
+
const header = {
|
|
1089
|
+
x402Version: 2,
|
|
1090
|
+
scheme: "exact",
|
|
1091
|
+
network: network.includes(":") ? network : chainToCAIP2(network),
|
|
1092
|
+
payload
|
|
1093
|
+
};
|
|
1094
|
+
if (accepts && accepts.length > 0) {
|
|
1095
|
+
header.accepts = accepts;
|
|
1096
|
+
}
|
|
1097
|
+
return header;
|
|
1098
|
+
}
|
|
1099
|
+
function createX402Header(chainConfig, payload, version = "auto") {
|
|
1100
|
+
const effectiveVersion = version === "auto" ? 1 : version;
|
|
1101
|
+
if (effectiveVersion === 2) {
|
|
1102
|
+
return createX402V2Header(chainConfig.name, payload);
|
|
1103
|
+
}
|
|
1104
|
+
return createX402V1Header(chainConfig.name, payload);
|
|
1105
|
+
}
|
|
1106
|
+
function generatePaymentOptions(chainConfigs, amount, facilitator) {
|
|
1107
|
+
return chainConfigs.filter((chain) => chain.x402.enabled).map((chain) => {
|
|
1108
|
+
const atomicAmount = Math.floor(
|
|
1109
|
+
parseFloat(amount) * Math.pow(10, chain.usdc.decimals)
|
|
1110
|
+
).toString();
|
|
1111
|
+
return {
|
|
1112
|
+
network: chainToCAIP2(chain.name),
|
|
1113
|
+
asset: chain.usdc.address,
|
|
1114
|
+
amount: atomicAmount,
|
|
1115
|
+
facilitator: facilitator || chain.x402.facilitatorUrl
|
|
1116
|
+
};
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
function isCAIP2Format(network) {
|
|
1120
|
+
return network.includes(":");
|
|
1121
|
+
}
|
|
1122
|
+
function convertX402Header(header, targetVersion) {
|
|
1123
|
+
if (header.x402Version === targetVersion) {
|
|
1124
|
+
return header;
|
|
1125
|
+
}
|
|
1126
|
+
if (targetVersion === 2) {
|
|
1127
|
+
return {
|
|
1128
|
+
x402Version: 2,
|
|
1129
|
+
scheme: "exact",
|
|
1130
|
+
network: chainToCAIP2(header.network),
|
|
1131
|
+
payload: header.payload
|
|
1132
|
+
};
|
|
1133
|
+
} else {
|
|
1134
|
+
const chainName = isCAIP2Format(header.network) ? caip2ToChain(header.network) || header.network : header.network;
|
|
1135
|
+
return {
|
|
1136
|
+
x402Version: 1,
|
|
1137
|
+
scheme: "exact",
|
|
1138
|
+
network: chainName,
|
|
1139
|
+
payload: header.payload
|
|
1140
|
+
};
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
export { CAIP2_IDENTIFIERS, CAIP2_TO_CHAIN, DEFAULT_CHAIN, DEFAULT_CONFIG, DEFAULT_FACILITATOR_URL, SUPPORTED_CHAINS, X402Client, X402Error, caip2ToChain, chainToCAIP2, convertX402Header, createX402Header, createX402V1Header, createX402V2Header, decodeX402Header, detectX402Version, encodeX402Header, generatePaymentOptions, getChainById, getChainByName, getChainsByNetworkType, getEVMChainIds, getEnabledChains, getExplorerAddressUrl, getExplorerTxUrl, getNetworkType, getSVMChains, isCAIP2Format, isChainSupported, isSVMChain, parseNetworkIdentifier };
|
|
1145
|
+
//# sourceMappingURL=index.mjs.map
|
|
1146
|
+
//# sourceMappingURL=index.mjs.map
|