@hongming-wang/usdc-bridge-widget 0.1.1 → 0.2.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/README.md +17 -0
- package/dist/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +93 -65
- package/dist/index.mjs +93 -65
- package/package.json +10 -10
- package/src/BridgeWidget.tsx +127 -72
- package/src/__tests__/BridgeWidget.test.tsx +29 -0
- package/src/__tests__/hooks.test.ts +20 -1
- package/src/hooks.ts +12 -3
- package/src/types.ts +2 -0
package/README.md
CHANGED
|
@@ -88,6 +88,7 @@ function App() {
|
|
|
88
88
|
| `onBridgeError` | `function` | - | Called on bridge error |
|
|
89
89
|
| `onConnectWallet` | `function` | - | Called when "Connect Wallet" clicked |
|
|
90
90
|
| `theme` | `BridgeWidgetTheme` | Default theme | Custom theme overrides |
|
|
91
|
+
| `borderless` | `boolean` | `false` | Remove borders/shadows for seamless integration |
|
|
91
92
|
| `className` | `string` | - | Custom CSS class |
|
|
92
93
|
| `style` | `CSSProperties` | - | Custom inline styles |
|
|
93
94
|
|
|
@@ -169,6 +170,22 @@ const customChainConfig = {
|
|
|
169
170
|
/>
|
|
170
171
|
```
|
|
171
172
|
|
|
173
|
+
## Borderless Mode
|
|
174
|
+
|
|
175
|
+
Use the `borderless` prop for seamless integration into your existing UI. This removes all borders, shadows, and backgrounds from the widget container and its child components:
|
|
176
|
+
|
|
177
|
+
```tsx
|
|
178
|
+
<BridgeWidget borderless />
|
|
179
|
+
|
|
180
|
+
{/* Or combine with custom styling */}
|
|
181
|
+
<div className="my-custom-container">
|
|
182
|
+
<BridgeWidget
|
|
183
|
+
borderless
|
|
184
|
+
style={{ padding: 0 }}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
```
|
|
188
|
+
|
|
172
189
|
## Callbacks
|
|
173
190
|
|
|
174
191
|
```tsx
|
package/dist/index.d.mts
CHANGED
|
@@ -126,6 +126,8 @@ interface BridgeWidgetProps {
|
|
|
126
126
|
onConnectWallet?: () => void;
|
|
127
127
|
/** Custom theme overrides to customize the widget appearance */
|
|
128
128
|
theme?: BridgeWidgetTheme;
|
|
129
|
+
/** Remove all borders from the widget for seamless integration */
|
|
130
|
+
borderless?: boolean;
|
|
129
131
|
/** Custom CSS class name to apply to the widget container */
|
|
130
132
|
className?: string;
|
|
131
133
|
/** Custom inline styles to apply to the widget container */
|
|
@@ -160,7 +162,7 @@ interface BridgeResult {
|
|
|
160
162
|
error?: string;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
declare function BridgeWidget({ chains, defaultSourceChainId, defaultDestinationChainId, onBridgeStart, onBridgeSuccess, onBridgeError, onConnectWallet, theme: themeOverrides, className, style, }: BridgeWidgetProps): react_jsx_runtime.JSX.Element;
|
|
165
|
+
declare function BridgeWidget({ chains, defaultSourceChainId, defaultDestinationChainId, onBridgeStart, onBridgeSuccess, onBridgeError, onConnectWallet, theme: themeOverrides, borderless, className, style, }: BridgeWidgetProps): react_jsx_runtime.JSX.Element;
|
|
164
166
|
|
|
165
167
|
/**
|
|
166
168
|
* Hook to get USDC balance for a specific chain
|
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,8 @@ interface BridgeWidgetProps {
|
|
|
126
126
|
onConnectWallet?: () => void;
|
|
127
127
|
/** Custom theme overrides to customize the widget appearance */
|
|
128
128
|
theme?: BridgeWidgetTheme;
|
|
129
|
+
/** Remove all borders from the widget for seamless integration */
|
|
130
|
+
borderless?: boolean;
|
|
129
131
|
/** Custom CSS class name to apply to the widget container */
|
|
130
132
|
className?: string;
|
|
131
133
|
/** Custom inline styles to apply to the widget container */
|
|
@@ -160,7 +162,7 @@ interface BridgeResult {
|
|
|
160
162
|
error?: string;
|
|
161
163
|
}
|
|
162
164
|
|
|
163
|
-
declare function BridgeWidget({ chains, defaultSourceChainId, defaultDestinationChainId, onBridgeStart, onBridgeSuccess, onBridgeError, onConnectWallet, theme: themeOverrides, className, style, }: BridgeWidgetProps): react_jsx_runtime.JSX.Element;
|
|
165
|
+
declare function BridgeWidget({ chains, defaultSourceChainId, defaultDestinationChainId, onBridgeStart, onBridgeSuccess, onBridgeError, onConnectWallet, theme: themeOverrides, borderless, className, style, }: BridgeWidgetProps): react_jsx_runtime.JSX.Element;
|
|
164
166
|
|
|
165
167
|
/**
|
|
166
168
|
* Hook to get USDC balance for a specific chain
|
package/dist/index.js
CHANGED
|
@@ -621,7 +621,7 @@ function useUSDCBalance(chainConfig) {
|
|
|
621
621
|
const { address } = (0, import_wagmi2.useAccount)();
|
|
622
622
|
const {
|
|
623
623
|
data: balance,
|
|
624
|
-
isLoading,
|
|
624
|
+
isLoading: queryLoading,
|
|
625
625
|
refetch
|
|
626
626
|
} = (0, import_wagmi2.useReadContract)({
|
|
627
627
|
address: chainConfig?.usdcAddress,
|
|
@@ -632,6 +632,7 @@ function useUSDCBalance(chainConfig) {
|
|
|
632
632
|
enabled: !!address && !!chainConfig?.usdcAddress
|
|
633
633
|
}
|
|
634
634
|
});
|
|
635
|
+
const isLoading = !!address && queryLoading;
|
|
635
636
|
return {
|
|
636
637
|
balance: balance ?? 0n,
|
|
637
638
|
balanceFormatted: balance ? (0, import_viem2.formatUnits)(balance, USDC_DECIMALS) : "0",
|
|
@@ -653,7 +654,7 @@ function useAllUSDCBalances(chainConfigs) {
|
|
|
653
654
|
}, [address, chainConfigs]);
|
|
654
655
|
const {
|
|
655
656
|
data: results,
|
|
656
|
-
isLoading,
|
|
657
|
+
isLoading: queryLoading,
|
|
657
658
|
refetch
|
|
658
659
|
} = (0, import_wagmi2.useReadContracts)({
|
|
659
660
|
contracts,
|
|
@@ -661,6 +662,7 @@ function useAllUSDCBalances(chainConfigs) {
|
|
|
661
662
|
enabled: !!address && contracts.length > 0
|
|
662
663
|
}
|
|
663
664
|
});
|
|
665
|
+
const isLoading = !!address && queryLoading;
|
|
664
666
|
const balances = (0, import_react2.useMemo)(() => {
|
|
665
667
|
const balanceMap = {};
|
|
666
668
|
if (!results) return balanceMap;
|
|
@@ -691,7 +693,7 @@ function useUSDCAllowance(chainConfig, spenderAddress) {
|
|
|
691
693
|
const effectiveSpender = spenderAddress || chainConfig?.tokenMessengerAddress;
|
|
692
694
|
const {
|
|
693
695
|
data: allowance,
|
|
694
|
-
isLoading,
|
|
696
|
+
isLoading: queryLoading,
|
|
695
697
|
refetch
|
|
696
698
|
} = (0, import_wagmi2.useReadContract)({
|
|
697
699
|
address: chainConfig?.usdcAddress,
|
|
@@ -702,6 +704,7 @@ function useUSDCAllowance(chainConfig, spenderAddress) {
|
|
|
702
704
|
enabled: !!address && !!chainConfig?.usdcAddress && !!effectiveSpender
|
|
703
705
|
}
|
|
704
706
|
});
|
|
707
|
+
const isLoading = !!address && queryLoading;
|
|
705
708
|
const { writeContractAsync, isPending: isApproving } = (0, import_wagmi2.useWriteContract)();
|
|
706
709
|
const [approvalTxHash, setApprovalTxHash] = (0, import_react2.useState)();
|
|
707
710
|
const [approvalError, setApprovalError] = (0, import_react2.useState)(null);
|
|
@@ -1269,12 +1272,40 @@ function WalletIcon({
|
|
|
1269
1272
|
|
|
1270
1273
|
// src/BridgeWidget.tsx
|
|
1271
1274
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1275
|
+
var TYPE_AHEAD_RESET_MS = 1e3;
|
|
1276
|
+
var DROPDOWN_MAX_HEIGHT = 300;
|
|
1277
|
+
var BOX_SHADOW_COLOR = "rgba(0,0,0,0.3)";
|
|
1278
|
+
var DISABLED_BUTTON_BACKGROUND = "rgba(255,255,255,0.1)";
|
|
1279
|
+
function getBorderlessStyles(borderless, theme, options) {
|
|
1280
|
+
const bgColor = options?.useBackgroundColor ? theme.backgroundColor : theme.cardBackgroundColor;
|
|
1281
|
+
return {
|
|
1282
|
+
borderRadius: borderless ? 0 : `${theme.borderRadius}px`,
|
|
1283
|
+
background: borderless ? "transparent" : bgColor,
|
|
1284
|
+
border: borderless ? "none" : `1px solid ${theme.borderColor}`,
|
|
1285
|
+
...options?.includeBoxShadow && {
|
|
1286
|
+
boxShadow: borderless ? "none" : `0 4px 24px ${BOX_SHADOW_COLOR}`
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
var SPINNER_KEYFRAMES = `@keyframes cc-balance-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }`;
|
|
1291
|
+
var KEYFRAMES_ATTR = "data-cc-spinner-keyframes";
|
|
1292
|
+
function injectSpinnerKeyframes() {
|
|
1293
|
+
if (typeof document === "undefined") return;
|
|
1294
|
+
if (document.querySelector(`style[${KEYFRAMES_ATTR}]`)) return;
|
|
1295
|
+
const style = document.createElement("style");
|
|
1296
|
+
style.setAttribute(KEYFRAMES_ATTR, "true");
|
|
1297
|
+
style.textContent = SPINNER_KEYFRAMES;
|
|
1298
|
+
document.head.appendChild(style);
|
|
1299
|
+
}
|
|
1272
1300
|
function ChainIcon({
|
|
1273
1301
|
chainConfig,
|
|
1274
1302
|
theme,
|
|
1275
1303
|
size = 24
|
|
1276
1304
|
}) {
|
|
1277
1305
|
const [hasError, setHasError] = (0, import_react3.useState)(false);
|
|
1306
|
+
(0, import_react3.useEffect)(() => {
|
|
1307
|
+
setHasError(false);
|
|
1308
|
+
}, [chainConfig.iconUrl]);
|
|
1278
1309
|
if (!chainConfig.iconUrl || hasError) {
|
|
1279
1310
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1280
1311
|
"div",
|
|
@@ -1308,6 +1339,9 @@ function ChainIcon({
|
|
|
1308
1339
|
);
|
|
1309
1340
|
}
|
|
1310
1341
|
function BalanceSpinner({ size = 12 }) {
|
|
1342
|
+
(0, import_react3.useEffect)(() => {
|
|
1343
|
+
injectSpinnerKeyframes();
|
|
1344
|
+
}, []);
|
|
1311
1345
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1312
1346
|
"svg",
|
|
1313
1347
|
{
|
|
@@ -1323,7 +1357,6 @@ function BalanceSpinner({ size = 12 }) {
|
|
|
1323
1357
|
},
|
|
1324
1358
|
"aria-hidden": "true",
|
|
1325
1359
|
children: [
|
|
1326
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes cc-balance-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
|
|
1327
1360
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "12", cy: "12", r: "10", strokeOpacity: "0.25" }),
|
|
1328
1361
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: "M12 2a10 10 0 0 1 10 10", strokeLinecap: "round" })
|
|
1329
1362
|
]
|
|
@@ -1340,7 +1373,8 @@ function ChainSelector({
|
|
|
1340
1373
|
id,
|
|
1341
1374
|
balances,
|
|
1342
1375
|
isLoadingBalances,
|
|
1343
|
-
disabled
|
|
1376
|
+
disabled,
|
|
1377
|
+
borderless
|
|
1344
1378
|
}) {
|
|
1345
1379
|
const [isOpen, setIsOpen] = (0, import_react3.useState)(false);
|
|
1346
1380
|
const [focusedIndex, setFocusedIndex] = (0, import_react3.useState)(-1);
|
|
@@ -1348,8 +1382,9 @@ function ChainSelector({
|
|
|
1348
1382
|
const typeAheadTimeoutRef = (0, import_react3.useRef)(null);
|
|
1349
1383
|
const buttonRef = (0, import_react3.useRef)(null);
|
|
1350
1384
|
const listRef = (0, import_react3.useRef)(null);
|
|
1351
|
-
const availableChains =
|
|
1352
|
-
(c) => c.chain.id !== excludeChainId
|
|
1385
|
+
const availableChains = (0, import_react3.useMemo)(
|
|
1386
|
+
() => chains.filter((c) => c.chain.id !== excludeChainId),
|
|
1387
|
+
[chains, excludeChainId]
|
|
1353
1388
|
);
|
|
1354
1389
|
(0, import_react3.useEffect)(() => {
|
|
1355
1390
|
return () => {
|
|
@@ -1412,7 +1447,7 @@ function ChainSelector({
|
|
|
1412
1447
|
}
|
|
1413
1448
|
typeAheadTimeoutRef.current = setTimeout(() => {
|
|
1414
1449
|
setTypeAhead("");
|
|
1415
|
-
},
|
|
1450
|
+
}, TYPE_AHEAD_RESET_MS);
|
|
1416
1451
|
const matchIndex = availableChains.findIndex(
|
|
1417
1452
|
(chain) => chain.chain.name.toLowerCase().startsWith(newTypeAhead)
|
|
1418
1453
|
);
|
|
@@ -1489,9 +1524,7 @@ function ChainSelector({
|
|
|
1489
1524
|
alignItems: "center",
|
|
1490
1525
|
justifyContent: "space-between",
|
|
1491
1526
|
padding: "10px 12px",
|
|
1492
|
-
|
|
1493
|
-
background: theme.cardBackgroundColor,
|
|
1494
|
-
border: `1px solid ${theme.borderColor}`,
|
|
1527
|
+
...getBorderlessStyles(borderless, theme),
|
|
1495
1528
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
1496
1529
|
opacity: disabled ? 0.6 : 1,
|
|
1497
1530
|
transition: "all 0.2s"
|
|
@@ -1526,7 +1559,7 @@ function ChainSelector({
|
|
|
1526
1559
|
" Loading..."
|
|
1527
1560
|
]
|
|
1528
1561
|
}
|
|
1529
|
-
) : selectedBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1562
|
+
) : balances && selectedBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1530
1563
|
"span",
|
|
1531
1564
|
{
|
|
1532
1565
|
style: {
|
|
@@ -1584,11 +1617,11 @@ function ChainSelector({
|
|
|
1584
1617
|
width: "100%",
|
|
1585
1618
|
marginTop: "8px",
|
|
1586
1619
|
borderRadius: `${theme.borderRadius}px`,
|
|
1587
|
-
boxShadow:
|
|
1620
|
+
boxShadow: `0 10px 40px ${BOX_SHADOW_COLOR}`,
|
|
1588
1621
|
background: theme.cardBackgroundColor,
|
|
1589
1622
|
backdropFilter: "blur(10px)",
|
|
1590
1623
|
border: `1px solid ${theme.borderColor}`,
|
|
1591
|
-
maxHeight:
|
|
1624
|
+
maxHeight: `${DROPDOWN_MAX_HEIGHT}px`,
|
|
1592
1625
|
overflowY: "auto",
|
|
1593
1626
|
overflowX: "hidden",
|
|
1594
1627
|
padding: 0,
|
|
@@ -1600,6 +1633,7 @@ function ChainSelector({
|
|
|
1600
1633
|
const chainBalance = balances?.[chainConfig.chain.id];
|
|
1601
1634
|
const isFocused = index === focusedIndex;
|
|
1602
1635
|
const isSelected = chainConfig.chain.id === selectedChain.chain.id;
|
|
1636
|
+
const hasPositiveBalance = chainBalance ? parseFloat(chainBalance.formatted) > 0 : false;
|
|
1603
1637
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1604
1638
|
"li",
|
|
1605
1639
|
{
|
|
@@ -1649,28 +1683,19 @@ function ChainSelector({
|
|
|
1649
1683
|
},
|
|
1650
1684
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BalanceSpinner, { size: 10 })
|
|
1651
1685
|
}
|
|
1652
|
-
) : chainBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1686
|
+
) : balances && chainBalance ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1653
1687
|
"span",
|
|
1654
1688
|
{
|
|
1655
1689
|
style: {
|
|
1656
1690
|
fontSize: "10px",
|
|
1657
|
-
color:
|
|
1691
|
+
color: hasPositiveBalance ? theme.successColor : theme.mutedTextColor
|
|
1658
1692
|
},
|
|
1659
1693
|
children: [
|
|
1660
1694
|
formatNumber(chainBalance.formatted, 2),
|
|
1661
1695
|
" USDC"
|
|
1662
1696
|
]
|
|
1663
1697
|
}
|
|
1664
|
-
) :
|
|
1665
|
-
"span",
|
|
1666
|
-
{
|
|
1667
|
-
style: {
|
|
1668
|
-
fontSize: "10px",
|
|
1669
|
-
color: theme.mutedTextColor
|
|
1670
|
-
},
|
|
1671
|
-
children: "0.00 USDC"
|
|
1672
|
-
}
|
|
1673
|
-
)
|
|
1698
|
+
) : null
|
|
1674
1699
|
] })
|
|
1675
1700
|
]
|
|
1676
1701
|
},
|
|
@@ -1722,7 +1747,9 @@ function AmountInput({
|
|
|
1722
1747
|
onMaxClick,
|
|
1723
1748
|
theme,
|
|
1724
1749
|
id,
|
|
1725
|
-
disabled
|
|
1750
|
+
disabled,
|
|
1751
|
+
showBalance = true,
|
|
1752
|
+
borderless
|
|
1726
1753
|
}) {
|
|
1727
1754
|
const inputId = `${id}-input`;
|
|
1728
1755
|
const labelId = `${id}-label`;
|
|
@@ -1768,7 +1795,7 @@ function AmountInput({
|
|
|
1768
1795
|
children: "Amount"
|
|
1769
1796
|
}
|
|
1770
1797
|
),
|
|
1771
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1798
|
+
showBalance && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1772
1799
|
"span",
|
|
1773
1800
|
{
|
|
1774
1801
|
style: { fontSize: "10px", color: theme.mutedTextColor },
|
|
@@ -1792,10 +1819,8 @@ function AmountInput({
|
|
|
1792
1819
|
style: {
|
|
1793
1820
|
display: "flex",
|
|
1794
1821
|
alignItems: "center",
|
|
1795
|
-
borderRadius: `${theme.borderRadius}px`,
|
|
1796
1822
|
overflow: "hidden",
|
|
1797
|
-
|
|
1798
|
-
border: `1px solid ${theme.borderColor}`,
|
|
1823
|
+
...getBorderlessStyles(borderless, theme),
|
|
1799
1824
|
opacity: disabled ? 0.6 : 1
|
|
1800
1825
|
},
|
|
1801
1826
|
children: [
|
|
@@ -1922,6 +1947,7 @@ function BridgeWidget({
|
|
|
1922
1947
|
onBridgeError,
|
|
1923
1948
|
onConnectWallet,
|
|
1924
1949
|
theme: themeOverrides,
|
|
1950
|
+
borderless = false,
|
|
1925
1951
|
className,
|
|
1926
1952
|
style
|
|
1927
1953
|
}) {
|
|
@@ -1935,7 +1961,6 @@ function BridgeWidget({
|
|
|
1935
1961
|
const validation = validateChainConfigs(chains);
|
|
1936
1962
|
if (!validation.isValid) {
|
|
1937
1963
|
const errorMsg = validation.errors.join("; ");
|
|
1938
|
-
console.error("[BridgeWidget] Invalid chain configuration:", errorMsg);
|
|
1939
1964
|
setConfigError(errorMsg);
|
|
1940
1965
|
} else {
|
|
1941
1966
|
setConfigError(null);
|
|
@@ -1962,22 +1987,19 @@ function BridgeWidget({
|
|
|
1962
1987
|
const [txHash, setTxHash] = (0, import_react3.useState)();
|
|
1963
1988
|
const [error, setError] = (0, import_react3.useState)(null);
|
|
1964
1989
|
const { balances: allBalances, isLoading: isLoadingAllBalances, refetch: refetchAllBalances } = useAllUSDCBalances(chains);
|
|
1965
|
-
const
|
|
1966
|
-
sourceChainConfig
|
|
1967
|
-
);
|
|
1990
|
+
const balanceFormatted = (0, import_react3.useMemo)(() => {
|
|
1991
|
+
return allBalances[sourceChainConfig.chain.id]?.formatted ?? "0";
|
|
1992
|
+
}, [allBalances, sourceChainConfig.chain.id]);
|
|
1993
|
+
const parsedBalance = (0, import_react3.useMemo)(() => parseFloat(balanceFormatted), [balanceFormatted]);
|
|
1994
|
+
const parsedAmount = (0, import_react3.useMemo)(() => parseFloat(amount) || 0, [amount]);
|
|
1968
1995
|
const { needsApproval, approve, isApproving } = useUSDCAllowance(
|
|
1969
1996
|
sourceChainConfig
|
|
1970
1997
|
);
|
|
1971
|
-
const refetchBalances = (0, import_react3.useCallback)(() => {
|
|
1972
|
-
refetchBalance();
|
|
1973
|
-
refetchAllBalances();
|
|
1974
|
-
}, [refetchBalance, refetchAllBalances]);
|
|
1975
1998
|
(0, import_react3.useEffect)(() => {
|
|
1976
1999
|
if (address) {
|
|
1977
2000
|
refetchAllBalances();
|
|
1978
|
-
refetchBalance();
|
|
1979
2001
|
}
|
|
1980
|
-
}, [address, refetchAllBalances
|
|
2002
|
+
}, [address, refetchAllBalances]);
|
|
1981
2003
|
const { bridge: executeBridge, state: bridgeState, reset: resetBridge } = useBridge();
|
|
1982
2004
|
const { isLoading: isConfirming, isSuccess } = (0, import_wagmi3.useWaitForTransactionReceipt)({
|
|
1983
2005
|
hash: txHash
|
|
@@ -1990,11 +2012,11 @@ function BridgeWidget({
|
|
|
1990
2012
|
onBridgeErrorRef.current = onBridgeError;
|
|
1991
2013
|
const needsChainSwitch = isConnected && currentChainId !== sourceChainConfig.chain.id;
|
|
1992
2014
|
const handleSwapChains = (0, import_react3.useCallback)(() => {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
}, [destChainConfig]);
|
|
2015
|
+
const newSource = destChainConfig;
|
|
2016
|
+
const newDest = sourceChainConfig;
|
|
2017
|
+
setSourceChainConfig(newSource);
|
|
2018
|
+
setDestChainConfig(newDest);
|
|
2019
|
+
}, [destChainConfig, sourceChainConfig]);
|
|
1998
2020
|
const handleMaxClick = (0, import_react3.useCallback)(() => {
|
|
1999
2021
|
setAmount(balanceFormatted);
|
|
2000
2022
|
}, [balanceFormatted]);
|
|
@@ -2006,7 +2028,7 @@ function BridgeWidget({
|
|
|
2006
2028
|
}
|
|
2007
2029
|
}, [switchChainAsync, sourceChainConfig.chain.id]);
|
|
2008
2030
|
const handleBridge = (0, import_react3.useCallback)(async () => {
|
|
2009
|
-
if (!address || !amount ||
|
|
2031
|
+
if (!address || !amount || parsedAmount <= 0) return;
|
|
2010
2032
|
setError(null);
|
|
2011
2033
|
resetBridge();
|
|
2012
2034
|
try {
|
|
@@ -2040,6 +2062,7 @@ function BridgeWidget({
|
|
|
2040
2062
|
}, [
|
|
2041
2063
|
address,
|
|
2042
2064
|
amount,
|
|
2065
|
+
parsedAmount,
|
|
2043
2066
|
needsApproval,
|
|
2044
2067
|
approve,
|
|
2045
2068
|
executeBridge,
|
|
@@ -2070,7 +2093,7 @@ function BridgeWidget({
|
|
|
2070
2093
|
const currentDestChainId = destChainConfig.chain.id;
|
|
2071
2094
|
const currentTxHash = bridgeState.txHash;
|
|
2072
2095
|
setAmount("");
|
|
2073
|
-
|
|
2096
|
+
refetchAllBalances();
|
|
2074
2097
|
if (currentTxHash) {
|
|
2075
2098
|
onBridgeSuccessRef.current?.({
|
|
2076
2099
|
sourceChainId: currentSourceChainId,
|
|
@@ -2086,12 +2109,12 @@ function BridgeWidget({
|
|
|
2086
2109
|
bridgeState.status,
|
|
2087
2110
|
bridgeState.txHash,
|
|
2088
2111
|
bridgeState.error,
|
|
2089
|
-
|
|
2112
|
+
refetchAllBalances,
|
|
2090
2113
|
amount,
|
|
2091
2114
|
sourceChainConfig.chain.id,
|
|
2092
2115
|
destChainConfig.chain.id
|
|
2093
2116
|
]);
|
|
2094
|
-
const isButtonDisabled = !isConnected || needsChainSwitch || !amount ||
|
|
2117
|
+
const isButtonDisabled = !isConnected || needsChainSwitch || !amount || parsedAmount <= 0 || parsedAmount > parsedBalance || isConfirming || isApproving || isBridging;
|
|
2095
2118
|
const isButtonActuallyDisabled = isButtonDisabled && !needsChainSwitch && isConnected;
|
|
2096
2119
|
const getButtonText = (0, import_react3.useCallback)(() => {
|
|
2097
2120
|
if (!isConnected) return "Connect Wallet";
|
|
@@ -2104,8 +2127,8 @@ function BridgeWidget({
|
|
|
2104
2127
|
if (isConfirming || isApproving) {
|
|
2105
2128
|
return "Approving...";
|
|
2106
2129
|
}
|
|
2107
|
-
if (!amount ||
|
|
2108
|
-
if (
|
|
2130
|
+
if (!amount || parsedAmount <= 0) return "Enter Amount";
|
|
2131
|
+
if (parsedAmount > parsedBalance) {
|
|
2109
2132
|
return "Insufficient Balance";
|
|
2110
2133
|
}
|
|
2111
2134
|
if (needsApproval(amount)) return "Approve & Bridge USDC";
|
|
@@ -2118,7 +2141,8 @@ function BridgeWidget({
|
|
|
2118
2141
|
isConfirming,
|
|
2119
2142
|
isApproving,
|
|
2120
2143
|
amount,
|
|
2121
|
-
|
|
2144
|
+
parsedAmount,
|
|
2145
|
+
parsedBalance,
|
|
2122
2146
|
needsApproval
|
|
2123
2147
|
]);
|
|
2124
2148
|
const handleButtonClick = (0, import_react3.useCallback)(() => {
|
|
@@ -2158,7 +2182,7 @@ function BridgeWidget({
|
|
|
2158
2182
|
cursor: isButtonActuallyDisabled ? "not-allowed" : "pointer",
|
|
2159
2183
|
transition: "all 0.2s",
|
|
2160
2184
|
color: isButtonActuallyDisabled ? theme.mutedTextColor : theme.textColor,
|
|
2161
|
-
background: isButtonActuallyDisabled ?
|
|
2185
|
+
background: isButtonActuallyDisabled ? DISABLED_BUTTON_BACKGROUND : `linear-gradient(135deg, ${theme.primaryColor} 0%, ${theme.secondaryColor} 100%)`,
|
|
2162
2186
|
boxShadow: isButtonActuallyDisabled ? "none" : `0 4px 14px ${theme.primaryColor}60, inset 0 1px 0 rgba(255,255,255,0.2)`
|
|
2163
2187
|
}),
|
|
2164
2188
|
[
|
|
@@ -2180,11 +2204,11 @@ function BridgeWidget({
|
|
|
2180
2204
|
fontFamily: theme.fontFamily,
|
|
2181
2205
|
maxWidth: "480px",
|
|
2182
2206
|
width: "100%",
|
|
2183
|
-
borderRadius: `${theme.borderRadius}px`,
|
|
2184
2207
|
padding: "16px",
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2208
|
+
...getBorderlessStyles(borderless, theme, {
|
|
2209
|
+
includeBoxShadow: true,
|
|
2210
|
+
useBackgroundColor: true
|
|
2211
|
+
}),
|
|
2188
2212
|
...style
|
|
2189
2213
|
},
|
|
2190
2214
|
children: [
|
|
@@ -2208,9 +2232,10 @@ function BridgeWidget({
|
|
|
2208
2232
|
onSelect: setSourceChainConfig,
|
|
2209
2233
|
excludeChainId: destChainConfig.chain.id,
|
|
2210
2234
|
theme,
|
|
2211
|
-
balances: allBalances,
|
|
2212
|
-
isLoadingBalances: isLoadingAllBalances,
|
|
2213
|
-
disabled: isOperationPending
|
|
2235
|
+
balances: isConnected ? allBalances : void 0,
|
|
2236
|
+
isLoadingBalances: isConnected && isLoadingAllBalances,
|
|
2237
|
+
disabled: isOperationPending,
|
|
2238
|
+
borderless
|
|
2214
2239
|
}
|
|
2215
2240
|
),
|
|
2216
2241
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SwapButton, { onClick: handleSwapChains, theme, disabled: isOperationPending }),
|
|
@@ -2224,9 +2249,10 @@ function BridgeWidget({
|
|
|
2224
2249
|
onSelect: setDestChainConfig,
|
|
2225
2250
|
excludeChainId: sourceChainConfig.chain.id,
|
|
2226
2251
|
theme,
|
|
2227
|
-
balances: allBalances,
|
|
2228
|
-
isLoadingBalances: isLoadingAllBalances,
|
|
2229
|
-
disabled: isOperationPending
|
|
2252
|
+
balances: isConnected ? allBalances : void 0,
|
|
2253
|
+
isLoadingBalances: isConnected && isLoadingAllBalances,
|
|
2254
|
+
disabled: isOperationPending,
|
|
2255
|
+
borderless
|
|
2230
2256
|
}
|
|
2231
2257
|
)
|
|
2232
2258
|
]
|
|
@@ -2241,7 +2267,9 @@ function BridgeWidget({
|
|
|
2241
2267
|
balance: balanceFormatted,
|
|
2242
2268
|
onMaxClick: handleMaxClick,
|
|
2243
2269
|
theme,
|
|
2244
|
-
disabled: isOperationPending
|
|
2270
|
+
disabled: isOperationPending,
|
|
2271
|
+
showBalance: isConnected,
|
|
2272
|
+
borderless
|
|
2245
2273
|
}
|
|
2246
2274
|
) }),
|
|
2247
2275
|
configError && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|