@hongming-wang/usdc-bridge-widget 0.1.0

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/dist/index.js ADDED
@@ -0,0 +1,2356 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BridgeWidget: () => BridgeWidget,
24
+ CHAIN_ICONS: () => CHAIN_ICONS,
25
+ CheckIcon: () => CheckIcon,
26
+ ChevronDownIcon: () => ChevronDownIcon,
27
+ DEFAULT_CHAIN_CONFIGS: () => DEFAULT_CHAIN_CONFIGS,
28
+ DEFAULT_LOCALE: () => DEFAULT_LOCALE,
29
+ ErrorIcon: () => ErrorIcon,
30
+ ExternalLinkIcon: () => ExternalLinkIcon,
31
+ MAX_USDC_AMOUNT: () => MAX_USDC_AMOUNT,
32
+ MIN_USDC_AMOUNT: () => MIN_USDC_AMOUNT,
33
+ SpinnerIcon: () => SpinnerIcon,
34
+ SwapIcon: () => SwapIcon,
35
+ TESTNET_CHAIN_CONFIGS: () => TESTNET_CHAIN_CONFIGS,
36
+ THEME_COLORS: () => THEME_COLORS,
37
+ THEME_FONTS: () => THEME_FONTS,
38
+ THEME_SIZING: () => THEME_SIZING,
39
+ TOKEN_MESSENGER_ADDRESSES: () => TOKEN_MESSENGER_ADDRESSES,
40
+ TOKEN_MESSENGER_V1_ADDRESSES: () => TOKEN_MESSENGER_V1_ADDRESSES,
41
+ TOKEN_MESSENGER_V2_ADDRESS: () => TOKEN_MESSENGER_V2_ADDRESS,
42
+ USDC_ADDRESSES: () => USDC_ADDRESSES,
43
+ USDC_BRAND_COLOR: () => USDC_BRAND_COLOR,
44
+ USDC_DECIMALS: () => USDC_DECIMALS,
45
+ WalletIcon: () => WalletIcon,
46
+ arbitrum: () => import_chains.arbitrum,
47
+ arbitrumSepolia: () => import_chains.arbitrumSepolia,
48
+ avalanche: () => import_chains.avalanche,
49
+ avalancheFuji: () => import_chains.avalancheFuji,
50
+ base: () => import_chains.base,
51
+ baseSepolia: () => import_chains.baseSepolia,
52
+ codex: () => codex,
53
+ createChainConfig: () => createChainConfig,
54
+ createTestnetChainConfig: () => createTestnetChainConfig,
55
+ defaultTheme: () => defaultTheme,
56
+ formatNumber: () => formatNumber,
57
+ getChainName: () => getChainName,
58
+ getErrorMessage: () => getErrorMessage,
59
+ hyperEvm: () => hyperEvm,
60
+ ink: () => import_chains.ink,
61
+ isValidPositiveAmount: () => isValidPositiveAmount,
62
+ linea: () => import_chains.linea,
63
+ mainnet: () => import_chains.mainnet,
64
+ mergeTheme: () => mergeTheme,
65
+ monad: () => monad,
66
+ optimism: () => import_chains.optimism,
67
+ optimismSepolia: () => import_chains.optimismSepolia,
68
+ parseUSDCAmount: () => parseUSDCAmount,
69
+ plume: () => plume,
70
+ polygon: () => import_chains.polygon,
71
+ polygonAmoy: () => import_chains.polygonAmoy,
72
+ sei: () => import_chains.sei,
73
+ sepolia: () => import_chains.sepolia,
74
+ sonic: () => import_chains.sonic,
75
+ themePresets: () => themePresets,
76
+ unichain: () => unichain,
77
+ useAllUSDCBalances: () => useAllUSDCBalances,
78
+ useBridge: () => useBridge,
79
+ useBridgeEstimate: () => useBridgeEstimate,
80
+ useBridgeQuote: () => useBridgeQuote,
81
+ useFormatNumber: () => useFormatNumber,
82
+ useUSDCAllowance: () => useUSDCAllowance,
83
+ useUSDCBalance: () => useUSDCBalance,
84
+ validateAmountInput: () => validateAmountInput,
85
+ validateChainConfig: () => validateChainConfig,
86
+ validateChainConfigs: () => validateChainConfigs,
87
+ worldchain: () => import_chains.worldchain,
88
+ xdc: () => import_chains.xdc
89
+ });
90
+ module.exports = __toCommonJS(index_exports);
91
+
92
+ // src/BridgeWidget.tsx
93
+ var import_react3 = require("react");
94
+ var import_wagmi3 = require("wagmi");
95
+
96
+ // src/hooks.ts
97
+ var import_react2 = require("react");
98
+ var import_wagmi2 = require("wagmi");
99
+ var import_viem2 = require("viem");
100
+
101
+ // src/constants.ts
102
+ var USDC_DECIMALS = 6;
103
+ var USDC_BRAND_COLOR = "#2775ca";
104
+ var MAX_USDC_AMOUNT = "100000000000";
105
+ var MIN_USDC_AMOUNT = "0.000001";
106
+ var DEFAULT_LOCALE = "en-US";
107
+ var USDC_ADDRESSES = {
108
+ // Original chains
109
+ 1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
110
+ // Ethereum
111
+ 42161: "0xaf88d065e77c8cC2239327C5EDb3A432268e5831",
112
+ // Arbitrum
113
+ 43114: "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E",
114
+ // Avalanche
115
+ 8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
116
+ // Base
117
+ 10: "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85",
118
+ // Optimism
119
+ 137: "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359",
120
+ // Polygon
121
+ 59144: "0x176211869cA2b568f2A7D4EE941E073a821EE1ff",
122
+ // Linea
123
+ // New CCTP V2 chains
124
+ 130: "0x078D782b760474a361dDA0AF3839290b0EF57AD6",
125
+ // Unichain
126
+ 146: "0x29219dd400f2Bf60E5a23d13Be72B486D4038894",
127
+ // Sonic
128
+ 480: "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1",
129
+ // World Chain
130
+ 10200: "0x754704Bc059F8C67012fEd69BC8A327a5aafb603",
131
+ // Monad
132
+ 1329: "0xe15fC38F6D8c56aF07bbCBe3BAf5708A2Bf42392",
133
+ // Sei
134
+ 50: "0xfA2958CB79b0491CC627c1557F441eF849Ca8eb1",
135
+ // XDC
136
+ 999: "0xb88339CB7199b77E23DB6E890353E22632Ba630f",
137
+ // HyperEVM
138
+ 57073: "0x2D270e6886d130D724215A266106e6832161EAEd",
139
+ // Ink
140
+ 98866: "0x222365EF19F7947e5484218551B56bb3965Aa7aF",
141
+ // Plume
142
+ 81224: "0xd996633a415985DBd7D6D12f4A4343E31f5037cf"
143
+ // Codex
144
+ };
145
+ var TOKEN_MESSENGER_V1_ADDRESSES = {
146
+ 1: "0xBd3fa81B58Ba92a82136038B25aDec7066af3155",
147
+ // Ethereum
148
+ 42161: "0x19330d10D9Cc8751218eaf51E8885D058642E08A",
149
+ // Arbitrum
150
+ 43114: "0x6B25532e1060CE10cc3B0A99e5683b91BFDe6982",
151
+ // Avalanche
152
+ 8453: "0x1682Ae6375C4E4A97e4B583BC394c861A46D8962",
153
+ // Base
154
+ 10: "0x2B4069517957735bE00ceE0fadAE88a26365528f",
155
+ // Optimism
156
+ 137: "0x9daF8c91AEFAE50b9c0E69629D3F6Ca40cA3B3FE"
157
+ // Polygon
158
+ };
159
+ var TOKEN_MESSENGER_V2_ADDRESS = "0x28b5a0e9C621a5BadaA536219b3a228C8168cf5d";
160
+ var TOKEN_MESSENGER_ADDRESSES = {
161
+ // All CCTP V2 supported chains use the same address
162
+ 1: TOKEN_MESSENGER_V2_ADDRESS,
163
+ // Ethereum
164
+ 42161: TOKEN_MESSENGER_V2_ADDRESS,
165
+ // Arbitrum
166
+ 43114: TOKEN_MESSENGER_V2_ADDRESS,
167
+ // Avalanche
168
+ 8453: TOKEN_MESSENGER_V2_ADDRESS,
169
+ // Base
170
+ 10: TOKEN_MESSENGER_V2_ADDRESS,
171
+ // Optimism
172
+ 137: TOKEN_MESSENGER_V2_ADDRESS,
173
+ // Polygon
174
+ 59144: TOKEN_MESSENGER_V2_ADDRESS,
175
+ // Linea
176
+ 130: TOKEN_MESSENGER_V2_ADDRESS,
177
+ // Unichain
178
+ 146: TOKEN_MESSENGER_V2_ADDRESS,
179
+ // Sonic
180
+ 480: TOKEN_MESSENGER_V2_ADDRESS,
181
+ // World Chain
182
+ 10200: TOKEN_MESSENGER_V2_ADDRESS,
183
+ // Monad
184
+ 1329: TOKEN_MESSENGER_V2_ADDRESS,
185
+ // Sei
186
+ 50: TOKEN_MESSENGER_V2_ADDRESS,
187
+ // XDC
188
+ 999: TOKEN_MESSENGER_V2_ADDRESS,
189
+ // HyperEVM
190
+ 57073: TOKEN_MESSENGER_V2_ADDRESS,
191
+ // Ink
192
+ 98866: TOKEN_MESSENGER_V2_ADDRESS,
193
+ // Plume
194
+ 81224: TOKEN_MESSENGER_V2_ADDRESS
195
+ // Codex
196
+ };
197
+ var CHAIN_ICONS = {
198
+ 1: "https://icons.llamao.fi/icons/chains/rsz_ethereum.jpg",
199
+ // Ethereum
200
+ 42161: "https://icons.llamao.fi/icons/chains/rsz_arbitrum.jpg",
201
+ // Arbitrum
202
+ 43114: "https://icons.llamao.fi/icons/chains/rsz_avalanche.jpg",
203
+ // Avalanche
204
+ 8453: "https://icons.llamao.fi/icons/chains/rsz_base.jpg",
205
+ // Base
206
+ 10: "https://icons.llamao.fi/icons/chains/rsz_optimism.jpg",
207
+ // Optimism
208
+ 137: "https://icons.llamao.fi/icons/chains/rsz_polygon.jpg",
209
+ // Polygon
210
+ 59144: "https://icons.llamao.fi/icons/chains/rsz_linea.jpg",
211
+ // Linea
212
+ 130: "https://icons.llamao.fi/icons/chains/rsz_unichain.jpg",
213
+ // Unichain
214
+ 146: "https://icons.llamao.fi/icons/chains/rsz_sonic.jpg",
215
+ // Sonic
216
+ 480: "https://icons.llamao.fi/icons/chains/rsz_world-chain.jpg",
217
+ // World Chain
218
+ 10200: "https://icons.llamao.fi/icons/chains/rsz_monad.jpg",
219
+ // Monad
220
+ 1329: "https://icons.llamao.fi/icons/chains/rsz_sei.jpg",
221
+ // Sei
222
+ 50: "https://icons.llamao.fi/icons/chains/rsz_xdc.jpg",
223
+ // XDC
224
+ 999: "https://icons.llamao.fi/icons/chains/rsz_hyperevm.jpg",
225
+ // HyperEVM
226
+ 57073: "https://icons.llamao.fi/icons/chains/rsz_ink.jpg",
227
+ // Ink
228
+ 98866: "https://icons.llamao.fi/icons/chains/rsz_plume.jpg",
229
+ // Plume
230
+ 81224: "https://raw.githubusercontent.com/0xa3k5/web3icons/main/packages/core/src/svgs/networks/branded/codex.svg"
231
+ // Codex
232
+ };
233
+
234
+ // src/utils.ts
235
+ var import_viem = require("viem");
236
+ function formatNumber(value, decimals = 2, locale = DEFAULT_LOCALE) {
237
+ const num = typeof value === "string" ? parseFloat(value) : value;
238
+ if (isNaN(num)) return "0";
239
+ return num.toLocaleString(locale, {
240
+ minimumFractionDigits: decimals,
241
+ maximumFractionDigits: decimals
242
+ });
243
+ }
244
+ function validateAmountInput(value) {
245
+ if (value === "") {
246
+ return { isValid: true, sanitized: "" };
247
+ }
248
+ if (/[eE]/.test(value)) {
249
+ return { isValid: false, sanitized: "", error: "Scientific notation not allowed" };
250
+ }
251
+ if (value.includes("-")) {
252
+ return { isValid: false, sanitized: "", error: "Negative values not allowed" };
253
+ }
254
+ if (value === "." || value === "0.") {
255
+ return { isValid: true, sanitized: value };
256
+ }
257
+ if (!/^[0-9]*\.?[0-9]*$/.test(value)) {
258
+ return { isValid: false, sanitized: "", error: "Invalid characters" };
259
+ }
260
+ const parts = value.split(".");
261
+ if (parts.length === 2 && parts[1].length > USDC_DECIMALS) {
262
+ return {
263
+ isValid: false,
264
+ sanitized: `${parts[0]}.${parts[1].slice(0, USDC_DECIMALS)}`,
265
+ error: `Maximum ${USDC_DECIMALS} decimal places`
266
+ };
267
+ }
268
+ const num = parseFloat(value);
269
+ if (!isNaN(num) && num > parseFloat(MAX_USDC_AMOUNT)) {
270
+ return { isValid: false, sanitized: value, error: "Amount exceeds maximum" };
271
+ }
272
+ let sanitized = value;
273
+ if (sanitized.length > 1 && sanitized.startsWith("0") && sanitized[1] !== ".") {
274
+ sanitized = sanitized.replace(/^0+/, "") || "0";
275
+ }
276
+ return { isValid: true, sanitized };
277
+ }
278
+ function parseUSDCAmount(amount) {
279
+ try {
280
+ if (!amount || parseFloat(amount) < 0) return null;
281
+ return (0, import_viem.parseUnits)(amount, USDC_DECIMALS);
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
286
+ function isValidPositiveAmount(amount) {
287
+ if (!amount) return false;
288
+ const num = parseFloat(amount);
289
+ return !isNaN(num) && num > 0;
290
+ }
291
+ function getErrorMessage(error) {
292
+ if (error instanceof Error) {
293
+ return error.message;
294
+ }
295
+ if (typeof error === "string") {
296
+ return error;
297
+ }
298
+ if (error !== null && typeof error === "object" && "message" in error && typeof error.message === "string") {
299
+ return error.message;
300
+ }
301
+ return "An unknown error occurred";
302
+ }
303
+ function validateChainConfig(config) {
304
+ const errors = [];
305
+ if (!config.chain) {
306
+ errors.push("Chain object is required");
307
+ return { isValid: false, errors };
308
+ }
309
+ if (typeof config.chain.id !== "number" || config.chain.id <= 0) {
310
+ errors.push(`Invalid chain ID: ${config.chain.id}`);
311
+ }
312
+ if (!config.chain.name || typeof config.chain.name !== "string") {
313
+ errors.push("Chain name is required");
314
+ }
315
+ if (!config.usdcAddress) {
316
+ errors.push(`USDC address is required for chain ${config.chain.name || config.chain.id}`);
317
+ } else if (!(0, import_viem.isAddress)(config.usdcAddress)) {
318
+ errors.push(`Invalid USDC address for chain ${config.chain.name}: ${config.usdcAddress}`);
319
+ }
320
+ if (config.tokenMessengerAddress && !(0, import_viem.isAddress)(config.tokenMessengerAddress)) {
321
+ errors.push(
322
+ `Invalid TokenMessenger address for chain ${config.chain.name}: ${config.tokenMessengerAddress}`
323
+ );
324
+ }
325
+ return {
326
+ isValid: errors.length === 0,
327
+ errors
328
+ };
329
+ }
330
+ function validateChainConfigs(configs) {
331
+ const allErrors = [];
332
+ if (!Array.isArray(configs)) {
333
+ return { isValid: false, errors: ["Chain configs must be an array"] };
334
+ }
335
+ if (configs.length === 0) {
336
+ return { isValid: false, errors: ["At least one chain configuration is required"] };
337
+ }
338
+ if (configs.length < 2) {
339
+ return { isValid: false, errors: ["At least two chains are required for bridging"] };
340
+ }
341
+ const chainIds = /* @__PURE__ */ new Set();
342
+ for (const config of configs) {
343
+ if (config.chain?.id) {
344
+ if (chainIds.has(config.chain.id)) {
345
+ allErrors.push(`Duplicate chain ID: ${config.chain.id}`);
346
+ }
347
+ chainIds.add(config.chain.id);
348
+ }
349
+ const result = validateChainConfig(config);
350
+ if (!result.isValid) {
351
+ allErrors.push(...result.errors);
352
+ }
353
+ }
354
+ return {
355
+ isValid: allErrors.length === 0,
356
+ errors: allErrors
357
+ };
358
+ }
359
+
360
+ // src/useBridge.ts
361
+ var import_react = require("react");
362
+ var import_wagmi = require("wagmi");
363
+ var import_bridge_kit = require("@circle-fin/bridge-kit");
364
+ var import_adapter_viem_v2 = require("@circle-fin/adapter-viem-v2");
365
+ var MAX_EVENTS = 100;
366
+ var CHAIN_ID_TO_BRIDGE_CHAIN = {
367
+ 1: import_bridge_kit.BridgeChain.Ethereum,
368
+ 42161: import_bridge_kit.BridgeChain.Arbitrum,
369
+ 43114: import_bridge_kit.BridgeChain.Avalanche,
370
+ 8453: import_bridge_kit.BridgeChain.Base,
371
+ 10: import_bridge_kit.BridgeChain.Optimism,
372
+ 137: import_bridge_kit.BridgeChain.Polygon,
373
+ 59144: import_bridge_kit.BridgeChain.Linea,
374
+ 130: import_bridge_kit.BridgeChain.Unichain,
375
+ 146: import_bridge_kit.BridgeChain.Sonic,
376
+ 480: import_bridge_kit.BridgeChain.World_Chain,
377
+ // Note: Monad (10200) not yet supported in Circle Bridge Kit
378
+ 1329: import_bridge_kit.BridgeChain.Sei,
379
+ 50: import_bridge_kit.BridgeChain.XDC,
380
+ 999: import_bridge_kit.BridgeChain.HyperEVM,
381
+ 57073: import_bridge_kit.BridgeChain.Ink,
382
+ 98866: import_bridge_kit.BridgeChain.Plume,
383
+ 81224: import_bridge_kit.BridgeChain.Codex
384
+ };
385
+ function isBridgeEventWithTxHash(event) {
386
+ return typeof event === "object" && event !== null && ("values" in event ? typeof event.values === "object" : true);
387
+ }
388
+ function extractTxHash(event) {
389
+ if (isBridgeEventWithTxHash(event) && event.values?.txHash) {
390
+ const hash = event.values.txHash;
391
+ if (typeof hash === "string" && hash.startsWith("0x")) {
392
+ return hash;
393
+ }
394
+ }
395
+ return void 0;
396
+ }
397
+ function isEIP1193Provider(provider) {
398
+ return typeof provider === "object" && provider !== null && "request" in provider && typeof provider.request === "function";
399
+ }
400
+ function getBridgeChain(chainId) {
401
+ return CHAIN_ID_TO_BRIDGE_CHAIN[chainId];
402
+ }
403
+ function getChainName(chainId) {
404
+ const bridgeChain = CHAIN_ID_TO_BRIDGE_CHAIN[chainId];
405
+ return bridgeChain || `Chain_${chainId}`;
406
+ }
407
+ function useBridge() {
408
+ const { connector, isConnected } = (0, import_wagmi.useAccount)();
409
+ const [state, setState] = (0, import_react.useState)({
410
+ status: "idle",
411
+ events: []
412
+ });
413
+ const isMountedRef = (0, import_react.useRef)(true);
414
+ const currentBridgeRef = (0, import_react.useRef)(null);
415
+ (0, import_react.useEffect)(() => {
416
+ isMountedRef.current = true;
417
+ return () => {
418
+ isMountedRef.current = false;
419
+ if (currentBridgeRef.current) {
420
+ currentBridgeRef.current.aborted = true;
421
+ }
422
+ };
423
+ }, []);
424
+ const addEvent = (0, import_react.useCallback)((type, data) => {
425
+ if (!isMountedRef.current) return;
426
+ setState((prev) => {
427
+ const newEvents = [...prev.events, { type, timestamp: Date.now(), data }];
428
+ if (newEvents.length > MAX_EVENTS) {
429
+ newEvents.splice(0, newEvents.length - MAX_EVENTS);
430
+ }
431
+ return { ...prev, events: newEvents };
432
+ });
433
+ }, []);
434
+ const reset = (0, import_react.useCallback)(() => {
435
+ if (currentBridgeRef.current) {
436
+ currentBridgeRef.current.aborted = true;
437
+ }
438
+ currentBridgeRef.current = null;
439
+ setState({ status: "idle", events: [] });
440
+ }, []);
441
+ const bridge = (0, import_react.useCallback)(
442
+ async (params) => {
443
+ const { sourceChainConfig, destChainConfig, amount, recipientAddress } = params;
444
+ if (currentBridgeRef.current) {
445
+ currentBridgeRef.current.aborted = true;
446
+ }
447
+ const operation = { aborted: false, kit: null };
448
+ currentBridgeRef.current = operation;
449
+ if (!isConnected || !connector) {
450
+ setState({
451
+ status: "error",
452
+ error: new Error("Wallet not connected"),
453
+ events: []
454
+ });
455
+ return;
456
+ }
457
+ setState({ status: "loading", events: [] });
458
+ addEvent("start", { amount, sourceChain: sourceChainConfig.chain.id, destChain: destChainConfig.chain.id });
459
+ let cleanupListeners = null;
460
+ try {
461
+ const provider = await connector.getProvider();
462
+ if (!provider) {
463
+ throw new Error("Could not get wallet provider from connector");
464
+ }
465
+ if (!isEIP1193Provider(provider)) {
466
+ throw new Error("Wallet provider is not EIP-1193 compatible");
467
+ }
468
+ const adapter = await (0, import_adapter_viem_v2.createViemAdapterFromProvider)({
469
+ provider
470
+ });
471
+ if (operation.aborted) {
472
+ return;
473
+ }
474
+ const kit = new import_bridge_kit.BridgeKit();
475
+ operation.kit = kit;
476
+ const handleApprove = (event) => {
477
+ if (operation.aborted || !isMountedRef.current) return;
478
+ addEvent("approve", event);
479
+ setState((prev) => ({
480
+ ...prev,
481
+ status: "approving",
482
+ txHash: extractTxHash(event)
483
+ }));
484
+ };
485
+ const handleBurn = (event) => {
486
+ if (operation.aborted || !isMountedRef.current) return;
487
+ addEvent("burn", event);
488
+ setState((prev) => ({
489
+ ...prev,
490
+ status: "burning",
491
+ txHash: extractTxHash(event)
492
+ }));
493
+ };
494
+ const handleFetchAttestation = (event) => {
495
+ if (operation.aborted || !isMountedRef.current) return;
496
+ addEvent("fetchAttestation", event);
497
+ setState((prev) => ({
498
+ ...prev,
499
+ status: "fetching-attestation"
500
+ }));
501
+ };
502
+ const handleMint = (event) => {
503
+ if (operation.aborted || !isMountedRef.current) return;
504
+ addEvent("mint", event);
505
+ setState((prev) => ({
506
+ ...prev,
507
+ status: "minting",
508
+ txHash: extractTxHash(event)
509
+ }));
510
+ };
511
+ cleanupListeners = () => {
512
+ try {
513
+ kit.off("approve", handleApprove);
514
+ kit.off("burn", handleBurn);
515
+ kit.off("fetchAttestation", handleFetchAttestation);
516
+ kit.off("mint", handleMint);
517
+ } catch {
518
+ }
519
+ };
520
+ kit.on("approve", handleApprove);
521
+ kit.on("burn", handleBurn);
522
+ kit.on("fetchAttestation", handleFetchAttestation);
523
+ kit.on("mint", handleMint);
524
+ const sourceBridgeChain = getBridgeChain(sourceChainConfig.chain.id);
525
+ const destBridgeChain = getBridgeChain(destChainConfig.chain.id);
526
+ if (!sourceBridgeChain) {
527
+ throw new Error(`Unsupported source chain: ${sourceChainConfig.chain.name} (${sourceChainConfig.chain.id})`);
528
+ }
529
+ if (!destBridgeChain) {
530
+ throw new Error(`Unsupported destination chain: ${destChainConfig.chain.name} (${destChainConfig.chain.id})`);
531
+ }
532
+ const result = await kit.bridge({
533
+ from: { adapter, chain: sourceBridgeChain },
534
+ to: recipientAddress ? { adapter, chain: destBridgeChain, recipientAddress } : { adapter, chain: destBridgeChain },
535
+ amount
536
+ });
537
+ cleanupListeners();
538
+ if (operation.aborted || !isMountedRef.current) {
539
+ return;
540
+ }
541
+ addEvent("complete", result);
542
+ setState((prev) => ({
543
+ ...prev,
544
+ status: "success",
545
+ txHash: extractTxHash(result)
546
+ }));
547
+ } catch (error) {
548
+ if (cleanupListeners) {
549
+ cleanupListeners();
550
+ }
551
+ if (operation.aborted || !isMountedRef.current) {
552
+ return;
553
+ }
554
+ addEvent("error", error);
555
+ setState((prev) => ({
556
+ ...prev,
557
+ status: "error",
558
+ error: error instanceof Error ? error : new Error("Bridge transfer failed")
559
+ }));
560
+ throw error;
561
+ }
562
+ },
563
+ [isConnected, connector, addEvent]
564
+ );
565
+ return { bridge, state, reset };
566
+ }
567
+ function useBridgeQuote(sourceChainId, destChainId, amount) {
568
+ const [quote, setQuote] = (0, import_react.useState)(null);
569
+ const [isLoading, setIsLoading] = (0, import_react.useState)(false);
570
+ const [error, setError] = (0, import_react.useState)(null);
571
+ (0, import_react.useEffect)(() => {
572
+ if (!sourceChainId || !destChainId || !amount || parseFloat(amount) <= 0) {
573
+ setQuote(null);
574
+ setError(null);
575
+ return;
576
+ }
577
+ const fetchQuote = async () => {
578
+ setIsLoading(true);
579
+ setError(null);
580
+ try {
581
+ const sourceBridgeChain = getBridgeChain(sourceChainId);
582
+ const destBridgeChain = getBridgeChain(destChainId);
583
+ if (!sourceBridgeChain || !destBridgeChain) {
584
+ setQuote({
585
+ estimatedGasFee: "Estimated by wallet",
586
+ bridgeFee: "0.00",
587
+ totalFee: "Gas only",
588
+ estimatedTime: "~15-20 minutes"
589
+ });
590
+ return;
591
+ }
592
+ setQuote({
593
+ estimatedGasFee: "Estimated by wallet",
594
+ bridgeFee: "0-14 bps (FAST) / 0 (SLOW)",
595
+ totalFee: "Gas + protocol fee",
596
+ estimatedTime: "~15-20 minutes"
597
+ });
598
+ } catch (err) {
599
+ setError(err instanceof Error ? err : new Error("Failed to get quote"));
600
+ setQuote(null);
601
+ } finally {
602
+ setIsLoading(false);
603
+ }
604
+ };
605
+ let isActive = true;
606
+ const debounceTimer = setTimeout(() => {
607
+ if (isActive) {
608
+ fetchQuote();
609
+ }
610
+ }, 500);
611
+ return () => {
612
+ isActive = false;
613
+ clearTimeout(debounceTimer);
614
+ };
615
+ }, [sourceChainId, destChainId, amount]);
616
+ return { quote, isLoading, error };
617
+ }
618
+
619
+ // src/hooks.ts
620
+ function useUSDCBalance(chainConfig) {
621
+ const { address } = (0, import_wagmi2.useAccount)();
622
+ const {
623
+ data: balance,
624
+ isLoading,
625
+ refetch
626
+ } = (0, import_wagmi2.useReadContract)({
627
+ address: chainConfig?.usdcAddress,
628
+ abi: import_viem2.erc20Abi,
629
+ functionName: "balanceOf",
630
+ args: address ? [address] : void 0,
631
+ query: {
632
+ enabled: !!address && !!chainConfig?.usdcAddress
633
+ }
634
+ });
635
+ return {
636
+ balance: balance ?? 0n,
637
+ balanceFormatted: balance ? (0, import_viem2.formatUnits)(balance, USDC_DECIMALS) : "0",
638
+ isLoading,
639
+ refetch
640
+ };
641
+ }
642
+ function useAllUSDCBalances(chainConfigs) {
643
+ const { address } = (0, import_wagmi2.useAccount)();
644
+ const contracts = (0, import_react2.useMemo)(() => {
645
+ if (!address) return [];
646
+ return chainConfigs.filter((config) => config.usdcAddress).map((config) => ({
647
+ address: config.usdcAddress,
648
+ abi: import_viem2.erc20Abi,
649
+ functionName: "balanceOf",
650
+ args: [address],
651
+ chainId: config.chain.id
652
+ }));
653
+ }, [address, chainConfigs]);
654
+ const {
655
+ data: results,
656
+ isLoading,
657
+ refetch
658
+ } = (0, import_wagmi2.useReadContracts)({
659
+ contracts,
660
+ query: {
661
+ enabled: !!address && contracts.length > 0
662
+ }
663
+ });
664
+ const balances = (0, import_react2.useMemo)(() => {
665
+ const balanceMap = {};
666
+ if (!results) return balanceMap;
667
+ chainConfigs.forEach((config, index) => {
668
+ const result = results[index];
669
+ if (result?.status === "success" && typeof result.result === "bigint") {
670
+ balanceMap[config.chain.id] = {
671
+ balance: result.result,
672
+ formatted: (0, import_viem2.formatUnits)(result.result, USDC_DECIMALS)
673
+ };
674
+ } else {
675
+ balanceMap[config.chain.id] = {
676
+ balance: 0n,
677
+ formatted: "0"
678
+ };
679
+ }
680
+ });
681
+ return balanceMap;
682
+ }, [results, chainConfigs]);
683
+ return {
684
+ balances,
685
+ isLoading,
686
+ refetch
687
+ };
688
+ }
689
+ function useUSDCAllowance(chainConfig, spenderAddress) {
690
+ const { address } = (0, import_wagmi2.useAccount)();
691
+ const effectiveSpender = spenderAddress || chainConfig?.tokenMessengerAddress;
692
+ const {
693
+ data: allowance,
694
+ isLoading,
695
+ refetch
696
+ } = (0, import_wagmi2.useReadContract)({
697
+ address: chainConfig?.usdcAddress,
698
+ abi: import_viem2.erc20Abi,
699
+ functionName: "allowance",
700
+ args: address && effectiveSpender ? [address, effectiveSpender] : void 0,
701
+ query: {
702
+ enabled: !!address && !!chainConfig?.usdcAddress && !!effectiveSpender
703
+ }
704
+ });
705
+ const { writeContractAsync, isPending: isApproving } = (0, import_wagmi2.useWriteContract)();
706
+ const [approvalTxHash, setApprovalTxHash] = (0, import_react2.useState)();
707
+ const [approvalError, setApprovalError] = (0, import_react2.useState)(null);
708
+ const { isLoading: isConfirming, isSuccess: isApprovalConfirmed } = (0, import_wagmi2.useWaitForTransactionReceipt)({
709
+ hash: approvalTxHash
710
+ });
711
+ const approve = (0, import_react2.useCallback)(
712
+ async (amount) => {
713
+ if (!chainConfig?.usdcAddress || !effectiveSpender) {
714
+ throw new Error("Missing chain config or spender address");
715
+ }
716
+ setApprovalError(null);
717
+ try {
718
+ const amountBigInt = (0, import_viem2.parseUnits)(amount, USDC_DECIMALS);
719
+ const hash = await writeContractAsync({
720
+ address: chainConfig.usdcAddress,
721
+ abi: import_viem2.erc20Abi,
722
+ functionName: "approve",
723
+ args: [effectiveSpender, amountBigInt]
724
+ });
725
+ setApprovalTxHash(hash);
726
+ return hash;
727
+ } catch (error) {
728
+ const err = error instanceof Error ? error : new Error("Approval failed");
729
+ setApprovalError(err);
730
+ throw err;
731
+ }
732
+ },
733
+ [chainConfig?.usdcAddress, effectiveSpender, writeContractAsync]
734
+ );
735
+ (0, import_react2.useEffect)(() => {
736
+ if (isApprovalConfirmed) {
737
+ refetch();
738
+ }
739
+ }, [isApprovalConfirmed, refetch]);
740
+ const needsApproval = (0, import_react2.useCallback)(
741
+ (amount) => {
742
+ if (!amount || !allowance) return false;
743
+ const parsedAmount = parseFloat(amount);
744
+ if (isNaN(parsedAmount) || parsedAmount <= 0) return false;
745
+ try {
746
+ const amountBigInt = (0, import_viem2.parseUnits)(amount, USDC_DECIMALS);
747
+ return allowance < amountBigInt;
748
+ } catch {
749
+ return false;
750
+ }
751
+ },
752
+ [allowance]
753
+ );
754
+ return {
755
+ allowance: allowance ?? 0n,
756
+ allowanceFormatted: allowance ? (0, import_viem2.formatUnits)(allowance, USDC_DECIMALS) : "0",
757
+ isLoading,
758
+ isApproving: isApproving || isConfirming,
759
+ approve,
760
+ needsApproval,
761
+ refetch,
762
+ approvalError
763
+ };
764
+ }
765
+ function useBridgeEstimate(sourceChainId, destChainId, amount) {
766
+ (0, import_react2.useEffect)(() => {
767
+ console.warn(
768
+ "[DEPRECATED] useBridgeEstimate is deprecated and will be removed in a future version. Use useBridgeQuote from './useBridge' instead."
769
+ );
770
+ }, []);
771
+ const [estimate, setEstimate] = (0, import_react2.useState)(null);
772
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(false);
773
+ const [error, setError] = (0, import_react2.useState)(null);
774
+ const fetchEstimate = (0, import_react2.useCallback)(async () => {
775
+ if (!amount || parseFloat(amount) <= 0 || !sourceChainId || !destChainId) {
776
+ setEstimate(null);
777
+ setError(null);
778
+ return;
779
+ }
780
+ setIsLoading(true);
781
+ setError(null);
782
+ try {
783
+ const sourceBridgeChain = getBridgeChain(sourceChainId);
784
+ const destBridgeChain = getBridgeChain(destChainId);
785
+ if (!sourceBridgeChain || !destBridgeChain) {
786
+ setEstimate({
787
+ gasFee: "Estimated by wallet",
788
+ bridgeFee: "0.00",
789
+ totalFee: "Gas only",
790
+ estimatedTime: "~15-20 minutes"
791
+ });
792
+ setIsLoading(false);
793
+ return;
794
+ }
795
+ setEstimate({
796
+ gasFee: "Estimated by wallet",
797
+ bridgeFee: "0-14 bps (FAST) / 0 (SLOW)",
798
+ totalFee: "Gas + protocol fee",
799
+ estimatedTime: "~15-20 minutes"
800
+ });
801
+ } catch (err) {
802
+ const error2 = err instanceof Error ? err : new Error("Failed to estimate bridge cost");
803
+ setError(error2);
804
+ setEstimate(null);
805
+ } finally {
806
+ setIsLoading(false);
807
+ }
808
+ }, [sourceChainId, destChainId, amount]);
809
+ (0, import_react2.useEffect)(() => {
810
+ let isActive = true;
811
+ const debounceTimer = setTimeout(() => {
812
+ if (isActive) {
813
+ fetchEstimate();
814
+ }
815
+ }, 500);
816
+ return () => {
817
+ isActive = false;
818
+ clearTimeout(debounceTimer);
819
+ };
820
+ }, [fetchEstimate]);
821
+ return { estimate, isLoading, error };
822
+ }
823
+ function useFormatNumber() {
824
+ (0, import_react2.useEffect)(() => {
825
+ console.warn(
826
+ "[DEPRECATED] useFormatNumber is deprecated and will be removed in a future version. Use the formatNumber utility function from './utils' directly instead."
827
+ );
828
+ }, []);
829
+ return (0, import_react2.useCallback)(
830
+ (value, decimals = 2) => {
831
+ return formatNumber(value, decimals);
832
+ },
833
+ []
834
+ );
835
+ }
836
+
837
+ // src/chains.ts
838
+ var import_viem3 = require("viem");
839
+ var import_chains = require("viem/chains");
840
+ var unichain = (0, import_viem3.defineChain)({
841
+ id: 130,
842
+ name: "Unichain",
843
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
844
+ rpcUrls: {
845
+ default: { http: ["https://mainnet.unichain.org"] }
846
+ },
847
+ blockExplorers: {
848
+ default: { name: "Uniscan", url: "https://uniscan.xyz" }
849
+ }
850
+ });
851
+ var hyperEvm = (0, import_viem3.defineChain)({
852
+ id: 999,
853
+ name: "HyperEVM",
854
+ nativeCurrency: { name: "HYPE", symbol: "HYPE", decimals: 18 },
855
+ rpcUrls: {
856
+ default: { http: ["https://rpc.hyperliquid.xyz/evm"] }
857
+ },
858
+ blockExplorers: {
859
+ default: { name: "Hyperscan", url: "https://hyperscan.xyz" }
860
+ }
861
+ });
862
+ var plume = (0, import_viem3.defineChain)({
863
+ id: 98866,
864
+ name: "Plume",
865
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
866
+ rpcUrls: {
867
+ default: { http: ["https://rpc.plume.org"] }
868
+ },
869
+ blockExplorers: {
870
+ default: { name: "Plume Explorer", url: "https://explorer.plume.org" }
871
+ }
872
+ });
873
+ var monad = (0, import_viem3.defineChain)({
874
+ id: 10200,
875
+ name: "Monad",
876
+ nativeCurrency: { name: "MON", symbol: "MON", decimals: 18 },
877
+ rpcUrls: {
878
+ default: { http: ["https://rpc.monad.xyz"] }
879
+ },
880
+ blockExplorers: {
881
+ default: { name: "Monad Explorer", url: "https://explorer.monad.xyz" }
882
+ }
883
+ });
884
+ var codex = (0, import_viem3.defineChain)({
885
+ id: 81224,
886
+ name: "Codex",
887
+ nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 },
888
+ rpcUrls: {
889
+ default: { http: ["https://rpc.codex.storage"] }
890
+ },
891
+ blockExplorers: {
892
+ default: { name: "Codex Explorer", url: "https://explorer.codex.storage" }
893
+ }
894
+ });
895
+ function createChainConfig(chain, options) {
896
+ return {
897
+ chain,
898
+ usdcAddress: options?.usdcAddress || USDC_ADDRESSES[chain.id],
899
+ tokenMessengerAddress: options?.tokenMessengerAddress || TOKEN_MESSENGER_ADDRESSES[chain.id],
900
+ iconUrl: options?.iconUrl || CHAIN_ICONS[chain.id]
901
+ };
902
+ }
903
+ var DEFAULT_CHAIN_CONFIGS = [
904
+ createChainConfig(import_chains.mainnet),
905
+ createChainConfig(import_chains.arbitrum),
906
+ createChainConfig(import_chains.base),
907
+ createChainConfig(import_chains.optimism),
908
+ createChainConfig(import_chains.polygon),
909
+ createChainConfig(import_chains.avalanche),
910
+ createChainConfig(import_chains.linea),
911
+ createChainConfig(import_chains.sonic),
912
+ createChainConfig(import_chains.worldchain),
913
+ createChainConfig(import_chains.sei),
914
+ createChainConfig(import_chains.xdc),
915
+ createChainConfig(import_chains.ink),
916
+ createChainConfig(unichain),
917
+ createChainConfig(hyperEvm),
918
+ createChainConfig(plume),
919
+ createChainConfig(codex)
920
+ ];
921
+ var TESTNET_USDC_ADDRESSES = {
922
+ 11155111: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
923
+ // Sepolia
924
+ 421614: "0x75faf114eafb1BDbe2F0316DF893fd58CE46AA4d",
925
+ // Arbitrum Sepolia
926
+ 43113: "0x5425890298aed601595a70AB815c96711a31Bc65",
927
+ // Avalanche Fuji
928
+ 84532: "0x036CbD53842c5426634e7929541eC2318f3dCF7e",
929
+ // Base Sepolia
930
+ 11155420: "0x5fd84259d66Cd46123540766Be93DFE6D43130D7",
931
+ // Optimism Sepolia
932
+ 80002: "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582"
933
+ // Polygon Amoy
934
+ };
935
+ var TESTNET_TOKEN_MESSENGER_V2_ADDRESS = "0x9f3B8679c73C2Fef8b59B4f3444d4e156fb70AA5";
936
+ function createTestnetChainConfig(chain, options) {
937
+ return {
938
+ chain,
939
+ usdcAddress: options?.usdcAddress || TESTNET_USDC_ADDRESSES[chain.id],
940
+ tokenMessengerAddress: options?.tokenMessengerAddress || TESTNET_TOKEN_MESSENGER_V2_ADDRESS,
941
+ iconUrl: options?.iconUrl || CHAIN_ICONS[chain.id]
942
+ };
943
+ }
944
+ var TESTNET_CHAIN_CONFIGS = [
945
+ createTestnetChainConfig(import_chains.sepolia),
946
+ createTestnetChainConfig(import_chains.arbitrumSepolia),
947
+ createTestnetChainConfig(import_chains.avalancheFuji),
948
+ createTestnetChainConfig(import_chains.baseSepolia),
949
+ createTestnetChainConfig(import_chains.optimismSepolia),
950
+ createTestnetChainConfig(import_chains.polygonAmoy)
951
+ ];
952
+
953
+ // src/theme.ts
954
+ var THEME_COLORS = {
955
+ /** Primary accent - Indigo */
956
+ primary: "#6366f1",
957
+ /** Secondary accent - Purple */
958
+ secondary: "#a855f7",
959
+ /** Success state - Green */
960
+ success: "#22c55e",
961
+ /** Error state - Red */
962
+ error: "#ef4444",
963
+ /** Text color - White */
964
+ text: "#ffffff",
965
+ /** Muted text - Semi-transparent white */
966
+ mutedText: "rgba(255, 255, 255, 0.54)",
967
+ /** Border color - Semi-transparent white */
968
+ border: "rgba(255, 255, 255, 0.06)",
969
+ /** Background - Dark with transparency */
970
+ background: "rgba(15, 15, 25, 0.8)",
971
+ /** Card background - Darker with transparency */
972
+ cardBackground: "rgba(15, 15, 25, 0.6)",
973
+ /** Dropdown background - Near opaque dark */
974
+ dropdownBackground: "rgba(20, 20, 35, 0.98)",
975
+ /** Input background - Transparent black */
976
+ inputBackground: "rgba(0, 0, 0, 0.3)",
977
+ /** Hover state - Semi-transparent white */
978
+ hover: "rgba(255, 255, 255, 0.05)"
979
+ };
980
+ var THEME_SIZING = {
981
+ /** Default border radius in pixels */
982
+ borderRadius: 12,
983
+ /** Widget max width */
984
+ maxWidth: "480px",
985
+ /** Standard padding */
986
+ padding: "16px",
987
+ /** Gap between elements */
988
+ gap: "12px",
989
+ /** Small gap */
990
+ smallGap: "8px",
991
+ /** Dropdown max height */
992
+ dropdownMaxHeight: "300px"
993
+ };
994
+ var THEME_FONTS = {
995
+ /** Primary font family */
996
+ family: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
997
+ /** Font sizes */
998
+ sizes: {
999
+ xs: "10px",
1000
+ sm: "12px",
1001
+ base: "14px",
1002
+ lg: "18px"
1003
+ },
1004
+ /** Font weights */
1005
+ weights: {
1006
+ normal: 400,
1007
+ medium: 500,
1008
+ semibold: 600,
1009
+ bold: 700
1010
+ }
1011
+ };
1012
+ var defaultTheme = {
1013
+ primaryColor: THEME_COLORS.primary,
1014
+ secondaryColor: THEME_COLORS.secondary,
1015
+ backgroundColor: THEME_COLORS.background,
1016
+ cardBackgroundColor: THEME_COLORS.cardBackground,
1017
+ textColor: THEME_COLORS.text,
1018
+ mutedTextColor: THEME_COLORS.mutedText,
1019
+ borderColor: THEME_COLORS.border,
1020
+ successColor: THEME_COLORS.success,
1021
+ errorColor: THEME_COLORS.error,
1022
+ hoverColor: THEME_COLORS.hover,
1023
+ borderRadius: THEME_SIZING.borderRadius,
1024
+ fontFamily: THEME_FONTS.family
1025
+ };
1026
+ function mergeTheme(theme) {
1027
+ return { ...defaultTheme, ...theme };
1028
+ }
1029
+ var themePresets = {
1030
+ /** Default dark theme */
1031
+ dark: defaultTheme,
1032
+ /** Light theme variant */
1033
+ light: {
1034
+ ...defaultTheme,
1035
+ backgroundColor: "rgba(255, 255, 255, 0.95)",
1036
+ cardBackgroundColor: "rgba(245, 245, 245, 0.9)",
1037
+ textColor: "#1a1a2e",
1038
+ mutedTextColor: "rgba(0, 0, 0, 0.54)",
1039
+ borderColor: "rgba(0, 0, 0, 0.1)",
1040
+ hoverColor: "rgba(0, 0, 0, 0.05)"
1041
+ },
1042
+ /** Blue accent theme */
1043
+ blue: {
1044
+ ...defaultTheme,
1045
+ primaryColor: "#3b82f6",
1046
+ secondaryColor: "#06b6d4"
1047
+ },
1048
+ /** Green accent theme */
1049
+ green: {
1050
+ ...defaultTheme,
1051
+ primaryColor: "#10b981",
1052
+ secondaryColor: "#34d399"
1053
+ }
1054
+ };
1055
+
1056
+ // src/icons.tsx
1057
+ var import_jsx_runtime = require("react/jsx-runtime");
1058
+ function ChevronDownIcon({
1059
+ size = 16,
1060
+ color = "currentColor",
1061
+ className,
1062
+ style
1063
+ }) {
1064
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1065
+ "svg",
1066
+ {
1067
+ width: size,
1068
+ height: size,
1069
+ viewBox: "0 0 24 24",
1070
+ fill: "none",
1071
+ stroke: color,
1072
+ className,
1073
+ style,
1074
+ "aria-hidden": "true",
1075
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1076
+ "path",
1077
+ {
1078
+ strokeLinecap: "round",
1079
+ strokeLinejoin: "round",
1080
+ strokeWidth: 2,
1081
+ d: "M19 9l-7 7-7-7"
1082
+ }
1083
+ )
1084
+ }
1085
+ );
1086
+ }
1087
+ function SwapIcon({
1088
+ size = 20,
1089
+ color = "currentColor",
1090
+ className,
1091
+ style
1092
+ }) {
1093
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1094
+ "svg",
1095
+ {
1096
+ width: size,
1097
+ height: size,
1098
+ viewBox: "0 0 24 24",
1099
+ fill: "none",
1100
+ stroke: color,
1101
+ className,
1102
+ style,
1103
+ "aria-hidden": "true",
1104
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1105
+ "path",
1106
+ {
1107
+ strokeLinecap: "round",
1108
+ strokeLinejoin: "round",
1109
+ strokeWidth: 2,
1110
+ d: "M7 16V4m0 0L3 8m4-4l4 4m6 0v12m0 0l4-4m-4 4l-4-4"
1111
+ }
1112
+ )
1113
+ }
1114
+ );
1115
+ }
1116
+ function SpinnerIcon({
1117
+ size = 20,
1118
+ color = "currentColor",
1119
+ className,
1120
+ style
1121
+ }) {
1122
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
1123
+ "svg",
1124
+ {
1125
+ width: size,
1126
+ height: size,
1127
+ viewBox: "0 0 24 24",
1128
+ fill: "none",
1129
+ stroke: color,
1130
+ className,
1131
+ style: {
1132
+ animation: "cc-spin 1s linear infinite",
1133
+ ...style
1134
+ },
1135
+ "aria-hidden": "true",
1136
+ children: [
1137
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes cc-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
1138
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1139
+ "circle",
1140
+ {
1141
+ cx: "12",
1142
+ cy: "12",
1143
+ r: "10",
1144
+ strokeWidth: 2,
1145
+ strokeDasharray: "32",
1146
+ strokeLinecap: "round"
1147
+ }
1148
+ )
1149
+ ]
1150
+ }
1151
+ );
1152
+ }
1153
+ function CheckIcon({
1154
+ size = 20,
1155
+ color = "currentColor",
1156
+ className,
1157
+ style
1158
+ }) {
1159
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1160
+ "svg",
1161
+ {
1162
+ width: size,
1163
+ height: size,
1164
+ viewBox: "0 0 24 24",
1165
+ fill: "none",
1166
+ stroke: color,
1167
+ className,
1168
+ style,
1169
+ "aria-hidden": "true",
1170
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1171
+ "path",
1172
+ {
1173
+ strokeLinecap: "round",
1174
+ strokeLinejoin: "round",
1175
+ strokeWidth: 2,
1176
+ d: "M5 13l4 4L19 7"
1177
+ }
1178
+ )
1179
+ }
1180
+ );
1181
+ }
1182
+ function ErrorIcon({
1183
+ size = 20,
1184
+ color = "currentColor",
1185
+ className,
1186
+ style
1187
+ }) {
1188
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1189
+ "svg",
1190
+ {
1191
+ width: size,
1192
+ height: size,
1193
+ viewBox: "0 0 24 24",
1194
+ fill: "none",
1195
+ stroke: color,
1196
+ className,
1197
+ style,
1198
+ "aria-hidden": "true",
1199
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1200
+ "path",
1201
+ {
1202
+ strokeLinecap: "round",
1203
+ strokeLinejoin: "round",
1204
+ strokeWidth: 2,
1205
+ d: "M6 18L18 6M6 6l12 12"
1206
+ }
1207
+ )
1208
+ }
1209
+ );
1210
+ }
1211
+ function ExternalLinkIcon({
1212
+ size = 16,
1213
+ color = "currentColor",
1214
+ className,
1215
+ style
1216
+ }) {
1217
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1218
+ "svg",
1219
+ {
1220
+ width: size,
1221
+ height: size,
1222
+ viewBox: "0 0 24 24",
1223
+ fill: "none",
1224
+ stroke: color,
1225
+ className,
1226
+ style,
1227
+ "aria-hidden": "true",
1228
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1229
+ "path",
1230
+ {
1231
+ strokeLinecap: "round",
1232
+ strokeLinejoin: "round",
1233
+ strokeWidth: 2,
1234
+ d: "M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"
1235
+ }
1236
+ )
1237
+ }
1238
+ );
1239
+ }
1240
+ function WalletIcon({
1241
+ size = 20,
1242
+ color = "currentColor",
1243
+ className,
1244
+ style
1245
+ }) {
1246
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1247
+ "svg",
1248
+ {
1249
+ width: size,
1250
+ height: size,
1251
+ viewBox: "0 0 24 24",
1252
+ fill: "none",
1253
+ stroke: color,
1254
+ className,
1255
+ style,
1256
+ "aria-hidden": "true",
1257
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
1258
+ "path",
1259
+ {
1260
+ strokeLinecap: "round",
1261
+ strokeLinejoin: "round",
1262
+ strokeWidth: 2,
1263
+ d: "M3 10h18M3 6h18a2 2 0 012 2v10a2 2 0 01-2 2H3a2 2 0 01-2-2V8a2 2 0 012-2zm14 6h.01"
1264
+ }
1265
+ )
1266
+ }
1267
+ );
1268
+ }
1269
+
1270
+ // src/BridgeWidget.tsx
1271
+ var import_jsx_runtime2 = require("react/jsx-runtime");
1272
+ function ChainIcon({
1273
+ chainConfig,
1274
+ theme,
1275
+ size = 24
1276
+ }) {
1277
+ const [hasError, setHasError] = (0, import_react3.useState)(false);
1278
+ if (!chainConfig.iconUrl || hasError) {
1279
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1280
+ "div",
1281
+ {
1282
+ style: {
1283
+ width: `${size}px`,
1284
+ height: `${size}px`,
1285
+ borderRadius: "50%",
1286
+ display: "flex",
1287
+ alignItems: "center",
1288
+ justifyContent: "center",
1289
+ fontSize: `${size * 0.5}px`,
1290
+ fontWeight: "bold",
1291
+ color: theme.textColor,
1292
+ background: `linear-gradient(135deg, ${theme.primaryColor}, ${theme.secondaryColor})`
1293
+ },
1294
+ "aria-hidden": "true",
1295
+ children: chainConfig.chain.name.charAt(0)
1296
+ }
1297
+ );
1298
+ }
1299
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1300
+ "img",
1301
+ {
1302
+ src: chainConfig.iconUrl,
1303
+ alt: "",
1304
+ "aria-hidden": "true",
1305
+ style: { width: `${size}px`, height: `${size}px`, borderRadius: "50%" },
1306
+ onError: () => setHasError(true)
1307
+ }
1308
+ );
1309
+ }
1310
+ function BalanceSpinner({ size = 12 }) {
1311
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1312
+ "svg",
1313
+ {
1314
+ width: size,
1315
+ height: size,
1316
+ viewBox: "0 0 24 24",
1317
+ fill: "none",
1318
+ stroke: "currentColor",
1319
+ strokeWidth: "2",
1320
+ style: {
1321
+ animation: "cc-balance-spin 1s linear infinite",
1322
+ opacity: 0.6
1323
+ },
1324
+ "aria-hidden": "true",
1325
+ children: [
1326
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes cc-balance-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
1327
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.25" }),
1328
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })
1329
+ ]
1330
+ }
1331
+ );
1332
+ }
1333
+ function ChainSelector({
1334
+ label,
1335
+ chains,
1336
+ selectedChain,
1337
+ onSelect,
1338
+ excludeChainId,
1339
+ theme,
1340
+ id,
1341
+ balances,
1342
+ isLoadingBalances,
1343
+ disabled
1344
+ }) {
1345
+ const [isOpen, setIsOpen] = (0, import_react3.useState)(false);
1346
+ const [focusedIndex, setFocusedIndex] = (0, import_react3.useState)(-1);
1347
+ const [typeAhead, setTypeAhead] = (0, import_react3.useState)("");
1348
+ const typeAheadTimeoutRef = (0, import_react3.useRef)(null);
1349
+ const buttonRef = (0, import_react3.useRef)(null);
1350
+ const listRef = (0, import_react3.useRef)(null);
1351
+ const availableChains = chains.filter(
1352
+ (c) => c.chain.id !== excludeChainId
1353
+ );
1354
+ (0, import_react3.useEffect)(() => {
1355
+ return () => {
1356
+ if (typeAheadTimeoutRef.current) {
1357
+ clearTimeout(typeAheadTimeoutRef.current);
1358
+ }
1359
+ };
1360
+ }, []);
1361
+ const handleButtonKeyDown = (0, import_react3.useCallback)(
1362
+ (e) => {
1363
+ if (e.key === "Escape") {
1364
+ setIsOpen(false);
1365
+ buttonRef.current?.focus();
1366
+ } else if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
1367
+ if (!isOpen) {
1368
+ e.preventDefault();
1369
+ setIsOpen(true);
1370
+ setFocusedIndex(0);
1371
+ }
1372
+ }
1373
+ },
1374
+ [isOpen]
1375
+ );
1376
+ const handleListKeyDown = (0, import_react3.useCallback)(
1377
+ (e) => {
1378
+ if (e.key === "Escape") {
1379
+ setIsOpen(false);
1380
+ setTypeAhead("");
1381
+ buttonRef.current?.focus();
1382
+ } else if (e.key === "ArrowDown") {
1383
+ e.preventDefault();
1384
+ setFocusedIndex(
1385
+ (prev) => prev < availableChains.length - 1 ? prev + 1 : 0
1386
+ );
1387
+ } else if (e.key === "ArrowUp") {
1388
+ e.preventDefault();
1389
+ setFocusedIndex(
1390
+ (prev) => prev > 0 ? prev - 1 : availableChains.length - 1
1391
+ );
1392
+ } else if (e.key === "Enter" || e.key === " ") {
1393
+ e.preventDefault();
1394
+ if (focusedIndex >= 0 && focusedIndex < availableChains.length) {
1395
+ onSelect(availableChains[focusedIndex]);
1396
+ setIsOpen(false);
1397
+ setTypeAhead("");
1398
+ buttonRef.current?.focus();
1399
+ }
1400
+ } else if (e.key === "Home") {
1401
+ e.preventDefault();
1402
+ setFocusedIndex(0);
1403
+ } else if (e.key === "End") {
1404
+ e.preventDefault();
1405
+ setFocusedIndex(availableChains.length - 1);
1406
+ } else if (e.key.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey) {
1407
+ e.preventDefault();
1408
+ const newTypeAhead = typeAhead + e.key.toLowerCase();
1409
+ setTypeAhead(newTypeAhead);
1410
+ if (typeAheadTimeoutRef.current) {
1411
+ clearTimeout(typeAheadTimeoutRef.current);
1412
+ }
1413
+ typeAheadTimeoutRef.current = setTimeout(() => {
1414
+ setTypeAhead("");
1415
+ }, 1e3);
1416
+ const matchIndex = availableChains.findIndex(
1417
+ (chain) => chain.chain.name.toLowerCase().startsWith(newTypeAhead)
1418
+ );
1419
+ if (matchIndex !== -1) {
1420
+ setFocusedIndex(matchIndex);
1421
+ }
1422
+ }
1423
+ },
1424
+ [availableChains, focusedIndex, onSelect, typeAhead]
1425
+ );
1426
+ (0, import_react3.useEffect)(() => {
1427
+ if (isOpen && listRef.current) {
1428
+ listRef.current.focus();
1429
+ }
1430
+ }, [isOpen]);
1431
+ (0, import_react3.useEffect)(() => {
1432
+ if (!isOpen) return;
1433
+ const handleGlobalKeyDown = (e) => {
1434
+ if (e.key === "Escape") {
1435
+ setIsOpen(false);
1436
+ setTypeAhead("");
1437
+ buttonRef.current?.focus();
1438
+ }
1439
+ };
1440
+ document.addEventListener("keydown", handleGlobalKeyDown);
1441
+ return () => document.removeEventListener("keydown", handleGlobalKeyDown);
1442
+ }, [isOpen]);
1443
+ (0, import_react3.useEffect)(() => {
1444
+ if (!isOpen) {
1445
+ setTypeAhead("");
1446
+ if (typeAheadTimeoutRef.current) {
1447
+ clearTimeout(typeAheadTimeoutRef.current);
1448
+ typeAheadTimeoutRef.current = null;
1449
+ }
1450
+ }
1451
+ }, [isOpen]);
1452
+ const buttonId = `${id}-button`;
1453
+ const listboxId = `${id}-listbox`;
1454
+ const selectedBalance = balances?.[selectedChain.chain.id];
1455
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { position: "relative", flex: 1 }, children: [
1456
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1457
+ "label",
1458
+ {
1459
+ id: `${id}-label`,
1460
+ htmlFor: buttonId,
1461
+ style: {
1462
+ display: "block",
1463
+ fontSize: "10px",
1464
+ color: theme.mutedTextColor,
1465
+ textTransform: "uppercase",
1466
+ letterSpacing: "0.05em",
1467
+ fontWeight: 500,
1468
+ marginBottom: "4px"
1469
+ },
1470
+ children: label
1471
+ }
1472
+ ),
1473
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1474
+ "button",
1475
+ {
1476
+ ref: buttonRef,
1477
+ id: buttonId,
1478
+ onClick: () => !disabled && setIsOpen(!isOpen),
1479
+ onKeyDown: disabled ? void 0 : handleButtonKeyDown,
1480
+ disabled,
1481
+ "aria-haspopup": "listbox",
1482
+ "aria-expanded": isOpen,
1483
+ "aria-labelledby": `${id}-label`,
1484
+ "aria-controls": isOpen ? listboxId : void 0,
1485
+ "aria-disabled": disabled,
1486
+ style: {
1487
+ width: "100%",
1488
+ display: "flex",
1489
+ alignItems: "center",
1490
+ justifyContent: "space-between",
1491
+ padding: "10px 12px",
1492
+ borderRadius: `${theme.borderRadius}px`,
1493
+ background: theme.cardBackgroundColor,
1494
+ border: `1px solid ${theme.borderColor}`,
1495
+ cursor: disabled ? "not-allowed" : "pointer",
1496
+ opacity: disabled ? 0.6 : 1,
1497
+ transition: "all 0.2s"
1498
+ },
1499
+ children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChainIcon, { chainConfig: selectedChain, theme }),
1502
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { display: "flex", flexDirection: "column", alignItems: "flex-start" }, children: [
1503
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1504
+ "span",
1505
+ {
1506
+ style: {
1507
+ fontSize: "14px",
1508
+ fontWeight: 500,
1509
+ color: theme.textColor
1510
+ },
1511
+ children: selectedChain.chain.name
1512
+ }
1513
+ ),
1514
+ isLoadingBalances ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1515
+ "span",
1516
+ {
1517
+ style: {
1518
+ fontSize: "10px",
1519
+ color: theme.mutedTextColor,
1520
+ display: "flex",
1521
+ alignItems: "center",
1522
+ gap: "4px"
1523
+ },
1524
+ children: [
1525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BalanceSpinner, { size: 10 }),
1526
+ " Loading..."
1527
+ ]
1528
+ }
1529
+ ) : selectedBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1530
+ "span",
1531
+ {
1532
+ style: {
1533
+ fontSize: "10px",
1534
+ color: theme.mutedTextColor
1535
+ },
1536
+ children: [
1537
+ formatNumber(selectedBalance.formatted, 2),
1538
+ " USDC"
1539
+ ]
1540
+ }
1541
+ ) : null
1542
+ ] })
1543
+ ] }),
1544
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1545
+ ChevronDownIcon,
1546
+ {
1547
+ size: 16,
1548
+ color: theme.mutedTextColor,
1549
+ style: {
1550
+ transition: "transform 0.2s",
1551
+ transform: isOpen ? "rotate(180deg)" : "rotate(0deg)"
1552
+ }
1553
+ }
1554
+ )
1555
+ ]
1556
+ }
1557
+ ),
1558
+ isOpen && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
1559
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1560
+ "div",
1561
+ {
1562
+ style: {
1563
+ position: "fixed",
1564
+ inset: 0,
1565
+ zIndex: 10
1566
+ },
1567
+ onClick: () => setIsOpen(false),
1568
+ "aria-hidden": "true"
1569
+ }
1570
+ ),
1571
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1572
+ "ul",
1573
+ {
1574
+ ref: listRef,
1575
+ id: listboxId,
1576
+ role: "listbox",
1577
+ "aria-labelledby": `${id}-label`,
1578
+ "aria-activedescendant": focusedIndex >= 0 ? `${id}-option-${availableChains[focusedIndex]?.chain.id}` : void 0,
1579
+ tabIndex: 0,
1580
+ onKeyDown: handleListKeyDown,
1581
+ style: {
1582
+ position: "absolute",
1583
+ zIndex: 20,
1584
+ width: "100%",
1585
+ marginTop: "8px",
1586
+ borderRadius: `${theme.borderRadius}px`,
1587
+ boxShadow: "0 10px 40px rgba(0,0,0,0.3)",
1588
+ background: theme.cardBackgroundColor,
1589
+ backdropFilter: "blur(10px)",
1590
+ border: `1px solid ${theme.borderColor}`,
1591
+ maxHeight: "300px",
1592
+ overflowY: "auto",
1593
+ overflowX: "hidden",
1594
+ padding: 0,
1595
+ margin: 0,
1596
+ listStyle: "none",
1597
+ outline: "none"
1598
+ },
1599
+ children: availableChains.map((chainConfig, index) => {
1600
+ const chainBalance = balances?.[chainConfig.chain.id];
1601
+ const isFocused = index === focusedIndex;
1602
+ const isSelected = chainConfig.chain.id === selectedChain.chain.id;
1603
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1604
+ "li",
1605
+ {
1606
+ id: `${id}-option-${chainConfig.chain.id}`,
1607
+ role: "option",
1608
+ "aria-selected": isSelected,
1609
+ onClick: () => {
1610
+ onSelect(chainConfig);
1611
+ setIsOpen(false);
1612
+ buttonRef.current?.focus();
1613
+ },
1614
+ style: {
1615
+ width: "100%",
1616
+ display: "flex",
1617
+ alignItems: "center",
1618
+ gap: "8px",
1619
+ padding: "10px 12px",
1620
+ background: isFocused ? theme.hoverColor : "transparent",
1621
+ border: "none",
1622
+ cursor: "pointer",
1623
+ transition: "background 0.2s"
1624
+ },
1625
+ onMouseEnter: () => setFocusedIndex(index),
1626
+ children: [
1627
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ChainIcon, { chainConfig, theme }),
1628
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { flex: 1, display: "flex", flexDirection: "column" }, children: [
1629
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1630
+ "span",
1631
+ {
1632
+ style: {
1633
+ fontSize: "14px",
1634
+ color: isSelected ? theme.textColor : theme.mutedTextColor,
1635
+ fontWeight: isSelected ? 500 : 400
1636
+ },
1637
+ children: chainConfig.chain.name
1638
+ }
1639
+ ),
1640
+ isLoadingBalances ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1641
+ "span",
1642
+ {
1643
+ style: {
1644
+ fontSize: "10px",
1645
+ color: theme.mutedTextColor,
1646
+ display: "flex",
1647
+ alignItems: "center",
1648
+ gap: "4px"
1649
+ },
1650
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BalanceSpinner, { size: 10 })
1651
+ }
1652
+ ) : chainBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1653
+ "span",
1654
+ {
1655
+ style: {
1656
+ fontSize: "10px",
1657
+ color: parseFloat(chainBalance.formatted) > 0 ? theme.successColor : theme.mutedTextColor
1658
+ },
1659
+ children: [
1660
+ formatNumber(chainBalance.formatted, 2),
1661
+ " USDC"
1662
+ ]
1663
+ }
1664
+ ) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1665
+ "span",
1666
+ {
1667
+ style: {
1668
+ fontSize: "10px",
1669
+ color: theme.mutedTextColor
1670
+ },
1671
+ children: "0.00 USDC"
1672
+ }
1673
+ )
1674
+ ] })
1675
+ ]
1676
+ },
1677
+ chainConfig.chain.id
1678
+ );
1679
+ })
1680
+ }
1681
+ )
1682
+ ] })
1683
+ ] });
1684
+ }
1685
+ function SwapButton({
1686
+ onClick,
1687
+ theme,
1688
+ disabled
1689
+ }) {
1690
+ const [isHovered, setIsHovered] = (0, import_react3.useState)(false);
1691
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1692
+ "button",
1693
+ {
1694
+ onClick,
1695
+ disabled,
1696
+ "aria-label": "Swap source and destination chains",
1697
+ style: {
1698
+ padding: "8px",
1699
+ borderRadius: `${theme.borderRadius}px`,
1700
+ background: `${theme.primaryColor}15`,
1701
+ border: `1px solid ${theme.primaryColor}40`,
1702
+ cursor: disabled ? "not-allowed" : "pointer",
1703
+ opacity: disabled ? 0.5 : 1,
1704
+ transition: "all 0.2s",
1705
+ display: "flex",
1706
+ alignItems: "center",
1707
+ justifyContent: "center",
1708
+ alignSelf: "flex-end",
1709
+ marginBottom: "4px",
1710
+ transform: isHovered && !disabled ? "scale(1.1)" : "scale(1)"
1711
+ },
1712
+ onMouseEnter: () => setIsHovered(true),
1713
+ onMouseLeave: () => setIsHovered(false),
1714
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SwapIcon, { size: 20, color: theme.primaryColor })
1715
+ }
1716
+ );
1717
+ }
1718
+ function AmountInput({
1719
+ value,
1720
+ onChange,
1721
+ balance,
1722
+ onMaxClick,
1723
+ theme,
1724
+ id,
1725
+ disabled
1726
+ }) {
1727
+ const inputId = `${id}-input`;
1728
+ const labelId = `${id}-label`;
1729
+ const handleInputChange = (0, import_react3.useCallback)(
1730
+ (e) => {
1731
+ if (disabled) return;
1732
+ const result = validateAmountInput(e.target.value);
1733
+ if (result.isValid) {
1734
+ onChange(result.sanitized);
1735
+ } else if (result.sanitized) {
1736
+ onChange(result.sanitized);
1737
+ }
1738
+ },
1739
+ [onChange, disabled]
1740
+ );
1741
+ const handleKeyDown = (0, import_react3.useCallback)((e) => {
1742
+ if (e.key === "e" || e.key === "E" || e.key === "+" || e.key === "-") {
1743
+ e.preventDefault();
1744
+ }
1745
+ }, []);
1746
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { children: [
1747
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1748
+ "div",
1749
+ {
1750
+ style: {
1751
+ display: "flex",
1752
+ justifyContent: "space-between",
1753
+ marginBottom: "4px"
1754
+ },
1755
+ children: [
1756
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1757
+ "label",
1758
+ {
1759
+ id: labelId,
1760
+ htmlFor: inputId,
1761
+ style: {
1762
+ fontSize: "10px",
1763
+ color: theme.mutedTextColor,
1764
+ textTransform: "uppercase",
1765
+ letterSpacing: "0.05em",
1766
+ fontWeight: 500
1767
+ },
1768
+ children: "Amount"
1769
+ }
1770
+ ),
1771
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1772
+ "span",
1773
+ {
1774
+ style: { fontSize: "10px", color: theme.mutedTextColor },
1775
+ "aria-live": "polite",
1776
+ children: [
1777
+ "Balance:",
1778
+ " ",
1779
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { color: theme.textColor }, children: [
1780
+ formatNumber(balance),
1781
+ " USDC"
1782
+ ] })
1783
+ ]
1784
+ }
1785
+ )
1786
+ ]
1787
+ }
1788
+ ),
1789
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1790
+ "div",
1791
+ {
1792
+ style: {
1793
+ display: "flex",
1794
+ alignItems: "center",
1795
+ borderRadius: `${theme.borderRadius}px`,
1796
+ overflow: "hidden",
1797
+ background: theme.cardBackgroundColor,
1798
+ border: `1px solid ${theme.borderColor}`,
1799
+ opacity: disabled ? 0.6 : 1
1800
+ },
1801
+ children: [
1802
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1803
+ "input",
1804
+ {
1805
+ id: inputId,
1806
+ type: "text",
1807
+ inputMode: "decimal",
1808
+ pattern: "[0-9]*\\.?[0-9]*",
1809
+ value,
1810
+ onChange: handleInputChange,
1811
+ onKeyDown: handleKeyDown,
1812
+ placeholder: "0.00",
1813
+ disabled,
1814
+ "aria-labelledby": labelId,
1815
+ "aria-describedby": `${id}-currency`,
1816
+ "aria-disabled": disabled,
1817
+ style: {
1818
+ flex: 1,
1819
+ background: "transparent",
1820
+ border: "none",
1821
+ padding: "12px",
1822
+ fontSize: "18px",
1823
+ color: theme.textColor,
1824
+ fontWeight: 500,
1825
+ outline: "none",
1826
+ minWidth: 0,
1827
+ fontFamily: theme.fontFamily,
1828
+ cursor: disabled ? "not-allowed" : "text"
1829
+ }
1830
+ }
1831
+ ),
1832
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1833
+ "div",
1834
+ {
1835
+ style: {
1836
+ display: "flex",
1837
+ alignItems: "center",
1838
+ gap: "8px",
1839
+ paddingRight: "12px"
1840
+ },
1841
+ children: [
1842
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1843
+ "button",
1844
+ {
1845
+ onClick: onMaxClick,
1846
+ disabled,
1847
+ "aria-label": "Set maximum amount",
1848
+ style: {
1849
+ padding: "4px 8px",
1850
+ fontSize: "10px",
1851
+ fontWeight: 600,
1852
+ borderRadius: "4px",
1853
+ background: `${theme.primaryColor}20`,
1854
+ color: theme.primaryColor,
1855
+ border: "none",
1856
+ cursor: disabled ? "not-allowed" : "pointer",
1857
+ opacity: disabled ? 0.5 : 1
1858
+ },
1859
+ children: "MAX"
1860
+ }
1861
+ ),
1862
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1863
+ "div",
1864
+ {
1865
+ id: `${id}-currency`,
1866
+ style: { display: "flex", alignItems: "center", gap: "4px" },
1867
+ children: [
1868
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1869
+ "div",
1870
+ {
1871
+ style: {
1872
+ width: "20px",
1873
+ height: "20px",
1874
+ borderRadius: "50%",
1875
+ background: USDC_BRAND_COLOR,
1876
+ display: "flex",
1877
+ alignItems: "center",
1878
+ justifyContent: "center"
1879
+ },
1880
+ "aria-hidden": "true",
1881
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1882
+ "span",
1883
+ {
1884
+ style: {
1885
+ fontSize: "10px",
1886
+ fontWeight: "bold",
1887
+ color: "#fff"
1888
+ },
1889
+ children: "$"
1890
+ }
1891
+ )
1892
+ }
1893
+ ),
1894
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1895
+ "span",
1896
+ {
1897
+ style: {
1898
+ fontSize: "14px",
1899
+ fontWeight: 500,
1900
+ color: theme.textColor
1901
+ },
1902
+ children: "USDC"
1903
+ }
1904
+ )
1905
+ ]
1906
+ }
1907
+ )
1908
+ ]
1909
+ }
1910
+ )
1911
+ ]
1912
+ }
1913
+ )
1914
+ ] });
1915
+ }
1916
+ function BridgeWidget({
1917
+ chains = DEFAULT_CHAIN_CONFIGS,
1918
+ defaultSourceChainId,
1919
+ defaultDestinationChainId,
1920
+ onBridgeStart,
1921
+ onBridgeSuccess,
1922
+ onBridgeError,
1923
+ onConnectWallet,
1924
+ theme: themeOverrides,
1925
+ className,
1926
+ style
1927
+ }) {
1928
+ const theme = mergeTheme(themeOverrides);
1929
+ const { address, isConnected } = (0, import_wagmi3.useAccount)();
1930
+ const currentChainId = (0, import_wagmi3.useChainId)();
1931
+ const { switchChainAsync } = (0, import_wagmi3.useSwitchChain)();
1932
+ const { connect, connectors } = (0, import_wagmi3.useConnect)();
1933
+ const [configError, setConfigError] = (0, import_react3.useState)(null);
1934
+ (0, import_react3.useEffect)(() => {
1935
+ const validation = validateChainConfigs(chains);
1936
+ if (!validation.isValid) {
1937
+ const errorMsg = validation.errors.join("; ");
1938
+ console.error("[BridgeWidget] Invalid chain configuration:", errorMsg);
1939
+ setConfigError(errorMsg);
1940
+ } else {
1941
+ setConfigError(null);
1942
+ }
1943
+ }, [chains]);
1944
+ const baseId = (0, import_react3.useId)();
1945
+ const sourceChainId = `${baseId}-source`;
1946
+ const destChainId = `${baseId}-dest`;
1947
+ const amountId = `${baseId}-amount`;
1948
+ const getChainConfig = (0, import_react3.useCallback)(
1949
+ (chainId) => {
1950
+ if (!chainId) return chains[0];
1951
+ return chains.find((c) => c.chain.id === chainId) || chains[0];
1952
+ },
1953
+ [chains]
1954
+ );
1955
+ const [sourceChainConfig, setSourceChainConfig] = (0, import_react3.useState)(
1956
+ () => getChainConfig(defaultSourceChainId)
1957
+ );
1958
+ const [destChainConfig, setDestChainConfig] = (0, import_react3.useState)(
1959
+ () => getChainConfig(defaultDestinationChainId) || chains.find((c) => c.chain.id !== sourceChainConfig.chain.id) || chains[1] || chains[0]
1960
+ );
1961
+ const [amount, setAmount] = (0, import_react3.useState)("");
1962
+ const [txHash, setTxHash] = (0, import_react3.useState)();
1963
+ const [error, setError] = (0, import_react3.useState)(null);
1964
+ const { balances: allBalances, isLoading: isLoadingAllBalances, refetch: refetchAllBalances } = useAllUSDCBalances(chains);
1965
+ const { balanceFormatted, refetch: refetchBalance } = useUSDCBalance(
1966
+ sourceChainConfig
1967
+ );
1968
+ const { needsApproval, approve, isApproving } = useUSDCAllowance(
1969
+ sourceChainConfig
1970
+ );
1971
+ const refetchBalances = (0, import_react3.useCallback)(() => {
1972
+ refetchBalance();
1973
+ refetchAllBalances();
1974
+ }, [refetchBalance, refetchAllBalances]);
1975
+ const { bridge: executeBridge, state: bridgeState, reset: resetBridge } = useBridge();
1976
+ const { isLoading: isConfirming, isSuccess } = (0, import_wagmi3.useWaitForTransactionReceipt)({
1977
+ hash: txHash
1978
+ });
1979
+ const isBridging = bridgeState.status === "loading" || bridgeState.status === "approving" || bridgeState.status === "burning" || bridgeState.status === "fetching-attestation" || bridgeState.status === "minting";
1980
+ const isOperationPending = isBridging || isConfirming || isApproving;
1981
+ const onBridgeSuccessRef = (0, import_react3.useRef)(onBridgeSuccess);
1982
+ onBridgeSuccessRef.current = onBridgeSuccess;
1983
+ const onBridgeErrorRef = (0, import_react3.useRef)(onBridgeError);
1984
+ onBridgeErrorRef.current = onBridgeError;
1985
+ const needsChainSwitch = isConnected && currentChainId !== sourceChainConfig.chain.id;
1986
+ const handleSwapChains = (0, import_react3.useCallback)(() => {
1987
+ setSourceChainConfig((prev) => {
1988
+ setDestChainConfig(prev);
1989
+ return destChainConfig;
1990
+ });
1991
+ }, [destChainConfig]);
1992
+ const handleMaxClick = (0, import_react3.useCallback)(() => {
1993
+ setAmount(balanceFormatted);
1994
+ }, [balanceFormatted]);
1995
+ const handleSwitchChain = (0, import_react3.useCallback)(async () => {
1996
+ try {
1997
+ await switchChainAsync({ chainId: sourceChainConfig.chain.id });
1998
+ } catch (err) {
1999
+ setError(getErrorMessage(err));
2000
+ }
2001
+ }, [switchChainAsync, sourceChainConfig.chain.id]);
2002
+ const handleBridge = (0, import_react3.useCallback)(async () => {
2003
+ if (!address || !amount || parseFloat(amount) <= 0) return;
2004
+ setError(null);
2005
+ resetBridge();
2006
+ try {
2007
+ onBridgeStart?.({
2008
+ sourceChainId: sourceChainConfig.chain.id,
2009
+ destChainId: destChainConfig.chain.id,
2010
+ amount
2011
+ });
2012
+ if (needsApproval(amount)) {
2013
+ pendingBridgeRef.current = {
2014
+ amount,
2015
+ sourceChainConfig,
2016
+ destChainConfig
2017
+ };
2018
+ const approveTx = await approve(amount);
2019
+ setTxHash(approveTx);
2020
+ } else {
2021
+ await executeBridge({
2022
+ sourceChainConfig,
2023
+ destChainConfig,
2024
+ amount
2025
+ });
2026
+ }
2027
+ } catch (err) {
2028
+ const errorMessage = getErrorMessage(err);
2029
+ setError(errorMessage);
2030
+ onBridgeErrorRef.current?.(
2031
+ err instanceof Error ? err : new Error(errorMessage)
2032
+ );
2033
+ }
2034
+ }, [
2035
+ address,
2036
+ amount,
2037
+ needsApproval,
2038
+ approve,
2039
+ executeBridge,
2040
+ resetBridge,
2041
+ onBridgeStart,
2042
+ sourceChainConfig,
2043
+ destChainConfig
2044
+ ]);
2045
+ const pendingBridgeRef = (0, import_react3.useRef)(null);
2046
+ (0, import_react3.useEffect)(() => {
2047
+ if (isSuccess && txHash && pendingBridgeRef.current) {
2048
+ const { amount: pendingAmount, sourceChainConfig: pendingSource, destChainConfig: pendingDest } = pendingBridgeRef.current;
2049
+ pendingBridgeRef.current = null;
2050
+ setTxHash(void 0);
2051
+ void executeBridge({
2052
+ sourceChainConfig: pendingSource,
2053
+ destChainConfig: pendingDest,
2054
+ amount: pendingAmount
2055
+ }).catch((err) => {
2056
+ setError(getErrorMessage(err));
2057
+ });
2058
+ }
2059
+ }, [isSuccess, txHash, executeBridge]);
2060
+ (0, import_react3.useEffect)(() => {
2061
+ if (bridgeState.status === "success") {
2062
+ const currentAmount = amount;
2063
+ const currentSourceChainId = sourceChainConfig.chain.id;
2064
+ const currentDestChainId = destChainConfig.chain.id;
2065
+ const currentTxHash = bridgeState.txHash;
2066
+ setAmount("");
2067
+ refetchBalances();
2068
+ if (currentTxHash) {
2069
+ onBridgeSuccessRef.current?.({
2070
+ sourceChainId: currentSourceChainId,
2071
+ destChainId: currentDestChainId,
2072
+ amount: currentAmount,
2073
+ txHash: currentTxHash
2074
+ });
2075
+ }
2076
+ } else if (bridgeState.status === "error" && bridgeState.error) {
2077
+ setError(bridgeState.error.message);
2078
+ }
2079
+ }, [
2080
+ bridgeState.status,
2081
+ bridgeState.txHash,
2082
+ bridgeState.error,
2083
+ refetchBalances,
2084
+ amount,
2085
+ sourceChainConfig.chain.id,
2086
+ destChainConfig.chain.id
2087
+ ]);
2088
+ const isButtonDisabled = !isConnected || needsChainSwitch || !amount || parseFloat(amount) <= 0 || parseFloat(amount) > parseFloat(balanceFormatted) || isConfirming || isApproving || isBridging;
2089
+ const isButtonActuallyDisabled = isButtonDisabled && !needsChainSwitch && isConnected;
2090
+ const getButtonText = (0, import_react3.useCallback)(() => {
2091
+ if (!isConnected) return "Connect Wallet";
2092
+ if (needsChainSwitch) return `Switch to ${sourceChainConfig.chain.name}`;
2093
+ if (bridgeState.status === "loading") return "Preparing Bridge...";
2094
+ if (bridgeState.status === "approving") return "Approving...";
2095
+ if (bridgeState.status === "burning") return "Burning USDC...";
2096
+ if (bridgeState.status === "fetching-attestation") return "Fetching Attestation...";
2097
+ if (bridgeState.status === "minting") return "Minting on Destination...";
2098
+ if (isConfirming || isApproving) {
2099
+ return "Approving...";
2100
+ }
2101
+ if (!amount || parseFloat(amount) <= 0) return "Enter Amount";
2102
+ if (parseFloat(amount) > parseFloat(balanceFormatted)) {
2103
+ return "Insufficient Balance";
2104
+ }
2105
+ if (needsApproval(amount)) return "Approve & Bridge USDC";
2106
+ return "Bridge USDC";
2107
+ }, [
2108
+ isConnected,
2109
+ needsChainSwitch,
2110
+ sourceChainConfig.chain.name,
2111
+ bridgeState.status,
2112
+ isConfirming,
2113
+ isApproving,
2114
+ amount,
2115
+ balanceFormatted,
2116
+ needsApproval
2117
+ ]);
2118
+ const handleButtonClick = (0, import_react3.useCallback)(() => {
2119
+ if (!isConnected) {
2120
+ if (onConnectWallet) {
2121
+ onConnectWallet();
2122
+ } else if (connectors.length > 0) {
2123
+ const injectedConnector = connectors.find(
2124
+ (c) => c.type === "injected"
2125
+ );
2126
+ connect({ connector: injectedConnector || connectors[0] });
2127
+ }
2128
+ return;
2129
+ }
2130
+ if (needsChainSwitch) {
2131
+ handleSwitchChain();
2132
+ return;
2133
+ }
2134
+ handleBridge();
2135
+ }, [
2136
+ isConnected,
2137
+ onConnectWallet,
2138
+ connectors,
2139
+ connect,
2140
+ needsChainSwitch,
2141
+ handleSwitchChain,
2142
+ handleBridge
2143
+ ]);
2144
+ const buttonStyles = (0, import_react3.useMemo)(
2145
+ () => ({
2146
+ width: "100%",
2147
+ padding: "14px",
2148
+ borderRadius: `${theme.borderRadius}px`,
2149
+ fontSize: "14px",
2150
+ fontWeight: 600,
2151
+ border: "none",
2152
+ cursor: isButtonActuallyDisabled ? "not-allowed" : "pointer",
2153
+ transition: "all 0.2s",
2154
+ color: isButtonActuallyDisabled ? theme.mutedTextColor : theme.textColor,
2155
+ background: isButtonActuallyDisabled ? "rgba(255,255,255,0.1)" : `linear-gradient(135deg, ${theme.primaryColor} 0%, ${theme.secondaryColor} 100%)`,
2156
+ boxShadow: isButtonActuallyDisabled ? "none" : `0 4px 14px ${theme.primaryColor}60, inset 0 1px 0 rgba(255,255,255,0.2)`
2157
+ }),
2158
+ [
2159
+ theme.borderRadius,
2160
+ theme.mutedTextColor,
2161
+ theme.textColor,
2162
+ theme.primaryColor,
2163
+ theme.secondaryColor,
2164
+ isButtonActuallyDisabled
2165
+ ]
2166
+ );
2167
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2168
+ "div",
2169
+ {
2170
+ className,
2171
+ role: "region",
2172
+ "aria-label": "USDC Bridge Widget",
2173
+ style: {
2174
+ fontFamily: theme.fontFamily,
2175
+ maxWidth: "480px",
2176
+ width: "100%",
2177
+ borderRadius: `${theme.borderRadius}px`,
2178
+ padding: "16px",
2179
+ background: theme.backgroundColor,
2180
+ border: `1px solid ${theme.borderColor}`,
2181
+ boxShadow: "0 4px 24px rgba(0,0,0,0.3)",
2182
+ ...style
2183
+ },
2184
+ children: [
2185
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2186
+ "div",
2187
+ {
2188
+ style: {
2189
+ display: "flex",
2190
+ alignItems: "flex-end",
2191
+ gap: "12px",
2192
+ marginBottom: "16px"
2193
+ },
2194
+ children: [
2195
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2196
+ ChainSelector,
2197
+ {
2198
+ id: sourceChainId,
2199
+ label: "From",
2200
+ chains,
2201
+ selectedChain: sourceChainConfig,
2202
+ onSelect: setSourceChainConfig,
2203
+ excludeChainId: destChainConfig.chain.id,
2204
+ theme,
2205
+ balances: allBalances,
2206
+ isLoadingBalances: isLoadingAllBalances,
2207
+ disabled: isOperationPending
2208
+ }
2209
+ ),
2210
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SwapButton, { onClick: handleSwapChains, theme, disabled: isOperationPending }),
2211
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2212
+ ChainSelector,
2213
+ {
2214
+ id: destChainId,
2215
+ label: "To",
2216
+ chains,
2217
+ selectedChain: destChainConfig,
2218
+ onSelect: setDestChainConfig,
2219
+ excludeChainId: sourceChainConfig.chain.id,
2220
+ theme,
2221
+ balances: allBalances,
2222
+ isLoadingBalances: isLoadingAllBalances,
2223
+ disabled: isOperationPending
2224
+ }
2225
+ )
2226
+ ]
2227
+ }
2228
+ ),
2229
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { marginBottom: "16px" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2230
+ AmountInput,
2231
+ {
2232
+ id: amountId,
2233
+ value: amount,
2234
+ onChange: setAmount,
2235
+ balance: balanceFormatted,
2236
+ onMaxClick: handleMaxClick,
2237
+ theme,
2238
+ disabled: isOperationPending
2239
+ }
2240
+ ) }),
2241
+ configError && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
2242
+ "div",
2243
+ {
2244
+ role: "alert",
2245
+ style: {
2246
+ fontSize: "12px",
2247
+ color: theme.errorColor,
2248
+ background: `${theme.errorColor}15`,
2249
+ padding: "8px 12px",
2250
+ borderRadius: `${theme.borderRadius}px`,
2251
+ marginBottom: "16px"
2252
+ },
2253
+ children: [
2254
+ "Configuration Error: ",
2255
+ configError
2256
+ ]
2257
+ }
2258
+ ),
2259
+ error && !configError && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2260
+ "div",
2261
+ {
2262
+ role: "alert",
2263
+ style: {
2264
+ fontSize: "12px",
2265
+ color: theme.errorColor,
2266
+ background: `${theme.errorColor}15`,
2267
+ padding: "8px 12px",
2268
+ borderRadius: `${theme.borderRadius}px`,
2269
+ marginBottom: "16px"
2270
+ },
2271
+ children: error
2272
+ }
2273
+ ),
2274
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2275
+ "button",
2276
+ {
2277
+ onClick: handleButtonClick,
2278
+ disabled: isButtonActuallyDisabled,
2279
+ "aria-busy": isConfirming || isApproving || isBridging,
2280
+ style: buttonStyles,
2281
+ children: getButtonText()
2282
+ }
2283
+ )
2284
+ ]
2285
+ }
2286
+ );
2287
+ }
2288
+ // Annotate the CommonJS export names for ESM import in node:
2289
+ 0 && (module.exports = {
2290
+ BridgeWidget,
2291
+ CHAIN_ICONS,
2292
+ CheckIcon,
2293
+ ChevronDownIcon,
2294
+ DEFAULT_CHAIN_CONFIGS,
2295
+ DEFAULT_LOCALE,
2296
+ ErrorIcon,
2297
+ ExternalLinkIcon,
2298
+ MAX_USDC_AMOUNT,
2299
+ MIN_USDC_AMOUNT,
2300
+ SpinnerIcon,
2301
+ SwapIcon,
2302
+ TESTNET_CHAIN_CONFIGS,
2303
+ THEME_COLORS,
2304
+ THEME_FONTS,
2305
+ THEME_SIZING,
2306
+ TOKEN_MESSENGER_ADDRESSES,
2307
+ TOKEN_MESSENGER_V1_ADDRESSES,
2308
+ TOKEN_MESSENGER_V2_ADDRESS,
2309
+ USDC_ADDRESSES,
2310
+ USDC_BRAND_COLOR,
2311
+ USDC_DECIMALS,
2312
+ WalletIcon,
2313
+ arbitrum,
2314
+ arbitrumSepolia,
2315
+ avalanche,
2316
+ avalancheFuji,
2317
+ base,
2318
+ baseSepolia,
2319
+ codex,
2320
+ createChainConfig,
2321
+ createTestnetChainConfig,
2322
+ defaultTheme,
2323
+ formatNumber,
2324
+ getChainName,
2325
+ getErrorMessage,
2326
+ hyperEvm,
2327
+ ink,
2328
+ isValidPositiveAmount,
2329
+ linea,
2330
+ mainnet,
2331
+ mergeTheme,
2332
+ monad,
2333
+ optimism,
2334
+ optimismSepolia,
2335
+ parseUSDCAmount,
2336
+ plume,
2337
+ polygon,
2338
+ polygonAmoy,
2339
+ sei,
2340
+ sepolia,
2341
+ sonic,
2342
+ themePresets,
2343
+ unichain,
2344
+ useAllUSDCBalances,
2345
+ useBridge,
2346
+ useBridgeEstimate,
2347
+ useBridgeQuote,
2348
+ useFormatNumber,
2349
+ useUSDCAllowance,
2350
+ useUSDCBalance,
2351
+ validateAmountInput,
2352
+ validateChainConfig,
2353
+ validateChainConfigs,
2354
+ worldchain,
2355
+ xdc
2356
+ });