@hongming-wang/usdc-bridge-widget 0.1.0 → 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 +97 -63
- package/dist/index.mjs +97 -63
- package/package.json +10 -10
- package/src/BridgeWidget.tsx +132 -69
- 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,16 +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
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1998
|
+
(0, import_react3.useEffect)(() => {
|
|
1999
|
+
if (address) {
|
|
2000
|
+
refetchAllBalances();
|
|
2001
|
+
}
|
|
2002
|
+
}, [address, refetchAllBalances]);
|
|
1975
2003
|
const { bridge: executeBridge, state: bridgeState, reset: resetBridge } = useBridge();
|
|
1976
2004
|
const { isLoading: isConfirming, isSuccess } = (0, import_wagmi3.useWaitForTransactionReceipt)({
|
|
1977
2005
|
hash: txHash
|
|
@@ -1984,11 +2012,11 @@ function BridgeWidget({
|
|
|
1984
2012
|
onBridgeErrorRef.current = onBridgeError;
|
|
1985
2013
|
const needsChainSwitch = isConnected && currentChainId !== sourceChainConfig.chain.id;
|
|
1986
2014
|
const handleSwapChains = (0, import_react3.useCallback)(() => {
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
}, [destChainConfig]);
|
|
2015
|
+
const newSource = destChainConfig;
|
|
2016
|
+
const newDest = sourceChainConfig;
|
|
2017
|
+
setSourceChainConfig(newSource);
|
|
2018
|
+
setDestChainConfig(newDest);
|
|
2019
|
+
}, [destChainConfig, sourceChainConfig]);
|
|
1992
2020
|
const handleMaxClick = (0, import_react3.useCallback)(() => {
|
|
1993
2021
|
setAmount(balanceFormatted);
|
|
1994
2022
|
}, [balanceFormatted]);
|
|
@@ -2000,7 +2028,7 @@ function BridgeWidget({
|
|
|
2000
2028
|
}
|
|
2001
2029
|
}, [switchChainAsync, sourceChainConfig.chain.id]);
|
|
2002
2030
|
const handleBridge = (0, import_react3.useCallback)(async () => {
|
|
2003
|
-
if (!address || !amount ||
|
|
2031
|
+
if (!address || !amount || parsedAmount <= 0) return;
|
|
2004
2032
|
setError(null);
|
|
2005
2033
|
resetBridge();
|
|
2006
2034
|
try {
|
|
@@ -2034,6 +2062,7 @@ function BridgeWidget({
|
|
|
2034
2062
|
}, [
|
|
2035
2063
|
address,
|
|
2036
2064
|
amount,
|
|
2065
|
+
parsedAmount,
|
|
2037
2066
|
needsApproval,
|
|
2038
2067
|
approve,
|
|
2039
2068
|
executeBridge,
|
|
@@ -2064,7 +2093,7 @@ function BridgeWidget({
|
|
|
2064
2093
|
const currentDestChainId = destChainConfig.chain.id;
|
|
2065
2094
|
const currentTxHash = bridgeState.txHash;
|
|
2066
2095
|
setAmount("");
|
|
2067
|
-
|
|
2096
|
+
refetchAllBalances();
|
|
2068
2097
|
if (currentTxHash) {
|
|
2069
2098
|
onBridgeSuccessRef.current?.({
|
|
2070
2099
|
sourceChainId: currentSourceChainId,
|
|
@@ -2080,12 +2109,12 @@ function BridgeWidget({
|
|
|
2080
2109
|
bridgeState.status,
|
|
2081
2110
|
bridgeState.txHash,
|
|
2082
2111
|
bridgeState.error,
|
|
2083
|
-
|
|
2112
|
+
refetchAllBalances,
|
|
2084
2113
|
amount,
|
|
2085
2114
|
sourceChainConfig.chain.id,
|
|
2086
2115
|
destChainConfig.chain.id
|
|
2087
2116
|
]);
|
|
2088
|
-
const isButtonDisabled = !isConnected || needsChainSwitch || !amount ||
|
|
2117
|
+
const isButtonDisabled = !isConnected || needsChainSwitch || !amount || parsedAmount <= 0 || parsedAmount > parsedBalance || isConfirming || isApproving || isBridging;
|
|
2089
2118
|
const isButtonActuallyDisabled = isButtonDisabled && !needsChainSwitch && isConnected;
|
|
2090
2119
|
const getButtonText = (0, import_react3.useCallback)(() => {
|
|
2091
2120
|
if (!isConnected) return "Connect Wallet";
|
|
@@ -2098,8 +2127,8 @@ function BridgeWidget({
|
|
|
2098
2127
|
if (isConfirming || isApproving) {
|
|
2099
2128
|
return "Approving...";
|
|
2100
2129
|
}
|
|
2101
|
-
if (!amount ||
|
|
2102
|
-
if (
|
|
2130
|
+
if (!amount || parsedAmount <= 0) return "Enter Amount";
|
|
2131
|
+
if (parsedAmount > parsedBalance) {
|
|
2103
2132
|
return "Insufficient Balance";
|
|
2104
2133
|
}
|
|
2105
2134
|
if (needsApproval(amount)) return "Approve & Bridge USDC";
|
|
@@ -2112,7 +2141,8 @@ function BridgeWidget({
|
|
|
2112
2141
|
isConfirming,
|
|
2113
2142
|
isApproving,
|
|
2114
2143
|
amount,
|
|
2115
|
-
|
|
2144
|
+
parsedAmount,
|
|
2145
|
+
parsedBalance,
|
|
2116
2146
|
needsApproval
|
|
2117
2147
|
]);
|
|
2118
2148
|
const handleButtonClick = (0, import_react3.useCallback)(() => {
|
|
@@ -2152,7 +2182,7 @@ function BridgeWidget({
|
|
|
2152
2182
|
cursor: isButtonActuallyDisabled ? "not-allowed" : "pointer",
|
|
2153
2183
|
transition: "all 0.2s",
|
|
2154
2184
|
color: isButtonActuallyDisabled ? theme.mutedTextColor : theme.textColor,
|
|
2155
|
-
background: isButtonActuallyDisabled ?
|
|
2185
|
+
background: isButtonActuallyDisabled ? DISABLED_BUTTON_BACKGROUND : `linear-gradient(135deg, ${theme.primaryColor} 0%, ${theme.secondaryColor} 100%)`,
|
|
2156
2186
|
boxShadow: isButtonActuallyDisabled ? "none" : `0 4px 14px ${theme.primaryColor}60, inset 0 1px 0 rgba(255,255,255,0.2)`
|
|
2157
2187
|
}),
|
|
2158
2188
|
[
|
|
@@ -2174,11 +2204,11 @@ function BridgeWidget({
|
|
|
2174
2204
|
fontFamily: theme.fontFamily,
|
|
2175
2205
|
maxWidth: "480px",
|
|
2176
2206
|
width: "100%",
|
|
2177
|
-
borderRadius: `${theme.borderRadius}px`,
|
|
2178
2207
|
padding: "16px",
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2208
|
+
...getBorderlessStyles(borderless, theme, {
|
|
2209
|
+
includeBoxShadow: true,
|
|
2210
|
+
useBackgroundColor: true
|
|
2211
|
+
}),
|
|
2182
2212
|
...style
|
|
2183
2213
|
},
|
|
2184
2214
|
children: [
|
|
@@ -2202,9 +2232,10 @@ function BridgeWidget({
|
|
|
2202
2232
|
onSelect: setSourceChainConfig,
|
|
2203
2233
|
excludeChainId: destChainConfig.chain.id,
|
|
2204
2234
|
theme,
|
|
2205
|
-
balances: allBalances,
|
|
2206
|
-
isLoadingBalances: isLoadingAllBalances,
|
|
2207
|
-
disabled: isOperationPending
|
|
2235
|
+
balances: isConnected ? allBalances : void 0,
|
|
2236
|
+
isLoadingBalances: isConnected && isLoadingAllBalances,
|
|
2237
|
+
disabled: isOperationPending,
|
|
2238
|
+
borderless
|
|
2208
2239
|
}
|
|
2209
2240
|
),
|
|
2210
2241
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SwapButton, { onClick: handleSwapChains, theme, disabled: isOperationPending }),
|
|
@@ -2218,9 +2249,10 @@ function BridgeWidget({
|
|
|
2218
2249
|
onSelect: setDestChainConfig,
|
|
2219
2250
|
excludeChainId: sourceChainConfig.chain.id,
|
|
2220
2251
|
theme,
|
|
2221
|
-
balances: allBalances,
|
|
2222
|
-
isLoadingBalances: isLoadingAllBalances,
|
|
2223
|
-
disabled: isOperationPending
|
|
2252
|
+
balances: isConnected ? allBalances : void 0,
|
|
2253
|
+
isLoadingBalances: isConnected && isLoadingAllBalances,
|
|
2254
|
+
disabled: isOperationPending,
|
|
2255
|
+
borderless
|
|
2224
2256
|
}
|
|
2225
2257
|
)
|
|
2226
2258
|
]
|
|
@@ -2235,7 +2267,9 @@ function BridgeWidget({
|
|
|
2235
2267
|
balance: balanceFormatted,
|
|
2236
2268
|
onMaxClick: handleMaxClick,
|
|
2237
2269
|
theme,
|
|
2238
|
-
disabled: isOperationPending
|
|
2270
|
+
disabled: isOperationPending,
|
|
2271
|
+
showBalance: isConnected,
|
|
2272
|
+
borderless
|
|
2239
2273
|
}
|
|
2240
2274
|
) }),
|
|
2241
2275
|
configError && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|