@xswap-link/sdk 0.0.13 → 0.1.1
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 +2 -1
- package/.github/workflows/publish.yml +2 -1
- package/.prettierrc +7 -0
- package/CHANGELOG.md +27 -1
- package/README.md +23 -14
- package/dist/index.css +1108 -0
- package/dist/index.d.mts +212 -52
- package/dist/index.d.ts +212 -52
- package/dist/index.js +2184 -85
- package/dist/index.mjs +2148 -76
- package/nodemon.json +5 -0
- package/package.json +20 -3
- package/postcss.config.js +3 -0
- package/src/components/Alert/index.tsx +9 -0
- package/src/components/Skeleton/index.tsx +10 -0
- 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 +250 -0
- package/src/components/TxConfigForm/History.tsx +155 -0
- package/src/components/TxConfigForm/HistoryCard.tsx +209 -0
- 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 +171 -0
- package/src/components/TxConfigForm/TopBar.tsx +53 -0
- package/src/components/TxConfigForm/index.tsx +121 -0
- package/src/components/global.css +34 -0
- package/src/components/icons/ArrowDownIcon.tsx +16 -0
- package/src/components/icons/ArrowRightIcon.tsx +11 -0
- package/src/components/icons/ArrowUpRightIcon.tsx +11 -0
- package/src/components/icons/ChainlinkCCIPIcon.tsx +23 -0
- package/src/components/icons/CheckIcon.tsx +11 -0
- package/src/components/icons/ChevronDownIcon.tsx +15 -0
- package/src/components/icons/ChevronUpIcon.tsx +15 -0
- package/src/components/icons/CircularProgressIcon.tsx +24 -0
- package/src/components/icons/CloseIcon.tsx +15 -0
- package/src/components/icons/CoinsIcon.tsx +17 -0
- package/src/components/icons/DownArrorIcon.tsx +17 -0
- package/src/components/icons/HistoryIcon.tsx +16 -0
- package/src/components/icons/HourGlassIcon.tsx +11 -0
- package/src/components/icons/InfoIcon.tsx +13 -0
- package/src/components/icons/PercentageIcon.tsx +29 -0
- package/src/components/icons/SearchIcon.tsx +15 -0
- package/src/components/icons/SettingsIcon.tsx +16 -0
- package/src/components/icons/TimerIcon.tsx +15 -0
- package/src/components/icons/XMarkIcon.tsx +11 -0
- package/src/components/icons/XSwapBadgeIcon.tsx +23 -0
- package/src/components/icons/index.ts +20 -0
- package/src/components/index.ts +3 -0
- package/src/config/index.ts +1 -0
- package/src/config/wagmiConfig.ts +45 -0
- package/src/constants/index.ts +9 -5
- package/src/contracts/abi/BatchQuery.json +52 -0
- package/src/contracts/abi/index.ts +2 -0
- package/src/contracts/addresses.ts +34 -0
- package/src/contracts/index.ts +1 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useDebounce.tsx +21 -0
- package/src/index.ts +6 -0
- package/src/models/Addresses.ts +12 -0
- package/src/models/Route.ts +17 -5
- package/src/models/TokenData.ts +45 -0
- package/src/models/TransactionHistory.ts +48 -0
- package/src/models/XSwapConfig.ts +3 -0
- package/src/models/forms/TxConfigFormData.ts +8 -0
- package/src/models/forms/index.ts +1 -0
- package/src/models/index.ts +6 -6
- package/src/models/integrations/GenerateStakingCallsParams.ts +8 -0
- package/src/models/integrations/index.ts +1 -0
- package/src/models/payloads/GetPricesPayload.ts +4 -0
- package/src/models/payloads/GetRoutePayload.ts +1 -3
- package/src/models/payloads/GetSwapTxPayload.ts +8 -0
- package/src/models/payloads/index.ts +2 -0
- package/src/services/api.ts +46 -64
- package/src/services/index.ts +1 -0
- package/src/services/integrations/customCalls/index.ts +1 -0
- package/src/services/integrations/customCalls/staking.ts +83 -0
- package/src/services/integrations/index.ts +2 -0
- package/src/services/integrations/transactions.ts +30 -0
- package/src/utils/contracts.ts +153 -11
- package/src/utils/index.ts +49 -1
- package/src/utils/numbers.ts +47 -0
- package/src/utils/strings.ts +6 -0
- package/tailwind.config.js +19 -0
- package/test/api.test.ts +1 -1
- package/tsconfig.json +10 -1
- package/xswap.config.ts +18 -0
- package/index.ts +0 -5
- package/src/models/ApiOverrides.ts +0 -3
- package/src/models/Chain.ts +0 -20
- package/src/models/Prices.ts +0 -9
- package/src/models/Token.ts +0 -10
- package/src/models/XSwapFee.ts +0 -4
- package/src/models/XSwapFees.ts +0 -7
- package/src/utils/bigNumbers.ts +0 -7
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { FC, useState, useEffect, useRef, useMemo } from "react";
|
|
2
|
+
import { NUMBER_INPUT_REGEX } from "@src/constants";
|
|
3
|
+
import { DownArrowIcon } from "../icons";
|
|
4
|
+
import { Token, Chain, TokenBalances, TokenPrices } from "@src/models";
|
|
5
|
+
import { TokenPicker } from "./TokenPicker";
|
|
6
|
+
import { weiToHumanReadable } from "@src/utils";
|
|
7
|
+
import { Alert } from "../Alert";
|
|
8
|
+
import { ChainListElement } from "./ChainListElement";
|
|
9
|
+
import { BalanceComponent } from "./BalanceComponent";
|
|
10
|
+
import BigNumber from "bignumber.js";
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
amount: string;
|
|
14
|
+
setAmount: React.Dispatch<React.SetStateAction<string>>;
|
|
15
|
+
srcToken: Token | undefined;
|
|
16
|
+
setSrcToken: React.Dispatch<React.SetStateAction<Token | undefined>>;
|
|
17
|
+
setSrcChain: React.Dispatch<React.SetStateAction<Chain | undefined>>;
|
|
18
|
+
srcChain: Chain | undefined;
|
|
19
|
+
signer: string | undefined;
|
|
20
|
+
balances: TokenBalances | undefined;
|
|
21
|
+
prices: TokenPrices | undefined;
|
|
22
|
+
supportedChains: Chain[];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const SwapPanel: FC<Props> = ({
|
|
26
|
+
amount,
|
|
27
|
+
setAmount,
|
|
28
|
+
srcToken,
|
|
29
|
+
setSrcToken,
|
|
30
|
+
srcChain,
|
|
31
|
+
setSrcChain,
|
|
32
|
+
signer,
|
|
33
|
+
balances,
|
|
34
|
+
prices,
|
|
35
|
+
supportedChains,
|
|
36
|
+
}) => {
|
|
37
|
+
const [chainListShown, setChainListShown] = useState(false);
|
|
38
|
+
const [tokenListShown, setTokenListShown] = useState(false);
|
|
39
|
+
const listRef = useRef(null);
|
|
40
|
+
const buttonRef = useRef(null);
|
|
41
|
+
|
|
42
|
+
const handleClickOutside = (event: MouseEvent) => {
|
|
43
|
+
if (listRef.current && buttonRef.current) {
|
|
44
|
+
const listElement = listRef.current as HTMLElement;
|
|
45
|
+
const buttonElement = buttonRef.current as HTMLElement;
|
|
46
|
+
if (
|
|
47
|
+
!listElement.contains(event.target as Node) &&
|
|
48
|
+
!buttonElement.contains(event.target as Node)
|
|
49
|
+
)
|
|
50
|
+
setChainListShown(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const handleMaxClick = () => {
|
|
55
|
+
if (
|
|
56
|
+
balances &&
|
|
57
|
+
srcToken &&
|
|
58
|
+
balances[srcToken.address] &&
|
|
59
|
+
balances[srcToken.address]?.toString() !== "0"
|
|
60
|
+
)
|
|
61
|
+
setAmount(
|
|
62
|
+
weiToHumanReadable({
|
|
63
|
+
amount: balances[srcToken.address]?.toString() || "0",
|
|
64
|
+
decimals: srcToken.decimals,
|
|
65
|
+
precisionFractionalPlaces: 4,
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
if (chainListShown) {
|
|
72
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
73
|
+
return () =>
|
|
74
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
75
|
+
}
|
|
76
|
+
}, [chainListShown]);
|
|
77
|
+
|
|
78
|
+
const chainListOptions = useMemo(
|
|
79
|
+
() =>
|
|
80
|
+
supportedChains.filter((chain) => chain.chainId !== srcChain?.chainId),
|
|
81
|
+
[supportedChains, srcChain],
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<div className="flex flex-col items-start globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4">
|
|
86
|
+
<div className="w-full flex gap-4 items-start justify-between mb-4">
|
|
87
|
+
<div className="flex flex-col gap-2 overflow-hidden w-1/2">
|
|
88
|
+
<p className="text-white opacity-60 text-sm font-medium">You pay</p>
|
|
89
|
+
<input
|
|
90
|
+
className="p-0 border-none outline-none bg-transparent text-2xl overflow-ellipsis"
|
|
91
|
+
value={amount}
|
|
92
|
+
onChange={(e) => {
|
|
93
|
+
if (e.target.value === ".") return;
|
|
94
|
+
if (NUMBER_INPUT_REGEX.test(e.target.value)) {
|
|
95
|
+
setAmount(e.target.value.replace(/,/g, "."));
|
|
96
|
+
}
|
|
97
|
+
}}
|
|
98
|
+
inputMode="decimal"
|
|
99
|
+
spellCheck="false"
|
|
100
|
+
autoComplete="off"
|
|
101
|
+
autoCorrect="off"
|
|
102
|
+
placeholder="0"
|
|
103
|
+
/>
|
|
104
|
+
</div>
|
|
105
|
+
<div className="flex flex-col relative items-end gap-2.5 w-1/2 text-xs">
|
|
106
|
+
<div
|
|
107
|
+
ref={buttonRef}
|
|
108
|
+
className="bg-black globalBorder rounded-2xl whitespace-nowrap flex items-center p-2 cursor-pointer gap-2"
|
|
109
|
+
onClick={() => setChainListShown((prev) => !prev)}
|
|
110
|
+
>
|
|
111
|
+
<img
|
|
112
|
+
width="16"
|
|
113
|
+
height="16"
|
|
114
|
+
src={srcChain?.image}
|
|
115
|
+
alt={srcChain?.name}
|
|
116
|
+
/>
|
|
117
|
+
<div>{srcChain?.displayName}</div>
|
|
118
|
+
<DownArrowIcon />
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{chainListShown && (
|
|
122
|
+
<ul
|
|
123
|
+
ref={listRef}
|
|
124
|
+
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"
|
|
125
|
+
>
|
|
126
|
+
{chainListOptions.map((chain, index) => (
|
|
127
|
+
<ChainListElement
|
|
128
|
+
index={index}
|
|
129
|
+
length={supportedChains.length}
|
|
130
|
+
setSrcChain={setSrcChain}
|
|
131
|
+
setChainListShown={setChainListShown}
|
|
132
|
+
chain={chain}
|
|
133
|
+
/>
|
|
134
|
+
))}
|
|
135
|
+
</ul>
|
|
136
|
+
)}
|
|
137
|
+
<div
|
|
138
|
+
className="flex gap-2 items-center p-2 max-w-full cursor-pointer bg-black text-white globalBorder rounded-3xl whitespace-nowrap"
|
|
139
|
+
onClick={() => {
|
|
140
|
+
setTokenListShown((state) => !state);
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
<img
|
|
144
|
+
height={16}
|
|
145
|
+
width={16}
|
|
146
|
+
src={srcToken?.image}
|
|
147
|
+
alt={srcToken?.name}
|
|
148
|
+
/>
|
|
149
|
+
<div>{srcToken?.symbol}</div>
|
|
150
|
+
<DownArrowIcon />
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
{tokenListShown && (
|
|
154
|
+
<TokenPicker
|
|
155
|
+
onCloseClick={() => setTokenListShown(false)}
|
|
156
|
+
tokens={srcChain?.tokens}
|
|
157
|
+
signer={signer}
|
|
158
|
+
selectedToken={srcToken}
|
|
159
|
+
setSelectedToken={(token) => setSrcToken(token)}
|
|
160
|
+
balances={balances}
|
|
161
|
+
/>
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<div className="flex justify-between items-center w-full text-sm text-[rgba(255,255,255,0.6)] relative">
|
|
167
|
+
{srcToken ? (
|
|
168
|
+
prices && prices[srcToken.address] ? (
|
|
169
|
+
<div>
|
|
170
|
+
{`$${(Number(amount) * Number(prices[srcToken.address])).toFixed(
|
|
171
|
+
2,
|
|
172
|
+
)}`}
|
|
173
|
+
</div>
|
|
174
|
+
) : (
|
|
175
|
+
<Alert desc="Unknown price" />
|
|
176
|
+
)
|
|
177
|
+
) : (
|
|
178
|
+
<>$0.00</>
|
|
179
|
+
)}
|
|
180
|
+
<div
|
|
181
|
+
onClick={handleMaxClick}
|
|
182
|
+
className="flex gap-1 items-center font-medium text-white opacity-60 py-0.5 cursor-pointer"
|
|
183
|
+
>
|
|
184
|
+
{signer && (
|
|
185
|
+
<BalanceComponent balances={balances} srcToken={srcToken} />
|
|
186
|
+
)}
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
</div>
|
|
190
|
+
);
|
|
191
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import React, { MouseEvent, FC, useState, useMemo } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
import { CloseIcon, SearchIcon } from "../icons";
|
|
4
|
+
import { Token, TokenBalances } from "@src/models";
|
|
5
|
+
import { weiToHumanReadable } from "@src/utils";
|
|
6
|
+
import { Skeleton } from "@src/components";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
onCloseClick: () => void;
|
|
10
|
+
tokens: Token[] | undefined;
|
|
11
|
+
selectedToken: Token | undefined;
|
|
12
|
+
setSelectedToken: (token: Token) => void;
|
|
13
|
+
signer: string | undefined;
|
|
14
|
+
balances: TokenBalances | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const TokenPicker: FC<Props> = ({
|
|
18
|
+
onCloseClick,
|
|
19
|
+
tokens,
|
|
20
|
+
selectedToken,
|
|
21
|
+
setSelectedToken,
|
|
22
|
+
signer,
|
|
23
|
+
balances,
|
|
24
|
+
}) => {
|
|
25
|
+
const [searchValue, setSearchValue] = useState("");
|
|
26
|
+
|
|
27
|
+
const modalRoot = document.querySelector(".xswap");
|
|
28
|
+
|
|
29
|
+
const onBackdropClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
30
|
+
e.stopPropagation();
|
|
31
|
+
e.nativeEvent.stopImmediatePropagation();
|
|
32
|
+
if (e.target === e.currentTarget) {
|
|
33
|
+
onCloseClick();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const filteredTokens = useMemo(() => {
|
|
38
|
+
const isMatch = (value: string, searchValue: string) =>
|
|
39
|
+
value.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
|
|
40
|
+
return tokens?.filter(
|
|
41
|
+
(token) =>
|
|
42
|
+
isMatch(token.symbol, searchValue) ||
|
|
43
|
+
isMatch(token.name, searchValue) ||
|
|
44
|
+
token.address === searchValue.toLowerCase(),
|
|
45
|
+
);
|
|
46
|
+
}, [searchValue, tokens]);
|
|
47
|
+
|
|
48
|
+
return modalRoot
|
|
49
|
+
? createPortal(
|
|
50
|
+
<div
|
|
51
|
+
onClick={onBackdropClick}
|
|
52
|
+
className="box-border fixed h-full w-full z-[999] top-0 left-0 bg-[rgba(0,0,0,0.8)] flex items-center justify-center p-5"
|
|
53
|
+
>
|
|
54
|
+
<div className="relative bg-black rounded-3xl p-6 max-h-[70%] flex flex-col text-base text-white w-[452px] h-[70%] border border-solid border-[rgba(255,255,255,0.2)]">
|
|
55
|
+
<div
|
|
56
|
+
onClick={onCloseClick}
|
|
57
|
+
className="absolute top-4 right-4 cursor-pointer"
|
|
58
|
+
>
|
|
59
|
+
<CloseIcon />
|
|
60
|
+
</div>
|
|
61
|
+
<p className="text-base mb-4">Pick a token</p>
|
|
62
|
+
<div className="flex items-center border border-solid border-[rgba(255,255,255,0.1)] rounded-lg py-0 px-3 gap-2 bg-[rgba(15,15,15,1)]">
|
|
63
|
+
<div className="w-6 h-6">
|
|
64
|
+
<SearchIcon />
|
|
65
|
+
</div>
|
|
66
|
+
<input
|
|
67
|
+
placeholder="Search name or paste address"
|
|
68
|
+
value={searchValue}
|
|
69
|
+
onChange={(e) => setSearchValue(e.target.value)}
|
|
70
|
+
className="relative h-[41px] leading-[41px] font-normal z-[1] w-full text-white bg-transparent border-none outline-none placeholder:text-sm"
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
<div className="my-4 mx-0 flex flex-wrap gap-3">
|
|
74
|
+
{tokens
|
|
75
|
+
?.filter((token) => token?.quickPick)
|
|
76
|
+
.map((token) => (
|
|
77
|
+
<div
|
|
78
|
+
key={token.address}
|
|
79
|
+
className={`flex gap-2 py-1 px-2 items-center bg-[rgb(15,15,15)] rounded-2xl border border-solid border-[rgba(255,255,255,0.1)] hover:bg-[rgb(25,25,25)] hover:cursor-pointer ${
|
|
80
|
+
token.address === selectedToken?.address
|
|
81
|
+
? "bg-[rgb(35, 35, 35)]"
|
|
82
|
+
: ""
|
|
83
|
+
}`}
|
|
84
|
+
onClick={() => {
|
|
85
|
+
setSelectedToken(token);
|
|
86
|
+
onCloseClick();
|
|
87
|
+
}}
|
|
88
|
+
>
|
|
89
|
+
<img src={token.image} alt={token.name} className="w-5" />
|
|
90
|
+
<div className="text-sm">{token.symbol}</div>
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</div>
|
|
94
|
+
<div className="h-[2px] my-0 mx-[-24px] w-[100%+48px] px-12 bg-[rgba(255,255,255,0.1)]" />
|
|
95
|
+
<div className="overflow-y-scroll h-full mx-[-24px] w-[100%+48px] flex flex-col">
|
|
96
|
+
{filteredTokens && filteredTokens.length > 0 ? (
|
|
97
|
+
filteredTokens
|
|
98
|
+
.sort((a, b) =>
|
|
99
|
+
balances &&
|
|
100
|
+
balances[a.address] &&
|
|
101
|
+
balances[b.address] &&
|
|
102
|
+
balances[a.address]?.lte(balances[b.address] || "0")
|
|
103
|
+
? 1
|
|
104
|
+
: -1,
|
|
105
|
+
)
|
|
106
|
+
.map((token, index) => (
|
|
107
|
+
<div
|
|
108
|
+
key={`${index}_${token.address}`}
|
|
109
|
+
className={`flex gap-3 py-2 px-[24px] w-full items-center hover:bg-[rgb(25,25,25)] hover:cursor-pointer ${
|
|
110
|
+
token.address === selectedToken?.address
|
|
111
|
+
? "bg-[rgb(35,35,35)]"
|
|
112
|
+
: ""
|
|
113
|
+
}`}
|
|
114
|
+
onClick={() => {
|
|
115
|
+
setSelectedToken(token);
|
|
116
|
+
onCloseClick();
|
|
117
|
+
}}
|
|
118
|
+
>
|
|
119
|
+
{token.image ? (
|
|
120
|
+
<img
|
|
121
|
+
src={token.image}
|
|
122
|
+
alt={token.name}
|
|
123
|
+
className="token-picker__all__logo"
|
|
124
|
+
/>
|
|
125
|
+
) : (
|
|
126
|
+
<div className="flex items-center justify-center w-9 h-9 text-[15px] ml-1 rounded-full bg-[#272e40] text-[#e0e7fa]">
|
|
127
|
+
{token?.symbol.substring(0, 1)}
|
|
128
|
+
</div>
|
|
129
|
+
)}
|
|
130
|
+
<div className="flex justify-between items-center gap-1 overflow-hidden grow">
|
|
131
|
+
<div className="flex flex-col leading-[16px]">
|
|
132
|
+
<div className="overflow-hidden whitespace-nowrap text-ellipsis text-[15px]">
|
|
133
|
+
{token.name}
|
|
134
|
+
</div>
|
|
135
|
+
<div className="text-[#888] text-[11px]">
|
|
136
|
+
{token.symbol}
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
{signer && (
|
|
140
|
+
<>
|
|
141
|
+
{balances &&
|
|
142
|
+
balances[token.address] &&
|
|
143
|
+
token.decimals ? (
|
|
144
|
+
<div className="text-xs">
|
|
145
|
+
{weiToHumanReadable({
|
|
146
|
+
amount:
|
|
147
|
+
balances[token.address]?.toString() || "0",
|
|
148
|
+
decimals: token.decimals,
|
|
149
|
+
precisionFractionalPlaces: 4,
|
|
150
|
+
})}
|
|
151
|
+
</div>
|
|
152
|
+
) : (
|
|
153
|
+
<Skeleton w="w-12" h="h-3" />
|
|
154
|
+
)}
|
|
155
|
+
</>
|
|
156
|
+
)}
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
))
|
|
160
|
+
) : (
|
|
161
|
+
<div className="mt-4 flex justify-center">
|
|
162
|
+
<p className="text-sm text-white">No tokens found.</p>
|
|
163
|
+
</div>
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
</div>
|
|
167
|
+
</div>,
|
|
168
|
+
modalRoot,
|
|
169
|
+
)
|
|
170
|
+
: null;
|
|
171
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { FC } from "react";
|
|
2
|
+
import { shortAddress } from "@src/utils";
|
|
3
|
+
import { SettingsIcon, HistoryIcon } from "../icons";
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
signer: string | undefined;
|
|
7
|
+
setSettingsShown: React.Dispatch<React.SetStateAction<boolean>>;
|
|
8
|
+
historyTabShown: boolean;
|
|
9
|
+
setHistoryTabShown: React.Dispatch<React.SetStateAction<boolean>>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const TopBar: FC<Props> = ({
|
|
13
|
+
signer,
|
|
14
|
+
setSettingsShown,
|
|
15
|
+
setHistoryTabShown,
|
|
16
|
+
historyTabShown,
|
|
17
|
+
}) => {
|
|
18
|
+
return (
|
|
19
|
+
<div className="flex w-full justify-between mx-auto pl-4 pr-8 pt-2">
|
|
20
|
+
<div className="rounded-lg text-[rgba(255,255,255,0.6)] text-base whitespace-nowrap">
|
|
21
|
+
{signer ? `Connected wallet: ${shortAddress(signer)}` : `Disconnected`}
|
|
22
|
+
</div>
|
|
23
|
+
<div className="flex gap-2">
|
|
24
|
+
{!historyTabShown && (
|
|
25
|
+
<div
|
|
26
|
+
onClick={() => setSettingsShown(true)}
|
|
27
|
+
className="flex items-center text-xs gap-1 cursor-pointer"
|
|
28
|
+
>
|
|
29
|
+
<div className="w-7 h-7 rounded-full bg-gradient-to-r from-x_blue_light to-x_blue_dark flex items-center justify-center">
|
|
30
|
+
<SettingsIcon />
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
{signer && (
|
|
35
|
+
<div
|
|
36
|
+
onClick={() => setHistoryTabShown((x) => !x)}
|
|
37
|
+
className="flex items-center text-sm gap-1 cursor-pointer"
|
|
38
|
+
>
|
|
39
|
+
{!historyTabShown ? (
|
|
40
|
+
<>
|
|
41
|
+
<div className="w-7 h-7 rounded-full bg-gradient-to-r from-x_blue_light to-x_blue_dark flex items-center justify-center">
|
|
42
|
+
<HistoryIcon />
|
|
43
|
+
</div>
|
|
44
|
+
</>
|
|
45
|
+
) : (
|
|
46
|
+
<div>Back</div>
|
|
47
|
+
)}
|
|
48
|
+
</div>
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
52
|
+
);
|
|
53
|
+
};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import "@src/global.css";
|
|
2
|
+
import { MouseEvent, useMemo } from "react";
|
|
3
|
+
import { createRoot, Root } from "react-dom/client";
|
|
4
|
+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
|
5
|
+
import { WagmiProvider } from "wagmi";
|
|
6
|
+
import { getWagmiConfig } from "@src/config";
|
|
7
|
+
import { Chain, ContractCall, Route } from "@src/models";
|
|
8
|
+
import { CloseIcon } from "@src/components/icons/CloseIcon";
|
|
9
|
+
import { Form } from "./Form";
|
|
10
|
+
import { ChainlinkCCIPIcon, XSwapBadgeIcon } from "../icons";
|
|
11
|
+
|
|
12
|
+
export type TxConfigFormProps = {
|
|
13
|
+
dstChainId: string;
|
|
14
|
+
dstTokenAddr: string;
|
|
15
|
+
customContractCalls: ContractCall[];
|
|
16
|
+
desc?: string;
|
|
17
|
+
supportedChains: Chain[];
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const TxConfigForm = ({
|
|
21
|
+
dstChainId,
|
|
22
|
+
dstTokenAddr,
|
|
23
|
+
customContractCalls,
|
|
24
|
+
desc,
|
|
25
|
+
supportedChains,
|
|
26
|
+
onSubmit,
|
|
27
|
+
onClose,
|
|
28
|
+
}: TxConfigFormProps & {
|
|
29
|
+
onSubmit: (route: Route) => void;
|
|
30
|
+
onClose: () => void;
|
|
31
|
+
}) => {
|
|
32
|
+
const onBackdropClick = (e: MouseEvent) => {
|
|
33
|
+
e.stopPropagation();
|
|
34
|
+
e.nativeEvent.stopImmediatePropagation();
|
|
35
|
+
if (e.target === e.currentTarget) {
|
|
36
|
+
onClose();
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const queryClient = new QueryClient();
|
|
41
|
+
const wagmiConfig = useMemo(
|
|
42
|
+
() => getWagmiConfig(supportedChains),
|
|
43
|
+
supportedChains,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<WagmiProvider config={wagmiConfig}>
|
|
48
|
+
<QueryClientProvider client={queryClient}>
|
|
49
|
+
<div
|
|
50
|
+
className="top-0 left-0 right-0 bottom-0 bg-[rgba(0,0,0,0.8)] fixed flex items-center justify-center z-10"
|
|
51
|
+
onClick={onBackdropClick}
|
|
52
|
+
>
|
|
53
|
+
<div className="relative bg-black rounded-3xl w-lg overflow-auto text-white border-2 border-solid border-[rgba(255,255,255,0.1)]">
|
|
54
|
+
<Form
|
|
55
|
+
dstChainId={dstChainId}
|
|
56
|
+
dstTokenAddr={dstTokenAddr}
|
|
57
|
+
customContractCalls={customContractCalls}
|
|
58
|
+
desc={desc}
|
|
59
|
+
supportedChains={supportedChains}
|
|
60
|
+
onSubmit={onSubmit}
|
|
61
|
+
/>
|
|
62
|
+
<div
|
|
63
|
+
className="absolute top-7 right-4 cursor-pointer text-white"
|
|
64
|
+
onClick={onClose}
|
|
65
|
+
>
|
|
66
|
+
<CloseIcon />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
<div className="swappage__poweredby">
|
|
70
|
+
<span>Powered by</span>
|
|
71
|
+
<XSwapBadgeIcon />
|
|
72
|
+
<span>✕</span>
|
|
73
|
+
<ChainlinkCCIPIcon />
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</QueryClientProvider>
|
|
78
|
+
</WagmiProvider>
|
|
79
|
+
);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const openTxConfigForm = async ({
|
|
83
|
+
dstChainId,
|
|
84
|
+
dstTokenAddr,
|
|
85
|
+
customContractCalls,
|
|
86
|
+
desc,
|
|
87
|
+
supportedChains,
|
|
88
|
+
}: TxConfigFormProps): Promise<Route> => {
|
|
89
|
+
try {
|
|
90
|
+
return await new Promise((resolve) => {
|
|
91
|
+
txConfigFormRoot.render(
|
|
92
|
+
<TxConfigForm
|
|
93
|
+
dstChainId={dstChainId}
|
|
94
|
+
dstTokenAddr={dstTokenAddr}
|
|
95
|
+
customContractCalls={customContractCalls}
|
|
96
|
+
desc={desc}
|
|
97
|
+
supportedChains={supportedChains}
|
|
98
|
+
onSubmit={(route) => {
|
|
99
|
+
resolve(route);
|
|
100
|
+
txConfigFormRoot.render("");
|
|
101
|
+
}}
|
|
102
|
+
onClose={() => {
|
|
103
|
+
txConfigFormRoot.render("");
|
|
104
|
+
}}
|
|
105
|
+
/>,
|
|
106
|
+
);
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
throw new Error(`XSwap component error, ${err}`);
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// skip on server-side integrations
|
|
114
|
+
let txConfigFormRoot: Root;
|
|
115
|
+
if (typeof document !== "undefined") {
|
|
116
|
+
const xswapElement = document.createElement("div");
|
|
117
|
+
xswapElement.setAttribute("id", "txConfigFormRoot");
|
|
118
|
+
xswapElement.classList.add("xswap");
|
|
119
|
+
document.body.appendChild(xswapElement);
|
|
120
|
+
txConfigFormRoot = createRoot(xswapElement);
|
|
121
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
@tailwind base;
|
|
2
|
+
@tailwind components;
|
|
3
|
+
@tailwind utilities;
|
|
4
|
+
|
|
5
|
+
.xswap {
|
|
6
|
+
/* Works on Firefox */
|
|
7
|
+
* {
|
|
8
|
+
scrollbar-width: thin;
|
|
9
|
+
scrollbar-color: #252525 #000;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/* Works on Chrome, Edge, and Safari */
|
|
13
|
+
*::-webkit-scrollbar {
|
|
14
|
+
width: 4px;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
*::-webkit-scrollbar-track {
|
|
18
|
+
background: #000;
|
|
19
|
+
border-radius: 4px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
*::-webkit-scrollbar-thumb {
|
|
23
|
+
background-color: #252525;
|
|
24
|
+
border-radius: 4px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
button {
|
|
28
|
+
@apply text-white border-none rounded-2xl text-xl py-4 cursor-pointer w-full bg-gradient-to-r from-[#3681c6] to-[#2b4a9d] disabled:opacity-25;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.globalBorder {
|
|
32
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const ArrowDownIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="20"
|
|
5
|
+
height="20"
|
|
6
|
+
viewBox="0 0 20 20"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
d="M12 2.45932L12 12.8957L15.8625 9.15023L18 11.1866L10.5 18.4593L3 11.1866L5.1375 9.15023L9 12.8957L9 2.45932L12 2.45932Z"
|
|
12
|
+
fill="white"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const ArrowRightIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
viewBox="0 0 448 512"
|
|
6
|
+
fill="currentColor"
|
|
7
|
+
>
|
|
8
|
+
<path d="M438.6 278.6c12.5-12.5 12.5-32.8 0-45.3l-160-160c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L338.8 224 32 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l306.7 0L233.4 393.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l160-160z" />
|
|
9
|
+
</svg>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const ArrowUpRightIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
viewBox="0 0 512 512"
|
|
6
|
+
fill="currentColor"
|
|
7
|
+
>
|
|
8
|
+
<path d="M320 0c-17.7 0-32 14.3-32 32s14.3 32 32 32h82.7L201.4 265.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L448 109.3V192c0 17.7 14.3 32 32 32s32-14.3 32-32V32c0-17.7-14.3-32-32-32H320zM80 32C35.8 32 0 67.8 0 112V432c0 44.2 35.8 80 80 80H400c44.2 0 80-35.8 80-80V320c0-17.7-14.3-32-32-32s-32 14.3-32 32V432c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V112c0-8.8 7.2-16 16-16H192c17.7 0 32-14.3 32-32s-14.3-32-32-32H80z" />
|
|
9
|
+
</svg>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const ChainlinkCCIPIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
version="1.2"
|
|
5
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
6
|
+
viewBox="0 0 67 27"
|
|
7
|
+
width="67"
|
|
8
|
+
height="27"
|
|
9
|
+
>
|
|
10
|
+
<title>chainlink-CCIP</title>
|
|
11
|
+
<defs>
|
|
12
|
+
<image
|
|
13
|
+
width="67"
|
|
14
|
+
height="27"
|
|
15
|
+
id="imgiVBORw0KGgoAAA"
|
|
16
|
+
href="data:image/png;base64,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"
|
|
17
|
+
/>
|
|
18
|
+
</defs>
|
|
19
|
+
<style></style>
|
|
20
|
+
<use id="Background" href="#imgiVBORw0KGgoAAA" x="0" y="0" />
|
|
21
|
+
</svg>
|
|
22
|
+
);
|
|
23
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const CheckIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
viewBox="0 0 448 512"
|
|
6
|
+
fill="currentColor"
|
|
7
|
+
>
|
|
8
|
+
<path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z" />
|
|
9
|
+
</svg>
|
|
10
|
+
);
|
|
11
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export const ChevronDownIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="12"
|
|
5
|
+
height="12"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
viewBox="0 0 512 512"
|
|
8
|
+
>
|
|
9
|
+
<path
|
|
10
|
+
fill="currentColor"
|
|
11
|
+
d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"
|
|
12
|
+
></path>
|
|
13
|
+
</svg>
|
|
14
|
+
);
|
|
15
|
+
};
|