@xswap-link/sdk 0.1.1 → 0.1.3
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/CHANGELOG.md +20 -0
- package/dist/index.css +98 -39
- package/dist/index.d.mts +62 -157
- package/dist/index.d.ts +62 -157
- package/dist/index.js +962 -1923
- package/dist/index.mjs +919 -1887
- package/package.json +1 -1
- package/src/components/Skeleton/index.tsx +8 -5
- package/src/components/TxConfigForm/FeesDetails.tsx +5 -5
- package/src/components/TxConfigForm/Form.tsx +20 -14
- package/src/components/TxConfigForm/History.tsx +50 -101
- package/src/components/TxConfigForm/HistoryCard.tsx +22 -42
- package/src/components/TxConfigForm/Summary.tsx +57 -28
- package/src/components/TxConfigForm/SwapPanel.tsx +70 -77
- package/src/components/TxConfigForm/TokenPicker.tsx +69 -66
- package/src/components/TxConfigForm/TopBar.tsx +19 -19
- package/src/components/TxConfigForm/UsdPrice.tsx +41 -0
- package/src/components/TxConfigForm/index.tsx +1 -2
- package/src/components/TxHistoryButton/index.tsx +150 -0
- package/src/components/icons/ArrowLeftIcon.tsx +16 -0
- package/src/components/icons/ChevronUpIcon.tsx +1 -6
- package/src/components/icons/CircularProgressIcon.tsx +13 -15
- package/src/components/icons/XSwapLogo.tsx +98 -0
- package/src/components/icons/index.ts +2 -0
- package/src/components/index.ts +1 -0
- package/src/constants/index.ts +3 -2
- package/src/contracts/abi/index.ts +0 -2
- package/src/contracts/addresses.ts +6 -0
- package/src/models/Addresses.ts +1 -0
- package/src/models/TransactionHistory.ts +44 -29
- package/src/services/integrations/transactions.ts +166 -1
- package/src/utils/index.ts +7 -0
- package/tailwind.config.js +9 -0
- package/tsconfig.json +3 -7
- package/src/contracts/abi/XSwapRouter.json +0 -1242
|
@@ -4,10 +4,9 @@ import { DownArrowIcon } from "../icons";
|
|
|
4
4
|
import { Token, Chain, TokenBalances, TokenPrices } from "@src/models";
|
|
5
5
|
import { TokenPicker } from "./TokenPicker";
|
|
6
6
|
import { weiToHumanReadable } from "@src/utils";
|
|
7
|
-
import { Alert } from "../Alert";
|
|
8
7
|
import { ChainListElement } from "./ChainListElement";
|
|
9
8
|
import { BalanceComponent } from "./BalanceComponent";
|
|
10
|
-
import
|
|
9
|
+
import { UsdPrice } from "@src/components/TxConfigForm/UsdPrice";
|
|
11
10
|
|
|
12
11
|
interface Props {
|
|
13
12
|
amount: string;
|
|
@@ -82,10 +81,12 @@ export const SwapPanel: FC<Props> = ({
|
|
|
82
81
|
);
|
|
83
82
|
|
|
84
83
|
return (
|
|
85
|
-
<div className="flex
|
|
86
|
-
<div className="
|
|
87
|
-
<div className="flex flex-col gap-
|
|
88
|
-
<p className="text-white opacity-60 text-sm font-medium">
|
|
84
|
+
<div className="flex justify-between globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4">
|
|
85
|
+
<div className="flex flex-col justify-between gap-2 overflow-hidden w-1/2">
|
|
86
|
+
<div className="flex flex-col gap-1">
|
|
87
|
+
<p className="text-white opacity-60 text-xs sm:text-sm font-medium">
|
|
88
|
+
You pay
|
|
89
|
+
</p>
|
|
89
90
|
<input
|
|
90
91
|
className="p-0 border-none outline-none bg-transparent text-2xl overflow-ellipsis"
|
|
91
92
|
value={amount}
|
|
@@ -102,80 +103,41 @@ export const SwapPanel: FC<Props> = ({
|
|
|
102
103
|
placeholder="0"
|
|
103
104
|
/>
|
|
104
105
|
</div>
|
|
105
|
-
<div className="flex
|
|
106
|
-
<
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
)}
|
|
106
|
+
<div className="flex items-center text-sm text-[rgba(255,255,255,0.6)]">
|
|
107
|
+
<UsdPrice
|
|
108
|
+
prices={prices}
|
|
109
|
+
token={srcToken}
|
|
110
|
+
amount={amount}
|
|
111
|
+
loading={false}
|
|
112
|
+
/>
|
|
163
113
|
</div>
|
|
164
114
|
</div>
|
|
115
|
+
<div className="flex flex-col relative items-end gap-2.5 w-1/2 text-xs">
|
|
116
|
+
<div
|
|
117
|
+
className="flex gap-2 items-center p-2 max-w-full cursor-pointer bg-black text-white globalBorder rounded-3xl whitespace-nowrap"
|
|
118
|
+
onClick={() => {
|
|
119
|
+
setTokenListShown((state) => !state);
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
<img
|
|
123
|
+
height={16}
|
|
124
|
+
width={16}
|
|
125
|
+
src={srcToken?.image}
|
|
126
|
+
alt={srcToken?.name}
|
|
127
|
+
/>
|
|
128
|
+
<div>{srcToken?.symbol}</div>
|
|
129
|
+
<DownArrowIcon />
|
|
130
|
+
</div>
|
|
165
131
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
<Alert desc="Unknown price" />
|
|
176
|
-
)
|
|
177
|
-
) : (
|
|
178
|
-
<>$0.00</>
|
|
132
|
+
{tokenListShown && (
|
|
133
|
+
<TokenPicker
|
|
134
|
+
onCloseClick={() => setTokenListShown(false)}
|
|
135
|
+
tokens={srcChain?.tokens}
|
|
136
|
+
signer={signer}
|
|
137
|
+
selectedToken={srcToken}
|
|
138
|
+
setSelectedToken={(token) => setSrcToken(token)}
|
|
139
|
+
balances={balances}
|
|
140
|
+
/>
|
|
179
141
|
)}
|
|
180
142
|
<div
|
|
181
143
|
onClick={handleMaxClick}
|
|
@@ -185,6 +147,37 @@ export const SwapPanel: FC<Props> = ({
|
|
|
185
147
|
<BalanceComponent balances={balances} srcToken={srcToken} />
|
|
186
148
|
)}
|
|
187
149
|
</div>
|
|
150
|
+
<div
|
|
151
|
+
ref={buttonRef}
|
|
152
|
+
className="bg-black globalBorder rounded-2xl whitespace-nowrap flex items-center p-2 cursor-pointer gap-2"
|
|
153
|
+
onClick={() => setChainListShown((prev) => !prev)}
|
|
154
|
+
>
|
|
155
|
+
<img
|
|
156
|
+
width="16"
|
|
157
|
+
height="16"
|
|
158
|
+
src={srcChain?.image}
|
|
159
|
+
alt={srcChain?.name}
|
|
160
|
+
/>
|
|
161
|
+
<div>{srcChain?.displayName}</div>
|
|
162
|
+
<DownArrowIcon />
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
{chainListShown && (
|
|
166
|
+
<ul
|
|
167
|
+
ref={listRef}
|
|
168
|
+
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"
|
|
169
|
+
>
|
|
170
|
+
{chainListOptions.map((chain, index) => (
|
|
171
|
+
<ChainListElement
|
|
172
|
+
index={index}
|
|
173
|
+
length={supportedChains.length}
|
|
174
|
+
setSrcChain={setSrcChain}
|
|
175
|
+
setChainListShown={setChainListShown}
|
|
176
|
+
chain={chain}
|
|
177
|
+
/>
|
|
178
|
+
))}
|
|
179
|
+
</ul>
|
|
180
|
+
)}
|
|
188
181
|
</div>
|
|
189
182
|
</div>
|
|
190
183
|
);
|
|
@@ -24,7 +24,7 @@ export const TokenPicker: FC<Props> = ({
|
|
|
24
24
|
}) => {
|
|
25
25
|
const [searchValue, setSearchValue] = useState("");
|
|
26
26
|
|
|
27
|
-
const modalRoot = document.querySelector("
|
|
27
|
+
const modalRoot = document.querySelector("#xswap-modal");
|
|
28
28
|
|
|
29
29
|
const onBackdropClick = (e: MouseEvent<HTMLDivElement>) => {
|
|
30
30
|
e.stopPropagation();
|
|
@@ -37,12 +37,24 @@ export const TokenPicker: FC<Props> = ({
|
|
|
37
37
|
const filteredTokens = useMemo(() => {
|
|
38
38
|
const isMatch = (value: string, searchValue: string) =>
|
|
39
39
|
value.toLowerCase().indexOf(searchValue.toLowerCase()) > -1;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
40
|
+
|
|
41
|
+
return tokens
|
|
42
|
+
?.filter(
|
|
43
|
+
(token) =>
|
|
44
|
+
isMatch(token.symbol, searchValue) ||
|
|
45
|
+
isMatch(token.name, searchValue) ||
|
|
46
|
+
token.address === searchValue.toLowerCase(),
|
|
47
|
+
)
|
|
48
|
+
.sort((a, b) =>
|
|
49
|
+
a.priority < b.priority
|
|
50
|
+
? 1
|
|
51
|
+
: balances &&
|
|
52
|
+
balances[a.address] &&
|
|
53
|
+
balances[b.address] &&
|
|
54
|
+
balances[a.address]?.lte(balances[b.address] || "0")
|
|
55
|
+
? 1
|
|
56
|
+
: -1,
|
|
57
|
+
);
|
|
46
58
|
}, [searchValue, tokens]);
|
|
47
59
|
|
|
48
60
|
return modalRoot
|
|
@@ -94,69 +106,60 @@ export const TokenPicker: FC<Props> = ({
|
|
|
94
106
|
<div className="h-[2px] my-0 mx-[-24px] w-[100%+48px] px-12 bg-[rgba(255,255,255,0.1)]" />
|
|
95
107
|
<div className="overflow-y-scroll h-full mx-[-24px] w-[100%+48px] flex flex-col">
|
|
96
108
|
{filteredTokens && filteredTokens.length > 0 ? (
|
|
97
|
-
filteredTokens
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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)}
|
|
109
|
+
filteredTokens.map((token, index) => (
|
|
110
|
+
<div
|
|
111
|
+
key={`${index}_${token.address}`}
|
|
112
|
+
className={`flex gap-3 py-2 px-[24px] w-full items-center hover:bg-[rgb(25,25,25)] hover:cursor-pointer ${
|
|
113
|
+
token.address === selectedToken?.address
|
|
114
|
+
? "bg-[rgb(35,35,35)]"
|
|
115
|
+
: ""
|
|
116
|
+
}`}
|
|
117
|
+
onClick={() => {
|
|
118
|
+
setSelectedToken(token);
|
|
119
|
+
onCloseClick();
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{token.image ? (
|
|
123
|
+
<img
|
|
124
|
+
src={token.image}
|
|
125
|
+
alt={token.name}
|
|
126
|
+
className="token-picker__all__logo"
|
|
127
|
+
/>
|
|
128
|
+
) : (
|
|
129
|
+
<div className="flex items-center justify-center w-9 h-9 text-[15px] ml-1 rounded-full bg-[#272e40] text-[#e0e7fa]">
|
|
130
|
+
{token?.symbol.substring(0, 1)}
|
|
131
|
+
</div>
|
|
132
|
+
)}
|
|
133
|
+
<div className="flex justify-between items-center gap-1 overflow-hidden grow">
|
|
134
|
+
<div className="flex flex-col leading-[16px]">
|
|
135
|
+
<div className="overflow-hidden whitespace-nowrap text-ellipsis text-[15px]">
|
|
136
|
+
{token.name}
|
|
128
137
|
</div>
|
|
129
|
-
|
|
130
|
-
|
|
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 className="text-[#888] text-[11px]">
|
|
139
|
+
{token.symbol}
|
|
138
140
|
</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
141
|
</div>
|
|
142
|
+
{signer && (
|
|
143
|
+
<>
|
|
144
|
+
{balances &&
|
|
145
|
+
balances[token.address] &&
|
|
146
|
+
token.decimals ? (
|
|
147
|
+
<div className="text-xs">
|
|
148
|
+
{weiToHumanReadable({
|
|
149
|
+
amount:
|
|
150
|
+
balances[token.address]?.toString() || "0",
|
|
151
|
+
decimals: token.decimals,
|
|
152
|
+
precisionFractionalPlaces: 4,
|
|
153
|
+
})}
|
|
154
|
+
</div>
|
|
155
|
+
) : (
|
|
156
|
+
<Skeleton width="w-12" height="h-3" />
|
|
157
|
+
)}
|
|
158
|
+
</>
|
|
159
|
+
)}
|
|
158
160
|
</div>
|
|
159
|
-
|
|
161
|
+
</div>
|
|
162
|
+
))
|
|
160
163
|
) : (
|
|
161
164
|
<div className="mt-4 flex justify-center">
|
|
162
165
|
<p className="text-sm text-white">No tokens found.</p>
|
|
@@ -16,9 +16,11 @@ export const TopBar: FC<Props> = ({
|
|
|
16
16
|
historyTabShown,
|
|
17
17
|
}) => {
|
|
18
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
|
|
19
|
+
<div className="flex w-full justify-between items-center mx-auto sm:pl-4 pr-8 pt-2">
|
|
20
|
+
<div className="rounded-lg text-[rgba(255,255,255,0.6)] text-xs sm:text-base whitespace-nowrap">
|
|
21
|
+
{signer
|
|
22
|
+
? `Connected wallet: ${shortAddress(signer)}`
|
|
23
|
+
: `Wallet disconnected`}
|
|
22
24
|
</div>
|
|
23
25
|
<div className="flex gap-2">
|
|
24
26
|
{!historyTabShown && (
|
|
@@ -31,22 +33,20 @@ export const TopBar: FC<Props> = ({
|
|
|
31
33
|
</div>
|
|
32
34
|
</div>
|
|
33
35
|
)}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
<
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
</div>
|
|
49
|
-
)}
|
|
36
|
+
<div
|
|
37
|
+
onClick={() => setHistoryTabShown((x) => !x)}
|
|
38
|
+
className="flex items-center text-sm gap-1 cursor-pointer"
|
|
39
|
+
>
|
|
40
|
+
{!historyTabShown ? (
|
|
41
|
+
<>
|
|
42
|
+
<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">
|
|
43
|
+
<HistoryIcon />
|
|
44
|
+
</div>
|
|
45
|
+
</>
|
|
46
|
+
) : (
|
|
47
|
+
<div>Back</div>
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
50
|
</div>
|
|
51
51
|
</div>
|
|
52
52
|
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Alert, Skeleton } from "@src/components";
|
|
2
|
+
import { Token, TokenPrices } from "@src/models";
|
|
3
|
+
import { FC, useEffect, useMemo, useState } from "react";
|
|
4
|
+
|
|
5
|
+
type Props = {
|
|
6
|
+
prices: TokenPrices | undefined;
|
|
7
|
+
token: Token | undefined;
|
|
8
|
+
amount: string | undefined;
|
|
9
|
+
loading: boolean;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const UsdPrice: FC<Props> = ({
|
|
13
|
+
prices,
|
|
14
|
+
token,
|
|
15
|
+
amount = "0",
|
|
16
|
+
loading = false,
|
|
17
|
+
}) => {
|
|
18
|
+
const [usdPrice, setUsdPrice] = useState<string>("0.00");
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (token && prices && prices[token.address]) {
|
|
22
|
+
setUsdPrice((Number(amount) * Number(prices[token.address])).toFixed(2));
|
|
23
|
+
}
|
|
24
|
+
}, [token, prices, amount]);
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="opacity-60 py-4">
|
|
28
|
+
{loading ? (
|
|
29
|
+
<Skeleton width="w-12" height="h-4" />
|
|
30
|
+
) : token ? (
|
|
31
|
+
prices && prices[token.address] ? (
|
|
32
|
+
<div>{`$${usdPrice}`}</div>
|
|
33
|
+
) : (
|
|
34
|
+
<Alert desc="Unknown price" />
|
|
35
|
+
)
|
|
36
|
+
) : (
|
|
37
|
+
<>$0.00</>
|
|
38
|
+
)}
|
|
39
|
+
</div>
|
|
40
|
+
);
|
|
41
|
+
};
|
|
@@ -114,8 +114,7 @@ export const openTxConfigForm = async ({
|
|
|
114
114
|
let txConfigFormRoot: Root;
|
|
115
115
|
if (typeof document !== "undefined") {
|
|
116
116
|
const xswapElement = document.createElement("div");
|
|
117
|
-
xswapElement.setAttribute("id", "
|
|
118
|
-
xswapElement.classList.add("xswap");
|
|
117
|
+
xswapElement.setAttribute("id", "xswap-modal");
|
|
119
118
|
document.body.appendChild(xswapElement);
|
|
120
119
|
txConfigFormRoot = createRoot(xswapElement);
|
|
121
120
|
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { FC, useState } from "react";
|
|
2
|
+
import { createRoot } from "react-dom/client";
|
|
3
|
+
import {
|
|
4
|
+
ArrowLeftIcon,
|
|
5
|
+
ArrowRightIcon,
|
|
6
|
+
CheckIcon,
|
|
7
|
+
CircularProgressIcon,
|
|
8
|
+
} from "../icons";
|
|
9
|
+
import { ArrowUpRightIcon, XSwapLogo } from "../icons";
|
|
10
|
+
import { MonitoredTransaction } from "@src/models";
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
transaction: MonitoredTransaction;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const TxHistoryButton: FC<Props> = ({ transaction }) => {
|
|
17
|
+
const {
|
|
18
|
+
fromChain,
|
|
19
|
+
fromChainImage,
|
|
20
|
+
fromToken,
|
|
21
|
+
fromTokenImage,
|
|
22
|
+
fromAmount,
|
|
23
|
+
toChain,
|
|
24
|
+
toChainImage,
|
|
25
|
+
isDone,
|
|
26
|
+
txHash,
|
|
27
|
+
explorer,
|
|
28
|
+
} = transaction;
|
|
29
|
+
const [isWide, setIsWide] = useState(false);
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex gap-2" onClick={() => setIsWide((x: boolean) => !x)}>
|
|
32
|
+
{isWide && (
|
|
33
|
+
<div
|
|
34
|
+
className={`flex items-center min-w-60 sm:w-[520px] text-white h-14 px-4 text-sm gap-3 rounded-xl bg-gradient-to-r ${
|
|
35
|
+
isDone
|
|
36
|
+
? "from-[rgba(76,175,80,0.1)] to-[rgba(46,125,50,0.1)] border border-solid border-x_green_dark"
|
|
37
|
+
: "from-[rgba(54,129,198,0.1)] to-[rgba(43,74,157,0.1)] border border-solid border-x_blue_dark"
|
|
38
|
+
}`}
|
|
39
|
+
>
|
|
40
|
+
<div className="flex justify-between flex-col gap-2 sm:gap-5 sm:flex-row w-full">
|
|
41
|
+
<div className="flex items-center gap-2">
|
|
42
|
+
<img
|
|
43
|
+
className="w-5 h-5"
|
|
44
|
+
src={fromChainImage}
|
|
45
|
+
alt="source chain"
|
|
46
|
+
/>
|
|
47
|
+
<div>{fromChain}</div>
|
|
48
|
+
<div className="w-3.5 h-3.5">
|
|
49
|
+
<ArrowRightIcon />
|
|
50
|
+
</div>
|
|
51
|
+
<img className="w-5 h-5" src={toChainImage} alt="to chain" />
|
|
52
|
+
<div>{toChain}</div>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<div className="flex items-center gap-2">
|
|
56
|
+
<div>Sent</div>
|
|
57
|
+
<div>{fromAmount}</div>
|
|
58
|
+
<div>{fromToken}</div>
|
|
59
|
+
<img
|
|
60
|
+
className="w-5 h-5"
|
|
61
|
+
src={fromTokenImage}
|
|
62
|
+
alt="source token"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
<a
|
|
67
|
+
href={explorer}
|
|
68
|
+
target="_blank"
|
|
69
|
+
rel="noreferrer"
|
|
70
|
+
className="no-underline cursor-pointer"
|
|
71
|
+
aria-label="Show the transaction in the chain explorer"
|
|
72
|
+
>
|
|
73
|
+
<div className="w-3.5 h-3.5">
|
|
74
|
+
<ArrowUpRightIcon />
|
|
75
|
+
</div>
|
|
76
|
+
</a>
|
|
77
|
+
</div>
|
|
78
|
+
)}
|
|
79
|
+
<div
|
|
80
|
+
className={`text-white rounded-xl cursor-pointer bg-gradient-to-r
|
|
81
|
+
${
|
|
82
|
+
isDone
|
|
83
|
+
? "from-[rgba(76,175,80,0.1)] to-[rgba(46,125,50,0.1)] border border-solid border-x_green_dark"
|
|
84
|
+
: "from-[rgba(54,129,198,0.1)] to-[rgba(43,74,157,0.1)] border border-solid border-x_blue_dark"
|
|
85
|
+
}`}
|
|
86
|
+
>
|
|
87
|
+
<div className="flex gap-2 items-center h-14 w-20 justify-center">
|
|
88
|
+
<ArrowLeftIcon />
|
|
89
|
+
<div className="relative flex items-center justify-center w-9 h-9">
|
|
90
|
+
{isDone ? (
|
|
91
|
+
<div className="text-x_green w-5 h-5">
|
|
92
|
+
<CheckIcon />
|
|
93
|
+
</div>
|
|
94
|
+
) : (
|
|
95
|
+
<>
|
|
96
|
+
<div className="w-5 h-5">
|
|
97
|
+
<XSwapLogo />
|
|
98
|
+
</div>
|
|
99
|
+
<div className="absolute flex items-center justify-center top-0 left-0 right-0 bottom-0 text-white">
|
|
100
|
+
<CircularProgressIcon />
|
|
101
|
+
</div>
|
|
102
|
+
</>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
let renderedTransactions: MonitoredTransaction[] = [];
|
|
112
|
+
|
|
113
|
+
export const addTransactionToRenderedTransactions = (
|
|
114
|
+
transaction: MonitoredTransaction,
|
|
115
|
+
) => {
|
|
116
|
+
if (!renderedTransactions.some(({ txHash }) => txHash === transaction.txHash))
|
|
117
|
+
renderedTransactions.push(transaction);
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const removeTransactionFromRenderedTransactions = (txHash: string) => {
|
|
121
|
+
const filteredRenderedTransactions = renderedTransactions.filter(
|
|
122
|
+
(item) => item.txHash !== txHash,
|
|
123
|
+
);
|
|
124
|
+
renderedTransactions = [...filteredRenderedTransactions];
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export const updateTransactionDoneInRenderedTransactions = (txHash: string) => {
|
|
128
|
+
const transaction = renderedTransactions.find(
|
|
129
|
+
(item) => item.txHash === txHash,
|
|
130
|
+
);
|
|
131
|
+
if (transaction) {
|
|
132
|
+
transaction.isDone = true;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const historyElement = document.createElement("div");
|
|
137
|
+
historyElement.setAttribute("id", "xswap-history");
|
|
138
|
+
historyElement.setAttribute(
|
|
139
|
+
"class",
|
|
140
|
+
"absolute bottom-5 right-2 flex flex-col gap-3 pl-9 items-end w-full z-50",
|
|
141
|
+
);
|
|
142
|
+
document.body.appendChild(historyElement);
|
|
143
|
+
const txHistoryButtonRoot = createRoot(historyElement);
|
|
144
|
+
|
|
145
|
+
export const renderTxHistoryButtons = () => {
|
|
146
|
+
const buttons = renderedTransactions.map((item) => (
|
|
147
|
+
<TxHistoryButton key={item.txHash} transaction={item} />
|
|
148
|
+
));
|
|
149
|
+
txHistoryButtonRoot.render(buttons);
|
|
150
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const ArrowLeftIcon = () => {
|
|
2
|
+
return (
|
|
3
|
+
<svg
|
|
4
|
+
width="10"
|
|
5
|
+
height="16"
|
|
6
|
+
viewBox="0 0 10 16"
|
|
7
|
+
fill="none"
|
|
8
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
9
|
+
>
|
|
10
|
+
<path
|
|
11
|
+
d="M0.5 8L8.13251 15.5L9.5 14.1562L3.20318 8L9.4682 1.84375L8.10071 0.5L0.5 8Z"
|
|
12
|
+
fill="white"
|
|
13
|
+
/>
|
|
14
|
+
</svg>
|
|
15
|
+
);
|
|
16
|
+
};
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
export const ChevronUpIcon = () => {
|
|
2
2
|
return (
|
|
3
|
-
<svg
|
|
4
|
-
height="12"
|
|
5
|
-
width="12"
|
|
6
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
-
viewBox="0 0 512 512"
|
|
8
|
-
>
|
|
3
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
|
|
9
4
|
<path
|
|
10
5
|
fill="currentColor"
|
|
11
6
|
d="M233.4 105.4c12.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 173.3 86.6 342.6c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3l192-192z"
|
|
@@ -1,24 +1,22 @@
|
|
|
1
1
|
export const CircularProgressIcon = () => {
|
|
2
2
|
return (
|
|
3
3
|
<svg
|
|
4
|
-
className="animate-spin
|
|
5
|
-
|
|
4
|
+
className="animate-spin-slow text-white"
|
|
5
|
+
width="33"
|
|
6
|
+
height="33"
|
|
7
|
+
viewBox="0 0 33 33"
|
|
6
8
|
fill="none"
|
|
7
|
-
|
|
9
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
10
|
>
|
|
9
11
|
<circle
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
strokeWidth="
|
|
16
|
-
|
|
17
|
-
<path
|
|
18
|
-
className="opacity-75"
|
|
19
|
-
fill="currentColor"
|
|
20
|
-
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
|
21
|
-
></path>
|
|
12
|
+
cx="16.5"
|
|
13
|
+
cy="16.5"
|
|
14
|
+
r="15.5"
|
|
15
|
+
stroke="white"
|
|
16
|
+
strokeOpacity="0.2"
|
|
17
|
+
strokeWidth="2"
|
|
18
|
+
/>
|
|
19
|
+
<path d="M1 16C1 7.71573 7.71573 1 16 1" stroke="white" strokeWidth="2" />
|
|
22
20
|
</svg>
|
|
23
21
|
);
|
|
24
22
|
};
|