@xswap-link/sdk 0.1.0 → 0.1.2
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/.github/workflows/main.yml +1 -0
- package/.github/workflows/publish.yml +1 -0
- package/CHANGELOG.md +24 -0
- package/dist/index.css +128 -32
- package/dist/index.d.mts +22 -19
- package/dist/index.d.ts +22 -19
- package/dist/index.js +1167 -32978
- package/dist/index.mjs +1152 -32988
- package/nodemon.json +5 -0
- package/package.json +17 -6
- package/src/components/TxConfigForm/BalanceComponent.tsx +51 -0
- package/src/components/TxConfigForm/ChainListElement.tsx +36 -0
- package/src/components/TxConfigForm/Description.tsx +15 -0
- package/src/components/TxConfigForm/ErrorField.tsx +17 -0
- package/src/components/TxConfigForm/FeesDetails.tsx +105 -0
- package/src/components/TxConfigForm/Form.tsx +129 -447
- package/src/components/TxConfigForm/History.tsx +67 -122
- package/src/components/TxConfigForm/HistoryCard.tsx +3 -2
- package/src/components/TxConfigForm/Settings.tsx +137 -0
- package/src/components/TxConfigForm/Summary.tsx +57 -0
- package/src/components/TxConfigForm/SwapPanel.tsx +191 -0
- package/src/components/TxConfigForm/TokenPicker.tsx +76 -67
- package/src/components/TxConfigForm/TopBar.tsx +53 -0
- package/src/components/TxConfigForm/index.tsx +121 -0
- package/src/components/icons/ArrowDownIcon.tsx +16 -0
- package/src/components/icons/ChainlinkCCIPIcon.tsx +23 -0
- package/src/components/icons/ChevronDownIcon.tsx +2 -2
- package/src/components/icons/ChevronUpIcon.tsx +2 -2
- package/src/components/icons/CloseIcon.tsx +2 -2
- package/src/components/icons/CoinsIcon.tsx +8 -6
- package/src/components/icons/HistoryIcon.tsx +8 -5
- package/src/components/icons/InfoIcon.tsx +13 -0
- package/src/components/icons/SettingsIcon.tsx +16 -0
- package/src/components/icons/TimerIcon.tsx +15 -0
- package/src/components/icons/XSwapBadgeIcon.tsx +23 -0
- package/src/components/icons/index.ts +6 -0
- package/src/config/index.ts +1 -0
- package/src/config/wagmiConfig.ts +45 -0
- package/src/constants/index.ts +6 -3
- package/tailwind.config.js +11 -2
- package/src/components/TxConfigForm/index.jsx +0 -100
- package/src/components/global.d.ts +0 -6
|
@@ -1,80 +1,75 @@
|
|
|
1
1
|
import "@src/global.css";
|
|
2
|
-
import React, {
|
|
2
|
+
import React, { FormEvent, useEffect, useState } from "react";
|
|
3
3
|
import { ethers } from "ethers";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
ContractCall,
|
|
7
|
-
Route,
|
|
8
|
-
Token,
|
|
9
|
-
TokenBalances,
|
|
10
|
-
TokenPrices,
|
|
11
|
-
} from "@src/models";
|
|
4
|
+
import { useAccount, useSwitchChain } from "wagmi";
|
|
5
|
+
import { Chain, Route, Token, TokenBalances, TokenPrices } from "@src/models";
|
|
12
6
|
import { getPrices, getRoute } from "@src/services";
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
shortAddress,
|
|
17
|
-
weiToHumanReadable,
|
|
18
|
-
} from "@src/utils";
|
|
19
|
-
import {
|
|
20
|
-
ChevronDownIcon,
|
|
21
|
-
ChevronUpIcon,
|
|
22
|
-
CoinsIcon,
|
|
23
|
-
DownArrowIcon,
|
|
24
|
-
HistoryIcon,
|
|
25
|
-
PercentageIcon,
|
|
26
|
-
} from "@src/components/icons";
|
|
27
|
-
import { SLIPPAGE_PRESETS, NUMBER_INPUT_REGEX } from "@src/constants";
|
|
28
|
-
import { History } from "./History";
|
|
29
|
-
import { Alert } from "@src/components";
|
|
7
|
+
import { getBalances } from "@src/utils";
|
|
8
|
+
import { ROUTE_TIMEOUT_MS } from "@src/constants";
|
|
9
|
+
import { TxConfigFormProps } from "@src/components";
|
|
30
10
|
import { useDebounce } from "@src/hooks";
|
|
31
|
-
import { TokenPicker } from "./TokenPicker";
|
|
32
|
-
import { Skeleton } from "@src/components/Skeleton";
|
|
33
11
|
import { ADDRESSES } from "@src/contracts";
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export const Form
|
|
12
|
+
import { History } from "./History";
|
|
13
|
+
import { TopBar } from "./TopBar";
|
|
14
|
+
import { Settings } from "./Settings";
|
|
15
|
+
import { FeesDetails } from "./FeesDetails";
|
|
16
|
+
import { Summary } from "./Summary";
|
|
17
|
+
import { SwapPanel } from "./SwapPanel";
|
|
18
|
+
import { ErrorField } from "./ErrorField";
|
|
19
|
+
import { Description } from "./Description";
|
|
20
|
+
import { DEFAULT_SOURCE_CHAIN_ID } from "@src/constants";
|
|
21
|
+
|
|
22
|
+
export const Form = ({
|
|
45
23
|
dstChainId,
|
|
46
24
|
dstTokenAddr,
|
|
47
25
|
customContractCalls,
|
|
48
26
|
desc,
|
|
49
27
|
supportedChains,
|
|
50
28
|
onSubmit,
|
|
29
|
+
}: TxConfigFormProps & {
|
|
30
|
+
onSubmit: (route: Route) => void;
|
|
51
31
|
}) => {
|
|
52
|
-
const [signer, setSigner] = useState<string>(
|
|
32
|
+
const [signer, setSigner] = useState<string | undefined>();
|
|
53
33
|
const [dstChain, setDstChain] = useState<Chain>();
|
|
54
34
|
const [dstToken, setDstToken] = useState<Token>();
|
|
55
35
|
const [srcChain, setSrcChain] = useState<Chain>();
|
|
56
36
|
const [srcToken, setSrcToken] = useState<Token>();
|
|
57
37
|
const [amount, setAmount] = useState<string>("");
|
|
58
38
|
const [paymentToken, setPaymentToken] = useState<Token>();
|
|
59
|
-
const [slippage, setSlippage] = useState<number>();
|
|
60
|
-
const [slippageActivePresetIndex, setSlippageActivePresetIndex] = useState(1);
|
|
61
|
-
const [usingSlippageInput, setUsingSlippageInput] = useState(false);
|
|
62
|
-
const [chainListShown, setChainListShown] = useState(false);
|
|
63
|
-
const [tokenListShown, setTokenListShown] = useState(false);
|
|
64
39
|
const [route, setRoute] = useState<Route>();
|
|
65
40
|
const [expressChecked, setExpressChecked] = useState(true);
|
|
66
|
-
const [feesDetailsShown, setFeesDetailsShown] = useState(false);
|
|
67
41
|
const [historyTabShown, setHistoryTabShown] = useState(false);
|
|
68
42
|
const [isGettingRoute, setIsGettingRoute] = useState(false);
|
|
69
43
|
const [prices, setPrices] = useState<TokenPrices>();
|
|
70
44
|
const [balances, setBalances] = useState<TokenBalances>();
|
|
45
|
+
const [settingsShown, setSettingsShown] = useState(false);
|
|
46
|
+
const [formError, setFormError] = useState("");
|
|
47
|
+
const [slippage, setSlippage] = useState<string>("1.5");
|
|
48
|
+
const [feesDetailsShown, setFeesDetailsShown] = useState(false);
|
|
71
49
|
|
|
72
50
|
const debouncedAmount = useDebounce(amount, 1000);
|
|
73
|
-
const
|
|
51
|
+
const account = useAccount();
|
|
52
|
+
const { switchChainAsync } = useSwitchChain();
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
setSigner(account.address);
|
|
56
|
+
}, [account.address]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
const defaultChain = supportedChains.find(
|
|
60
|
+
({ chainId }) => chainId === account.chainId?.toString(),
|
|
61
|
+
);
|
|
62
|
+
setSrcChain(
|
|
63
|
+
defaultChain ??
|
|
64
|
+
supportedChains.find(
|
|
65
|
+
({ chainId }) => chainId === DEFAULT_SOURCE_CHAIN_ID,
|
|
66
|
+
),
|
|
67
|
+
);
|
|
68
|
+
}, [account.chainId]);
|
|
74
69
|
|
|
75
70
|
useEffect(() => {
|
|
76
71
|
setDstChain(supportedChains.find((chain) => chain.chainId === dstChainId));
|
|
77
|
-
}, [supportedChains]);
|
|
72
|
+
}, [supportedChains, dstChainId]);
|
|
78
73
|
|
|
79
74
|
useEffect(() => {
|
|
80
75
|
setDstToken(
|
|
@@ -84,12 +79,6 @@ export const Form: FC<Props> = ({
|
|
|
84
79
|
);
|
|
85
80
|
}, [dstChain]);
|
|
86
81
|
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
if (!usingSlippageInput) {
|
|
89
|
-
setSlippage(SLIPPAGE_PRESETS[slippageActivePresetIndex]);
|
|
90
|
-
}
|
|
91
|
-
}, [slippageActivePresetIndex, usingSlippageInput]);
|
|
92
|
-
|
|
93
82
|
useEffect(() => {
|
|
94
83
|
if (
|
|
95
84
|
srcChain &&
|
|
@@ -99,6 +88,10 @@ export const Form: FC<Props> = ({
|
|
|
99
88
|
paymentToken &&
|
|
100
89
|
slippage
|
|
101
90
|
) {
|
|
91
|
+
setFormError("");
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
setFormError("Getting Route is taking longer than usually");
|
|
94
|
+
}, ROUTE_TIMEOUT_MS);
|
|
102
95
|
setIsGettingRoute(true);
|
|
103
96
|
setFeesDetailsShown(false);
|
|
104
97
|
getRoute({
|
|
@@ -112,12 +105,21 @@ export const Form: FC<Props> = ({
|
|
|
112
105
|
toChain: dstChainId,
|
|
113
106
|
toToken: dstTokenAddr,
|
|
114
107
|
paymentToken: paymentToken.address,
|
|
115
|
-
slippage,
|
|
108
|
+
slippage: Number(slippage),
|
|
116
109
|
expressDelivery: expressChecked,
|
|
117
110
|
customContractCalls,
|
|
118
111
|
})
|
|
119
|
-
.then((response) =>
|
|
120
|
-
|
|
112
|
+
.then((response) => {
|
|
113
|
+
setRoute(response);
|
|
114
|
+
setFormError("");
|
|
115
|
+
})
|
|
116
|
+
.catch((err) => {
|
|
117
|
+
setFormError(err.message);
|
|
118
|
+
})
|
|
119
|
+
.finally(() => {
|
|
120
|
+
setIsGettingRoute(false);
|
|
121
|
+
clearTimeout(timeout);
|
|
122
|
+
});
|
|
121
123
|
}
|
|
122
124
|
}, [
|
|
123
125
|
signer,
|
|
@@ -129,52 +131,6 @@ export const Form: FC<Props> = ({
|
|
|
129
131
|
expressChecked,
|
|
130
132
|
]);
|
|
131
133
|
|
|
132
|
-
useEffect(() => {
|
|
133
|
-
if (ethereum) {
|
|
134
|
-
ethereum
|
|
135
|
-
.request({ method: "eth_accounts" })
|
|
136
|
-
.then((accounts: string[]) => {
|
|
137
|
-
if (accounts[0]) {
|
|
138
|
-
setSigner(accounts[0]);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
ethereum.on("accountsChanged", (accounts: string[]) => {
|
|
143
|
-
if (accounts[0]) {
|
|
144
|
-
setSigner(accounts[0]);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
return () => ethereum.removeListener("accountsChanged", () => {});
|
|
148
|
-
}
|
|
149
|
-
}, [ethereum]);
|
|
150
|
-
|
|
151
|
-
useEffect(() => {
|
|
152
|
-
if (
|
|
153
|
-
ethereum &&
|
|
154
|
-
ethereum.request &&
|
|
155
|
-
supportedChains &&
|
|
156
|
-
supportedChains.length > 0
|
|
157
|
-
) {
|
|
158
|
-
ethereum.request({ method: "eth_chainId" }).then((chainId: string) => {
|
|
159
|
-
setSrcChain(
|
|
160
|
-
supportedChains.find(
|
|
161
|
-
(chain) => chain.chainId === parseInt(chainId, 16).toString(),
|
|
162
|
-
),
|
|
163
|
-
);
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
ethereum.on("chainChanged", (chainId: string) => {
|
|
167
|
-
setSrcChain(
|
|
168
|
-
supportedChains.find(
|
|
169
|
-
(chain) => chain.chainId === parseInt(chainId, 16).toString(),
|
|
170
|
-
),
|
|
171
|
-
);
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
return () => ethereum.removeListener("chainChanged", () => {});
|
|
175
|
-
}
|
|
176
|
-
}, [ethereum, supportedChains]);
|
|
177
|
-
|
|
178
134
|
useEffect(() => {
|
|
179
135
|
if (srcChain) {
|
|
180
136
|
setPaymentToken(
|
|
@@ -188,9 +144,12 @@ export const Form: FC<Props> = ({
|
|
|
188
144
|
}, [srcChain]);
|
|
189
145
|
|
|
190
146
|
useEffect(() => {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
147
|
+
setAmount("");
|
|
148
|
+
setFormError("");
|
|
149
|
+
}, [srcToken]);
|
|
150
|
+
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
setSrcToken(srcChain?.tokens[0]);
|
|
194
153
|
}, [srcChain]);
|
|
195
154
|
|
|
196
155
|
useEffect(() => {
|
|
@@ -209,357 +168,80 @@ export const Form: FC<Props> = ({
|
|
|
209
168
|
|
|
210
169
|
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
|
|
211
170
|
event.preventDefault();
|
|
212
|
-
if (route)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
171
|
+
if (route) onSubmit(route);
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const handleSwitchChain = async () => {
|
|
175
|
+
await switchChainAsync({ chainId: Number(srcChain?.chainId) });
|
|
217
176
|
};
|
|
218
177
|
|
|
219
178
|
return (
|
|
220
179
|
<form
|
|
221
|
-
className="flex flex-col gap-2 z-10 my-0 mx-auto p-
|
|
180
|
+
className="flex flex-col gap-2 z-10 my-0 mx-auto p-4 rounded-3xl overflow-hidden font-light w-x_mobile sm:w-x_desktop"
|
|
222
181
|
onSubmit={handleSubmit}
|
|
223
182
|
>
|
|
224
|
-
<
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
</>
|
|
240
|
-
) : (
|
|
241
|
-
<div>Back</div>
|
|
242
|
-
)}
|
|
243
|
-
</div>
|
|
244
|
-
)}
|
|
245
|
-
</div>
|
|
183
|
+
<TopBar
|
|
184
|
+
signer={signer}
|
|
185
|
+
historyTabShown={historyTabShown}
|
|
186
|
+
setHistoryTabShown={setHistoryTabShown}
|
|
187
|
+
setSettingsShown={setSettingsShown}
|
|
188
|
+
/>
|
|
189
|
+
{settingsShown && (
|
|
190
|
+
<Settings
|
|
191
|
+
expressChecked={expressChecked}
|
|
192
|
+
setExpressChecked={setExpressChecked}
|
|
193
|
+
setSettingsShown={setSettingsShown}
|
|
194
|
+
slippage={slippage}
|
|
195
|
+
setSlippage={setSlippage}
|
|
196
|
+
/>
|
|
197
|
+
)}
|
|
246
198
|
{historyTabShown ? (
|
|
247
|
-
<
|
|
248
|
-
<History signer={signer} supportedChains={supportedChains} />
|
|
249
|
-
</div>
|
|
199
|
+
<History signer={signer} supportedChains={supportedChains} />
|
|
250
200
|
) : (
|
|
251
201
|
<div className="flex flex-col gap-2">
|
|
252
|
-
{desc &&
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
202
|
+
{desc && <Description description={desc} />}
|
|
203
|
+
<SwapPanel
|
|
204
|
+
amount={amount}
|
|
205
|
+
setAmount={setAmount}
|
|
206
|
+
srcToken={srcToken}
|
|
207
|
+
setSrcToken={setSrcToken}
|
|
208
|
+
srcChain={srcChain}
|
|
209
|
+
signer={signer}
|
|
210
|
+
balances={balances}
|
|
211
|
+
prices={prices}
|
|
212
|
+
supportedChains={supportedChains}
|
|
213
|
+
setSrcChain={setSrcChain}
|
|
214
|
+
/>
|
|
215
|
+
<Summary
|
|
216
|
+
isGettingRoute={isGettingRoute}
|
|
217
|
+
amount={amount}
|
|
218
|
+
route={route}
|
|
219
|
+
dstToken={dstToken}
|
|
220
|
+
dstChain={dstChain}
|
|
221
|
+
/>
|
|
222
|
+
<FeesDetails
|
|
223
|
+
route={route}
|
|
224
|
+
isGettingRoute={isGettingRoute}
|
|
225
|
+
paymentToken={paymentToken}
|
|
226
|
+
dstToken={dstToken}
|
|
227
|
+
expressChecked={expressChecked}
|
|
228
|
+
slippage={slippage}
|
|
229
|
+
feesDetailsShown={feesDetailsShown}
|
|
230
|
+
setFeesDetailsShown={setFeesDetailsShown}
|
|
231
|
+
/>
|
|
232
|
+
{formError.length > 0 && <ErrorField error={formError} />}
|
|
233
|
+
{srcChain && srcChain.chainId !== account.chainId?.toString() ? (
|
|
234
|
+
<button type="button" onClick={handleSwitchChain}>
|
|
235
|
+
Switch chain
|
|
236
|
+
</button>
|
|
237
|
+
) : (
|
|
238
|
+
<button
|
|
239
|
+
type="submit"
|
|
240
|
+
disabled={!signer || !route || isGettingRoute}
|
|
241
|
+
>
|
|
242
|
+
Submit
|
|
243
|
+
</button>
|
|
258
244
|
)}
|
|
259
|
-
<div className="flex flex-col items-start globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4">
|
|
260
|
-
<div className="w-full flex gap-4 items-start justify-between mb-4">
|
|
261
|
-
<div className="flex flex-col overflow-hidden w-1/2">
|
|
262
|
-
<input
|
|
263
|
-
className="p-0 border-none bg-transparent text-2xl overflow-ellipsis"
|
|
264
|
-
value={amount}
|
|
265
|
-
onChange={(e) => {
|
|
266
|
-
if (e.target.value === ".") return;
|
|
267
|
-
if (NUMBER_INPUT_REGEX.test(e.target.value)) {
|
|
268
|
-
setIsGettingRoute(true);
|
|
269
|
-
setFeesDetailsShown(false);
|
|
270
|
-
setAmount(e.target.value.replace(/,/g, "."));
|
|
271
|
-
}
|
|
272
|
-
}}
|
|
273
|
-
inputMode="decimal"
|
|
274
|
-
spellCheck="false"
|
|
275
|
-
autoComplete="off"
|
|
276
|
-
autoCorrect="off"
|
|
277
|
-
placeholder="0"
|
|
278
|
-
/>
|
|
279
|
-
</div>
|
|
280
|
-
<div className="flex flex-col relative items-end gap-2.5 w-1/2">
|
|
281
|
-
<div
|
|
282
|
-
className="flex gap-2 items-center p-2 max-w-full cursor-pointer bg-black text-white text-xs globalBorder rounded-3xl whitespace-nowrap"
|
|
283
|
-
onClick={() => {
|
|
284
|
-
setTokenListShown((state) => !state);
|
|
285
|
-
}}
|
|
286
|
-
>
|
|
287
|
-
<img
|
|
288
|
-
height={16}
|
|
289
|
-
width={16}
|
|
290
|
-
src={srcToken?.image}
|
|
291
|
-
style={{ marginRight: "4px" }}
|
|
292
|
-
alt={srcToken?.name}
|
|
293
|
-
/>
|
|
294
|
-
<div>{srcToken?.symbol}</div>
|
|
295
|
-
<DownArrowIcon />
|
|
296
|
-
</div>
|
|
297
|
-
|
|
298
|
-
{tokenListShown && (
|
|
299
|
-
<TokenPicker
|
|
300
|
-
onCloseClick={() => setTokenListShown(false)}
|
|
301
|
-
tokens={srcChain?.tokens}
|
|
302
|
-
signer={signer}
|
|
303
|
-
selectedToken={srcToken}
|
|
304
|
-
setSelectedToken={(token) => setSrcToken(token)}
|
|
305
|
-
balances={balances}
|
|
306
|
-
/>
|
|
307
|
-
)}
|
|
308
|
-
|
|
309
|
-
<div className="flex gap-2 items-center text-xs font-medium text-white opacity-60 py-0.5 px-2">
|
|
310
|
-
{signer && (
|
|
311
|
-
<>
|
|
312
|
-
Balance:{" "}
|
|
313
|
-
{srcToken && balances ? (
|
|
314
|
-
weiToHumanReadable({
|
|
315
|
-
amount: balances[srcToken.address]?.toString() || "0",
|
|
316
|
-
decimals: srcToken.decimals,
|
|
317
|
-
precisionFractionalPlaces: 4,
|
|
318
|
-
})
|
|
319
|
-
) : (
|
|
320
|
-
<Skeleton w="w-12" h="h-3" />
|
|
321
|
-
)}
|
|
322
|
-
</>
|
|
323
|
-
)}
|
|
324
|
-
</div>
|
|
325
|
-
</div>
|
|
326
|
-
</div>
|
|
327
|
-
|
|
328
|
-
<div className="flex justify-between items-center w-full text-xs text-[rgba(255,255,255,0.6)] relative">
|
|
329
|
-
{srcToken ? (
|
|
330
|
-
prices && prices[srcToken.address] ? (
|
|
331
|
-
<div>
|
|
332
|
-
{`$${(
|
|
333
|
-
Number(amount) * Number(prices[srcToken.address])
|
|
334
|
-
).toFixed(2)}`}
|
|
335
|
-
</div>
|
|
336
|
-
) : (
|
|
337
|
-
<Alert desc="Unknown price" />
|
|
338
|
-
)
|
|
339
|
-
) : (
|
|
340
|
-
<>$0.00</>
|
|
341
|
-
)}
|
|
342
|
-
<div
|
|
343
|
-
className="bg-black globalBorder rounded-2xl whitespace-nowrap flex items-center p-2 cursor-pointer gap-1"
|
|
344
|
-
onClick={() => setChainListShown((prev) => !prev)}
|
|
345
|
-
>
|
|
346
|
-
<img
|
|
347
|
-
width={12}
|
|
348
|
-
height={12}
|
|
349
|
-
src={srcChain?.image}
|
|
350
|
-
alt={srcChain?.name}
|
|
351
|
-
/>
|
|
352
|
-
<div>{srcChain?.displayName}</div>
|
|
353
|
-
<DownArrowIcon />
|
|
354
|
-
</div>
|
|
355
|
-
|
|
356
|
-
{chainListShown && (
|
|
357
|
-
<ul className="bg-black globalBorder rounded-lg whitespace-nowrap z-1 right-0 top-10 px-2 max-w-full max-h-[40vh] overflow-auto absolute text-sm z-20">
|
|
358
|
-
{supportedChains
|
|
359
|
-
.filter((chain) => chain.chainId !== srcChain?.chainId)
|
|
360
|
-
.map((chain, index) => (
|
|
361
|
-
<div key={`${chain.ecosystem}-${chain.chainId}`}>
|
|
362
|
-
<li
|
|
363
|
-
className="bg-transparent border-none flex gap-1 items-center cursor-pointer py-2 w-full"
|
|
364
|
-
key={index}
|
|
365
|
-
onClick={() => {
|
|
366
|
-
setSrcChain(chain);
|
|
367
|
-
setChainListShown(false);
|
|
368
|
-
}}
|
|
369
|
-
>
|
|
370
|
-
<img
|
|
371
|
-
width={12}
|
|
372
|
-
height={12}
|
|
373
|
-
src={chain.image}
|
|
374
|
-
alt={chain.displayName}
|
|
375
|
-
/>
|
|
376
|
-
{chain.displayName}
|
|
377
|
-
</li>
|
|
378
|
-
{index !== supportedChains.length - 1 && (
|
|
379
|
-
<div className="h-px mx-2 bg-[rgba(255,255,255,0.15)]"></div>
|
|
380
|
-
)}
|
|
381
|
-
</div>
|
|
382
|
-
))}
|
|
383
|
-
</ul>
|
|
384
|
-
)}
|
|
385
|
-
</div>
|
|
386
|
-
</div>
|
|
387
|
-
<div className="flex flex-col items-start globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4">
|
|
388
|
-
<div className="flex justify-around sm:justify-start gap-4 items-center w-full text-xs text-[rgba(255,255,255,0.6)]">
|
|
389
|
-
<div className="flex flex-col items-center sm:flex-row gap-1">
|
|
390
|
-
You're getting:
|
|
391
|
-
{isGettingRoute && amount.length > 0 ? (
|
|
392
|
-
<div className="bg-current rounded animate-pulse w-[190px] h-7" />
|
|
393
|
-
) : (
|
|
394
|
-
<div className="flex gap-1">
|
|
395
|
-
<div className="text-white text-lg">
|
|
396
|
-
{weiToHumanReadable({
|
|
397
|
-
amount: route?.estAmountOut || "0",
|
|
398
|
-
decimals: dstToken?.decimals || 18,
|
|
399
|
-
precisionFractionalPlaces: 5,
|
|
400
|
-
})}
|
|
401
|
-
</div>
|
|
402
|
-
<div className="flex items-center gap-1 text-white">
|
|
403
|
-
<div className="w-4 h-4">
|
|
404
|
-
<img src={dstToken?.image} alt={dstToken?.name} />
|
|
405
|
-
</div>
|
|
406
|
-
<div className="text-lg">{dstToken?.symbol}</div>
|
|
407
|
-
</div>
|
|
408
|
-
</div>
|
|
409
|
-
)}
|
|
410
|
-
</div>
|
|
411
|
-
<div className="flex flex-col sm:flex-row gap-1">
|
|
412
|
-
on chain:
|
|
413
|
-
<div className="flex gap-1 text-white">
|
|
414
|
-
<div className="w-4 h-4">
|
|
415
|
-
<img src={dstChain?.image} alt={dstChain?.name} />
|
|
416
|
-
</div>
|
|
417
|
-
<div>{dstChain?.displayName}</div>
|
|
418
|
-
</div>
|
|
419
|
-
</div>
|
|
420
|
-
</div>
|
|
421
|
-
</div>
|
|
422
|
-
<div className="flex flex-col gap-2 globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4">
|
|
423
|
-
<div className="flex w-full items-center justify-between">
|
|
424
|
-
<div className="flex gap-1 text-xs font-medium text-white opacity-60">
|
|
425
|
-
<CoinsIcon />
|
|
426
|
-
<div className="mr-1">Fees:</div>
|
|
427
|
-
{isGettingRoute ? (
|
|
428
|
-
<div className="bg-current rounded animate-pulse w-20 h-4" />
|
|
429
|
-
) : (
|
|
430
|
-
` ${weiToHumanReadable({
|
|
431
|
-
amount: safeBigNumberFrom(
|
|
432
|
-
route?.xSwapFees.xSwapFee.nativeFee || "0",
|
|
433
|
-
)
|
|
434
|
-
.add(safeBigNumberFrom(route?.xSwapFees.ccipFee || "0"))
|
|
435
|
-
.add(
|
|
436
|
-
safeBigNumberFrom(
|
|
437
|
-
route?.xSwapFees.expressDeliveryFee || "0",
|
|
438
|
-
),
|
|
439
|
-
)
|
|
440
|
-
.toString(),
|
|
441
|
-
decimals: 18,
|
|
442
|
-
precisionFractionalPlaces: 5,
|
|
443
|
-
})} ${paymentToken?.symbol}`
|
|
444
|
-
)}
|
|
445
|
-
</div>
|
|
446
|
-
<div
|
|
447
|
-
onClick={() => setFeesDetailsShown((x) => !x)}
|
|
448
|
-
className="text-xs font-medium text-white opacity-60 cursor-pointer"
|
|
449
|
-
>
|
|
450
|
-
{feesDetailsShown ? <ChevronUpIcon /> : <ChevronDownIcon />}
|
|
451
|
-
</div>
|
|
452
|
-
</div>
|
|
453
|
-
{feesDetailsShown && (
|
|
454
|
-
<div className="flex w-full items-center justify-between ">
|
|
455
|
-
<div className="flex flex-col text-[10px] ">
|
|
456
|
-
<div className="font-medium text-white opacity-60">
|
|
457
|
-
CCIP Fee:
|
|
458
|
-
{` ${weiToHumanReadable({
|
|
459
|
-
amount: route?.xSwapFees.ccipFee || "0",
|
|
460
|
-
decimals: 18,
|
|
461
|
-
precisionFractionalPlaces: 5,
|
|
462
|
-
})} ${paymentToken?.symbol}`}
|
|
463
|
-
</div>
|
|
464
|
-
<div className="font-medium text-white opacity-60">
|
|
465
|
-
Native Fee:
|
|
466
|
-
{` ${weiToHumanReadable({
|
|
467
|
-
amount: route?.xSwapFees.xSwapFee.nativeFee || "0",
|
|
468
|
-
decimals: 18,
|
|
469
|
-
precisionFractionalPlaces: 5,
|
|
470
|
-
})} ${paymentToken?.symbol}`}
|
|
471
|
-
</div>
|
|
472
|
-
</div>
|
|
473
|
-
<div className="flex flex-col text-[10px] ">
|
|
474
|
-
<div className="font-medium text-white opacity-60">
|
|
475
|
-
Slippage: {`${slippage}%`}
|
|
476
|
-
</div>
|
|
477
|
-
<div className="font-medium text-white opacity-60">
|
|
478
|
-
Min amount out:{" "}
|
|
479
|
-
{weiToHumanReadable({
|
|
480
|
-
amount: route?.minAmountOut || "0",
|
|
481
|
-
decimals: dstToken?.decimals || 18,
|
|
482
|
-
precisionFractionalPlaces: 5,
|
|
483
|
-
})}{" "}
|
|
484
|
-
{dstToken?.symbol}
|
|
485
|
-
</div>
|
|
486
|
-
</div>
|
|
487
|
-
</div>
|
|
488
|
-
)}
|
|
489
|
-
</div>
|
|
490
|
-
<div className="flex justify-between">
|
|
491
|
-
<div className="flex flex-col gap-2 ">
|
|
492
|
-
<div className=" text-[rgba(255,255,255,0.6)]">Slippage:</div>
|
|
493
|
-
<div className="flex gap-2">
|
|
494
|
-
<div className="flex items-center bg-[rgba(15,15,15,1)] p-1 globalBorder rounded-xl">
|
|
495
|
-
{SLIPPAGE_PRESETS.map((preset, index) => (
|
|
496
|
-
<div
|
|
497
|
-
key={index}
|
|
498
|
-
className={`transition-all cursor-pointer block rounded-lg p-2 border-none text-sm ${
|
|
499
|
-
index === slippageActivePresetIndex &&
|
|
500
|
-
!usingSlippageInput
|
|
501
|
-
? "text-white"
|
|
502
|
-
: "text-[rgba(255,255,255,0.2)]"
|
|
503
|
-
} ${
|
|
504
|
-
index === slippageActivePresetIndex &&
|
|
505
|
-
!usingSlippageInput
|
|
506
|
-
? "bg-gradient-to-r from-[#3681c6] to-[#2b4a9d]"
|
|
507
|
-
: ""
|
|
508
|
-
}`}
|
|
509
|
-
onClick={() => {
|
|
510
|
-
setUsingSlippageInput(false);
|
|
511
|
-
setSlippageActivePresetIndex(index);
|
|
512
|
-
}}
|
|
513
|
-
>
|
|
514
|
-
{`${preset}%`}
|
|
515
|
-
</div>
|
|
516
|
-
))}
|
|
517
|
-
</div>
|
|
518
|
-
<div className="bg-[rgba(39,39,39,1)] p-2 border border-solid border-[rgba(82,82,82,1)] rounded-xl text-white flex items-center">
|
|
519
|
-
<input
|
|
520
|
-
className="text-white border-none bg-[rgba(39,39,39,1)] w-10 placeholder:text-[rgba(255,255,255,0.2)] focus:outline-none"
|
|
521
|
-
placeholder="1.5"
|
|
522
|
-
value={usingSlippageInput ? slippage : ""}
|
|
523
|
-
onChange={(e) => {
|
|
524
|
-
if (e.target.value.length > 0) {
|
|
525
|
-
if (NUMBER_INPUT_REGEX.test(e.target.value)) {
|
|
526
|
-
setUsingSlippageInput(true);
|
|
527
|
-
setSlippage(Number(e.target.value));
|
|
528
|
-
}
|
|
529
|
-
} else {
|
|
530
|
-
setUsingSlippageInput(false);
|
|
531
|
-
setSlippageActivePresetIndex(1);
|
|
532
|
-
}
|
|
533
|
-
}}
|
|
534
|
-
/>
|
|
535
|
-
<PercentageIcon />
|
|
536
|
-
</div>
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
|
|
540
|
-
<div className="flex gap-2 items-center">
|
|
541
|
-
<div className=" text-[rgba(255,255,255,0.6)]">
|
|
542
|
-
Express delivery:
|
|
543
|
-
</div>
|
|
544
|
-
<div>
|
|
545
|
-
<span
|
|
546
|
-
className="inline-flex w-14 h-9 p-3 relative align-middle box-border overflow-hidden cursor-pointer"
|
|
547
|
-
onClick={() => {
|
|
548
|
-
setExpressChecked((x) => !x);
|
|
549
|
-
}}
|
|
550
|
-
>
|
|
551
|
-
<span className="h-full, w-full rounded-lg bg-[rgba(255,255,255,0.3)]"></span>
|
|
552
|
-
<span
|
|
553
|
-
className={`transition-all w-5 h-5 rounded-full absolute translate-y-[-4px]
|
|
554
|
-
${
|
|
555
|
-
expressChecked ? "translate-x-[12px] bg-x_blue" : "bg-white"
|
|
556
|
-
} `}
|
|
557
|
-
></span>
|
|
558
|
-
</span>
|
|
559
|
-
</div>
|
|
560
|
-
</div>
|
|
561
|
-
</div>
|
|
562
|
-
<button disabled={!signer || !route || isGettingRoute}>Submit</button>
|
|
563
245
|
</div>
|
|
564
246
|
)}
|
|
565
247
|
</form>
|