@wheelx-widget/widget 0.1.17 → 0.1.19
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 +25 -17
- package/dist/index.d.mts +0 -2
- package/dist/index.d.ts +0 -2
- package/dist/index.js +115 -37
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +116 -38
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ An open-source, framework-agnostic React widget for cross-chain token swaps and
|
|
|
8
8
|
- 👛 **Built-in Wallet Connection** — Dynamic SDK + Wagmi v2 supporting EVM (MetaMask, OKX, etc.) and Solana (Phantom)
|
|
9
9
|
- 🎨 **Configurable Theme** — 14 color tokens for custom skinning
|
|
10
10
|
- ⚛️ **Framework Agnostic** — Works with any React framework (Next.js, Vite, Create React App, etc.)
|
|
11
|
-
- 📦 **
|
|
11
|
+
- 📦 **ESM + CJS** — Dual-format builds with full TypeScript declarations
|
|
12
12
|
- 🔌 **Configurable Default Tokens** — Set default from/to chains and tokens
|
|
13
13
|
- 📱 **Deep Link Support** — Wallet app launch support in WebView (React Native) — see [Deep Link Guide](./DEEP_LINK_GUIDE.md)
|
|
14
14
|
|
|
@@ -22,6 +22,19 @@ Note: `@chakra-ui/react` and `@emotion/react` are required peer dependencies.
|
|
|
22
22
|
|
|
23
23
|
## Quick Start
|
|
24
24
|
|
|
25
|
+
If you don't have a project yet, scaffold one first:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm create vite@latest my-swap-page -- --template react-ts
|
|
29
|
+
cd my-swap-page
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Then install the widget and required peer dependencies:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm install @wheelx-widget/widget @chakra-ui/react @emotion/react
|
|
36
|
+
```
|
|
37
|
+
|
|
25
38
|
```tsx
|
|
26
39
|
// main.tsx — wrap with ChakraProvider
|
|
27
40
|
import { ChakraProvider, defaultSystem } from '@chakra-ui/react'
|
|
@@ -38,7 +51,7 @@ createRoot(document.getElementById('root')!).render(
|
|
|
38
51
|
```tsx
|
|
39
52
|
// App.tsx
|
|
40
53
|
// Import the CSS for animations (required!)
|
|
41
|
-
import '@wheelx-widget/widget/
|
|
54
|
+
import '@wheelx-widget/widget/styles.css'
|
|
42
55
|
import { WheelXProvider, WheelXWidget } from '@wheelx-widget/widget'
|
|
43
56
|
|
|
44
57
|
function App() {
|
|
@@ -106,18 +119,6 @@ Customize 14 visual tokens:
|
|
|
106
119
|
</WheelXProvider>
|
|
107
120
|
```
|
|
108
121
|
|
|
109
|
-
### API Base URL
|
|
110
|
-
|
|
111
|
-
Override the default WheelX API endpoint. Useful for testing against a staging or development environment.
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
<WheelXProvider config={{
|
|
115
|
-
apiBaseUrl: 'https://dev-api.wheelx.fi' // Default: 'https://api.wheelx.fi'
|
|
116
|
-
}}>
|
|
117
|
-
<WheelXWidget />
|
|
118
|
-
</WheelXProvider>
|
|
119
|
-
```
|
|
120
|
-
|
|
121
122
|
### Fixed Recipient Address (Different Address)
|
|
122
123
|
|
|
123
124
|
Configure fixed recipient addresses for EVM or Solana chains. When set, the widget will:
|
|
@@ -170,9 +171,6 @@ The main trading widget component.
|
|
|
170
171
|
interface WidgetConfig {
|
|
171
172
|
theme?: Partial<WidgetTheme>
|
|
172
173
|
|
|
173
|
-
/** API base URL. Default 'https://api.wheelx.fi' */
|
|
174
|
-
apiBaseUrl?: string
|
|
175
|
-
|
|
176
174
|
from?: {
|
|
177
175
|
chains?: number[]
|
|
178
176
|
defaultChainId?: number
|
|
@@ -242,6 +240,16 @@ In short, three things are required:
|
|
|
242
240
|
2. Configure `<queries>` declaration in Android's `AndroidManifest.xml`
|
|
243
241
|
3. Configure `LSApplicationQueriesSchemes` in iOS's `Info.plist`
|
|
244
242
|
|
|
243
|
+
## Troubleshooting
|
|
244
|
+
|
|
245
|
+
| Problem | Symptom | Solution |
|
|
246
|
+
|----------|---------|----------|
|
|
247
|
+
| **Missing ChakraProvider** | White screen or unstyled component | Wrap your app in `<ChakraProvider value={defaultSystem}>` — the widget depends on Chakra's context |
|
|
248
|
+
| **Forgot to import CSS** | Component works but has no styling/animations | Add `import '@wheelx-widget/widget/styles.css'` in your entry file |
|
|
249
|
+
| **Vite dev server starts slow** | `vite dev` takes 30+ seconds | Normal — Vite pre-bundles large wallet dependencies (wagmi, viem, ethers, etc.). Subsequent starts with cache are faster |
|
|
250
|
+
| **Build output is large** | dist/ folder tens of MB, main bundle ~7.5MB (1.9MB gzipped) | Expected — the widget bundles all wallet SDKs. Use code-splitting or lazy-loading in your host app if needed |
|
|
251
|
+
| **React hooks errors** | "Invalid hook call" or context mismatch | You may have duplicate React instances. Ensure `react` and `react-dom` are peer dependencies in your project, and use a single version (18 or 19) |
|
|
252
|
+
|
|
245
253
|
## License
|
|
246
254
|
|
|
247
255
|
MIT
|
package/dist/index.d.mts
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -43737,12 +43737,9 @@ var WidgetThemeProvider = ({
|
|
|
43737
43737
|
};
|
|
43738
43738
|
|
|
43739
43739
|
// src/api/consts.ts
|
|
43740
|
-
var
|
|
43740
|
+
var BASE_API_URL = "https://api.wheelx.fi";
|
|
43741
43741
|
function getBaseApiUrl() {
|
|
43742
|
-
return
|
|
43743
|
-
}
|
|
43744
|
-
function setBaseApiUrl(url) {
|
|
43745
|
-
_baseApiUrl = url;
|
|
43742
|
+
return BASE_API_URL;
|
|
43746
43743
|
}
|
|
43747
43744
|
async function getChainsAndTokens() {
|
|
43748
43745
|
const url = `${getBaseApiUrl()}/v1/chain-info`;
|
|
@@ -44787,9 +44784,6 @@ function ChainsAndTokensLoader({ children, config }) {
|
|
|
44787
44784
|
var DEFAULT_ENVIRONMENT_ID = "f217660e-2cec-470b-a8f4-ac655b16f802";
|
|
44788
44785
|
var solanaRpcUrl = "https://mainnet.helius-rpc.com/?api-key=7337bed2-6c3a-472f-b06f-9b2be0e1392f";
|
|
44789
44786
|
var WheelXProvider = ({ config, children }) => {
|
|
44790
|
-
if (config?.apiBaseUrl) {
|
|
44791
|
-
setBaseApiUrl(config.apiBaseUrl);
|
|
44792
|
-
}
|
|
44793
44787
|
const queryClient = React5.useMemo(() => new reactQuery.QueryClient(), []);
|
|
44794
44788
|
const evmNetworks = React5.useMemo(() => supportedChains.map((chain) => ({
|
|
44795
44789
|
chainId: chain.id,
|
|
@@ -48010,14 +48004,107 @@ var OptimizedImage = React5.forwardRef(
|
|
|
48010
48004
|
}
|
|
48011
48005
|
);
|
|
48012
48006
|
OptimizedImage.displayName = "OptimizedImage";
|
|
48013
|
-
|
|
48014
|
-
|
|
48015
|
-
|
|
48016
|
-
|
|
48017
|
-
|
|
48018
|
-
|
|
48007
|
+
var usdtAbi = [
|
|
48008
|
+
{
|
|
48009
|
+
name: "approve",
|
|
48010
|
+
type: "function",
|
|
48011
|
+
stateMutability: "nonpayable",
|
|
48012
|
+
inputs: [
|
|
48013
|
+
{ name: "_spender", type: "address" },
|
|
48014
|
+
{ name: "_value", type: "uint256" }
|
|
48015
|
+
],
|
|
48016
|
+
outputs: [{ name: "", type: "bool" }]
|
|
48017
|
+
},
|
|
48018
|
+
{
|
|
48019
|
+
name: "allowance",
|
|
48020
|
+
type: "function",
|
|
48021
|
+
stateMutability: "view",
|
|
48022
|
+
inputs: [
|
|
48023
|
+
{ name: "_owner", type: "address" },
|
|
48024
|
+
{ name: "_spender", type: "address" }
|
|
48025
|
+
],
|
|
48026
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
48027
|
+
}
|
|
48028
|
+
];
|
|
48029
|
+
function useUSDTHandler(chainId) {
|
|
48030
|
+
const config = wagmi.useConfig();
|
|
48031
|
+
const { address: owner } = wagmi.useAccount();
|
|
48032
|
+
const [error, setError] = React5.useState(null);
|
|
48033
|
+
const [isLoading, setIsLoading] = React5.useState(false);
|
|
48034
|
+
const handleApprove = async (tokenAddress, spender, amount) => {
|
|
48035
|
+
setError(null);
|
|
48036
|
+
setIsLoading(true);
|
|
48037
|
+
try {
|
|
48038
|
+
if (!owner) {
|
|
48039
|
+
const errorMsg = "No account available. Please connect your wallet and try again.";
|
|
48040
|
+
setError(errorMsg);
|
|
48041
|
+
throw new Error(errorMsg);
|
|
48042
|
+
}
|
|
48043
|
+
const targetChainId = chainId ?? 1;
|
|
48044
|
+
console.log("[USDT Approve] Reading allowance:", {
|
|
48045
|
+
chainId: targetChainId,
|
|
48046
|
+
tokenAddress,
|
|
48047
|
+
owner,
|
|
48048
|
+
spender
|
|
48049
|
+
});
|
|
48050
|
+
const currentAllowance = await readContract(config, {
|
|
48051
|
+
abi: usdtAbi,
|
|
48052
|
+
address: tokenAddress,
|
|
48053
|
+
functionName: "allowance",
|
|
48054
|
+
chainId: targetChainId,
|
|
48055
|
+
args: [owner, spender]
|
|
48056
|
+
});
|
|
48057
|
+
console.log("[USDT Approve] Current allowance:", currentAllowance.toString());
|
|
48058
|
+
if (currentAllowance >= amount) {
|
|
48059
|
+
console.log(
|
|
48060
|
+
"[USDT Approve] Allowance is sufficient, no additional approval needed"
|
|
48061
|
+
);
|
|
48062
|
+
return;
|
|
48063
|
+
}
|
|
48064
|
+
if (currentAllowance > 0n) {
|
|
48065
|
+
console.log("[USDT Approve] Performing allowance reset to zero");
|
|
48066
|
+
const zeroApproveHash = await writeContract(config, {
|
|
48067
|
+
abi: usdtAbi,
|
|
48068
|
+
address: tokenAddress,
|
|
48069
|
+
functionName: "approve",
|
|
48070
|
+
chainId: targetChainId,
|
|
48071
|
+
args: [spender, 0n]
|
|
48072
|
+
});
|
|
48073
|
+
console.log(
|
|
48074
|
+
"[USDT Approve] Waiting for zero-approval transaction confirmation..."
|
|
48075
|
+
);
|
|
48076
|
+
await waitForTransactionReceipt(config, {
|
|
48077
|
+
hash: zeroApproveHash,
|
|
48078
|
+
chainId: targetChainId
|
|
48079
|
+
});
|
|
48080
|
+
console.log("[USDT Approve] Zero-approval completed");
|
|
48081
|
+
}
|
|
48082
|
+
console.log("[USDT Approve] Performing target amount approval");
|
|
48083
|
+
const approveHash = await writeContract(config, {
|
|
48084
|
+
abi: usdtAbi,
|
|
48085
|
+
address: tokenAddress,
|
|
48086
|
+
functionName: "approve",
|
|
48087
|
+
chainId: targetChainId,
|
|
48088
|
+
args: [spender, amount]
|
|
48089
|
+
});
|
|
48090
|
+
console.log(
|
|
48091
|
+
"[USDT Approve] Waiting for target amount approval transaction confirmation..."
|
|
48092
|
+
);
|
|
48093
|
+
await waitForTransactionReceipt(config, {
|
|
48094
|
+
hash: approveHash,
|
|
48095
|
+
chainId: targetChainId
|
|
48096
|
+
});
|
|
48097
|
+
console.log("[USDT Approve] Double approval process completed");
|
|
48098
|
+
} catch (err) {
|
|
48099
|
+
const errorMessage = err instanceof Error ? err.message : "Authorization operation failed";
|
|
48100
|
+
setError(errorMessage);
|
|
48101
|
+
throw err;
|
|
48102
|
+
} finally {
|
|
48103
|
+
setIsLoading(false);
|
|
48104
|
+
}
|
|
48019
48105
|
};
|
|
48020
|
-
};
|
|
48106
|
+
return { handleApprove, error, isLoading };
|
|
48107
|
+
}
|
|
48021
48108
|
var buttonStyles = {
|
|
48022
48109
|
w: "100%",
|
|
48023
48110
|
h: {
|
|
@@ -48191,7 +48278,7 @@ var FormButton = ({
|
|
|
48191
48278
|
startLoading();
|
|
48192
48279
|
const vCode = extractVPathSegment();
|
|
48193
48280
|
const fromAddress = fromTokenInfo.isSolana ? solanaWalletAddress || "" : effectiveEvmAddress || "";
|
|
48194
|
-
const toAddress = toTokenInfo.isSolana ? solanaWalletAddress || "" : differentAddress || effectiveEvmAddress || "";
|
|
48281
|
+
const toAddress = toTokenInfo.isSolana ? config?.solanaDifferentAddress || solanaWalletAddress || "" : config?.evmDifferentAddress || differentAddress || effectiveEvmAddress || "";
|
|
48195
48282
|
if (fromAddress && fromAmount) {
|
|
48196
48283
|
console.log(
|
|
48197
48284
|
"[Transaction] Refetching quote after transaction rejection..."
|
|
@@ -48226,7 +48313,9 @@ var FormButton = ({
|
|
|
48226
48313
|
effectiveEvmAddress,
|
|
48227
48314
|
solanaWalletAddress,
|
|
48228
48315
|
differentAddress,
|
|
48229
|
-
slippage
|
|
48316
|
+
slippage,
|
|
48317
|
+
config?.evmDifferentAddress,
|
|
48318
|
+
config?.solanaDifferentAddress
|
|
48230
48319
|
]
|
|
48231
48320
|
);
|
|
48232
48321
|
const { sendTransaction, isPending } = wagmi.useSendTransaction({
|
|
@@ -48516,7 +48605,7 @@ var FormButton = ({
|
|
|
48516
48605
|
/* @__PURE__ */ jsxRuntime.jsx(react.Icon, { w: "24px", h: "24px", as: WalletIcon }),
|
|
48517
48606
|
/* @__PURE__ */ jsxRuntime.jsxs(Heading, { variant: buttonHeadingVariant, children: [
|
|
48518
48607
|
"Connect ",
|
|
48519
|
-
name
|
|
48608
|
+
name ,
|
|
48520
48609
|
" Wallet"
|
|
48521
48610
|
] })
|
|
48522
48611
|
] });
|
|
@@ -48591,7 +48680,7 @@ var FormButton = ({
|
|
|
48591
48680
|
startLoading();
|
|
48592
48681
|
const vCode = extractVPathSegment();
|
|
48593
48682
|
const fromAddress = solanaWalletAddress || "";
|
|
48594
|
-
const toAddress = toTokenInfo.isSolana ? solanaWalletAddress || "" : differentAddress || effectiveEvmAddress || "";
|
|
48683
|
+
const toAddress = toTokenInfo.isSolana ? config?.solanaDifferentAddress || solanaWalletAddress || "" : config?.evmDifferentAddress || differentAddress || effectiveEvmAddress || "";
|
|
48595
48684
|
if (fromAddress && fromAmount) {
|
|
48596
48685
|
console.log(
|
|
48597
48686
|
"[Solana Transaction] Refetching quote after transaction rejection..."
|
|
@@ -48632,28 +48721,17 @@ var FormButton = ({
|
|
|
48632
48721
|
}
|
|
48633
48722
|
) });
|
|
48634
48723
|
}
|
|
48635
|
-
if (
|
|
48724
|
+
if (!fromTokenInfo.isSolana && !hasEvmUiWallet) {
|
|
48725
|
+
return renderEvmWalletConnectButton("EVM");
|
|
48726
|
+
}
|
|
48727
|
+
if (fromTokenInfo.isSolana && !solanaWalletAddress) {
|
|
48636
48728
|
return renderSolanaWalletConnectButton();
|
|
48637
48729
|
}
|
|
48638
|
-
if (
|
|
48730
|
+
if (fromTokenInfo.isSolana && !toTokenInfo.isSolana && !skipEvmWalletConnect && !hasEvmUiWallet) {
|
|
48639
48731
|
return renderEvmWalletConnectButton("EVM");
|
|
48640
48732
|
}
|
|
48641
|
-
if (!
|
|
48642
|
-
|
|
48643
|
-
return renderSolanaWalletConnectButton();
|
|
48644
|
-
}
|
|
48645
|
-
return renderEvmWalletConnectButton();
|
|
48646
|
-
} else if (!hasEvmUiWallet && solanaWalletAddress) {
|
|
48647
|
-
if (!fromTokenInfo.isSolana && !toTokenInfo.isSolana || !fromTokenInfo.isSolana && toTokenInfo.isSolana || fromTokenInfo.isSolana && !toTokenInfo.isSolana && !skipEvmWalletConnect) {
|
|
48648
|
-
return renderEvmWalletConnectButton("EVM");
|
|
48649
|
-
}
|
|
48650
|
-
} else if (hasEvmUiWallet && !solanaWalletAddress) {
|
|
48651
|
-
if (fromTokenInfo.isSolana || toTokenInfo.isSolana && !skipSolanaWalletConnect) {
|
|
48652
|
-
return renderSolanaWalletConnectButton();
|
|
48653
|
-
}
|
|
48654
|
-
if (fromTokenInfo.isSolana && toTokenInfo.isSolana || !fromTokenInfo.isSolana && toTokenInfo.isSolana && !skipSolanaWalletConnect || fromTokenInfo.isSolana && !toTokenInfo.isSolana) {
|
|
48655
|
-
return renderSolanaWalletConnectButton();
|
|
48656
|
-
}
|
|
48733
|
+
if (!fromTokenInfo.isSolana && toTokenInfo.isSolana && !skipSolanaWalletConnect && !solanaWalletAddress) {
|
|
48734
|
+
return renderSolanaWalletConnectButton();
|
|
48657
48735
|
}
|
|
48658
48736
|
if (isFetching || fromAmount && !toAmount) {
|
|
48659
48737
|
return /* @__PURE__ */ jsxRuntime.jsx(
|