@xswap-link/sdk 0.1.2 → 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.
Files changed (35) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/dist/index.css +92 -33
  3. package/dist/index.d.mts +60 -155
  4. package/dist/index.d.ts +60 -155
  5. package/dist/index.js +915 -1841
  6. package/dist/index.mjs +867 -1800
  7. package/package.json +1 -1
  8. package/src/components/Skeleton/index.tsx +8 -5
  9. package/src/components/TxConfigForm/FeesDetails.tsx +5 -5
  10. package/src/components/TxConfigForm/Form.tsx +10 -3
  11. package/src/components/TxConfigForm/History.tsx +1 -1
  12. package/src/components/TxConfigForm/HistoryCard.tsx +19 -40
  13. package/src/components/TxConfigForm/Summary.tsx +57 -28
  14. package/src/components/TxConfigForm/SwapPanel.tsx +70 -77
  15. package/src/components/TxConfigForm/TokenPicker.tsx +2 -2
  16. package/src/components/TxConfigForm/TopBar.tsx +2 -2
  17. package/src/components/TxConfigForm/UsdPrice.tsx +41 -0
  18. package/src/components/TxConfigForm/index.tsx +1 -2
  19. package/src/components/TxHistoryButton/index.tsx +150 -0
  20. package/src/components/icons/ArrowLeftIcon.tsx +16 -0
  21. package/src/components/icons/ChevronUpIcon.tsx +1 -6
  22. package/src/components/icons/CircularProgressIcon.tsx +13 -15
  23. package/src/components/icons/XSwapLogo.tsx +98 -0
  24. package/src/components/icons/index.ts +2 -0
  25. package/src/components/index.ts +1 -0
  26. package/src/constants/index.ts +1 -0
  27. package/src/contracts/abi/index.ts +0 -2
  28. package/src/contracts/addresses.ts +6 -0
  29. package/src/models/Addresses.ts +1 -0
  30. package/src/models/TransactionHistory.ts +44 -29
  31. package/src/services/integrations/transactions.ts +166 -1
  32. package/src/utils/index.ts +7 -0
  33. package/tailwind.config.js +6 -1
  34. package/tsconfig.json +3 -7
  35. package/src/contracts/abi/XSwapRouter.json +0 -1242
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -1,10 +1,13 @@
1
1
  import React from "react";
2
2
 
3
- export const Skeleton = ({ w, h }: Style) => {
4
- return <div className={`bg-current rounded animate-pulse ${w} ${h}`} />;
3
+ type Props = {
4
+ width: string;
5
+ height: string;
6
+ other?: string;
5
7
  };
6
8
 
7
- type Style = {
8
- w: string;
9
- h: string;
9
+ export const Skeleton = ({ width, height }: Props) => {
10
+ return (
11
+ <div className={`bg-current rounded animate-pulse ${width} ${height}`} />
12
+ );
10
13
  };
@@ -26,8 +26,8 @@ export const FeesDetails: FC<Props> = ({
26
26
  }) => {
27
27
  return (
28
28
  <div className="flex flex-col gap-3 globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4 mb-1">
29
- <div className="flex w-full items-center justify-between">
30
- <div className="flex items-center gap-1 text-sm font-medium text-white opacity-60">
29
+ <div className="flex w-full items-center justify-between text-xs sm:text-sm ">
30
+ <div className="flex items-center gap-1 font-medium text-white opacity-60">
31
31
  <CoinsIcon />
32
32
  <div className="mr-1">Fees:</div>
33
33
  {isGettingRoute ? (
@@ -47,9 +47,9 @@ export const FeesDetails: FC<Props> = ({
47
47
  })} ${paymentToken?.symbol}`
48
48
  )}
49
49
  </div>
50
- <div className="flex gap-1">
50
+ <div className="flex gap-1 items-center">
51
51
  <div
52
- className={`flex gap-1 text-xs font-medium ${
52
+ className={`flex gap-1 items-center font-medium ${
53
53
  expressChecked ? "text-x_green" : "text-white opacity-60"
54
54
  }`}
55
55
  >
@@ -58,7 +58,7 @@ export const FeesDetails: FC<Props> = ({
58
58
  </div>
59
59
  <div
60
60
  onClick={() => setFeesDetailsShown((x) => !x)}
61
- className="text-xs font-medium text-white opacity-60 cursor-pointer flex items-center"
61
+ className="font-medium text-white opacity-60 cursor-pointer flex items-center w-[15px] h-[9px]"
62
62
  >
63
63
  {feesDetailsShown ? <ChevronUpIcon /> : <ChevronDownIcon />}
64
64
  </div>
@@ -94,9 +94,13 @@ export const Form = ({
94
94
  }, ROUTE_TIMEOUT_MS);
95
95
  setIsGettingRoute(true);
96
96
  setFeesDetailsShown(false);
97
+ if (!srcToken.decimals)
98
+ throw new Error(
99
+ `Configuration error: selected token ($${srcToken.symbol} ${srcToken.name}) has unknown decimals param.`,
100
+ );
97
101
  getRoute({
98
102
  fromAmount: ethers.utils
99
- .parseUnits(debouncedAmount, srcToken?.decimals)
103
+ .parseUnits(debouncedAmount, srcToken.decimals)
100
104
  .toString(),
101
105
  fromAddress: signer || ADDRESSES[srcChain.chainId]!.FeeCollector,
102
106
  fromChain: srcChain.chainId,
@@ -214,7 +218,6 @@ export const Form = ({
214
218
  />
215
219
  <Summary
216
220
  isGettingRoute={isGettingRoute}
217
- amount={amount}
218
221
  route={route}
219
222
  dstToken={dstToken}
220
223
  dstChain={dstChain}
@@ -231,7 +234,11 @@ export const Form = ({
231
234
  />
232
235
  {formError.length > 0 && <ErrorField error={formError} />}
233
236
  {srcChain && srcChain.chainId !== account.chainId?.toString() ? (
234
- <button type="button" onClick={handleSwitchChain}>
237
+ <button
238
+ disabled={!signer}
239
+ type="button"
240
+ onClick={handleSwitchChain}
241
+ >
235
242
  Switch chain
236
243
  </button>
237
244
  ) : (
@@ -80,7 +80,7 @@ export const History: FC<Props> = ({ signer, supportedChains }) => {
80
80
 
81
81
  return (
82
82
  <div className="flex items-center justify-center w-full">
83
- <div className="w-full h-[30vh] overflow-scroll overflow-x-hidden">
83
+ <div className="w-full h-96 overflow-scroll overflow-x-hidden">
84
84
  {fetchedHistory?.length === 0 && historyLoadedOnce && (
85
85
  <div className="w-full text-center py-4 px-0 text-[rgb(158,158,158)]">
86
86
  Your history is empty...
@@ -1,5 +1,5 @@
1
1
  import { FC, useCallback, useMemo } from "react";
2
- import { format } from "date-fns";
2
+ import { getDate } from "@src/utils";
3
3
  import { Transaction, Chain, Token } from "@src/models";
4
4
  import { deepMergeObjects, weiToHumanReadable } from "@src/utils";
5
5
  import {
@@ -17,11 +17,6 @@ interface Props {
17
17
  }
18
18
 
19
19
  export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
20
- const getDate = useCallback((blockTimestamp: number) => {
21
- const date = new Date(blockTimestamp * 1000);
22
- return format(date, "d MMM yyyy HH:mm:ss");
23
- }, []);
24
-
25
20
  const date = getDate(transaction.timestamp);
26
21
 
27
22
  const supportedTokens: Record<string, Record<string, Token>> = useMemo(() => {
@@ -80,34 +75,25 @@ export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
80
75
  );
81
76
 
82
77
  return (
83
- <div className="flex flex-col border border-solid border-[rgba(255,255,255,0.1)] rounded-xl bg-[rgb(15,15,15)] p-2 gap-4 mb-2">
84
- <div className="flex items-center">
85
- <div className="text-[rgba(255,255,255,0.6)] text-xs">{date}</div>
86
- <div className="flex flex-grow justify-end items-center">
78
+ <div className="flex flex-col globalBorder rounded-xl bg-[rgb(15,15,15)] text-white text-xs sm:text-sm p-4 gap-3 mb-2">
79
+ <div className="flex w-full items-center justify-between">
80
+ <div className="text-sm sm:text-base">{date}</div>
81
+ <div className="flex items-center">
87
82
  {transaction.status === "IN_PROGRESS" && (
88
83
  <div className="flex items-center">
89
- <div className="w-3.5 h-3.5 mr-1 text-[rgb(250,200,100)]">
90
- <HourGlassIcon />
91
- </div>
92
- <div className="text-xs text-[rgb(250,200,100)]">In progress</div>
84
+ <div className="text-[rgb(250,200,100)]">In progress</div>
93
85
  </div>
94
86
  )}
95
87
 
96
88
  {transaction.status === "DONE" && (
97
89
  <div className="flex items-center">
98
- <div className="w-3.5 h-3.5 mr-1 text-[rgb(100,200,100)]">
99
- <CheckIcon />
100
- </div>
101
- <div className="text-xs text-[rgb(100,200,100)]">Done</div>
90
+ <div className="text-[rgb(100,200,100)]">Done</div>
102
91
  </div>
103
92
  )}
104
93
 
105
94
  {transaction.status === "REVERTED" && (
106
95
  <div className="flex items-center">
107
- <div className="w-4 h-4 mr-1 text-[rgb(255,100,100)]">
108
- <XMarkIcon />
109
- </div>
110
- <div className="text-xs text-[rgb(255,100,100)]">Reverted</div>
96
+ <div className="text-[rgb(255,100,100)]">Reverted</div>
111
97
  </div>
112
98
  )}
113
99
 
@@ -122,26 +108,24 @@ export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
122
108
  className="ml-2 no-underline"
123
109
  aria-label="Show the transaction in the chain explorer"
124
110
  >
125
- <div className="w-3.5 h-3.5 text-[rgba(255,255,255,0.6)]">
111
+ <div className="w-3.5 h-3.5">
126
112
  <ArrowUpRightIcon />
127
113
  </div>
128
114
  </a>
129
115
  </div>
130
116
  </div>
131
- <div className="flex flex-wrap gap-8">
132
- <div className="flex flex-grow items-center">
117
+ <div className="flex justify-between flex-wrap gap-2">
118
+ <div className="flex items-center">
133
119
  <div className="flex items-center">
134
120
  <img
135
121
  src={sourceChainData?.image}
136
122
  alt={`${sourceChainData?.name} logo`}
137
123
  className="w-5 h-5 mr-1"
138
124
  />
139
- <div className="text-xs text-[rgba(255,255,255,0.6)]">
140
- {sourceChainData?.displayName}
141
- </div>
125
+ <div>{sourceChainData?.displayName}</div>
142
126
  </div>
143
127
 
144
- <div className="w-3.5 h-3.5 text-[rgba(255,255,255,0.6)] my-0 mx-1.5">
128
+ <div className="w-3.5 h-3.5 my-0 mx-1.5">
145
129
  <ArrowRightIcon />
146
130
  </div>
147
131
 
@@ -151,20 +135,17 @@ export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
151
135
  alt={`${targetChainData?.name} logo`}
152
136
  className="w-5 h-5 mr-1"
153
137
  />
154
- <div className="text-xs text-[rgba(255,255,255,0.6)]">
155
- {targetChainData?.displayName}
156
- </div>
138
+ <div>{targetChainData?.displayName}</div>
157
139
  </div>
158
140
  </div>
141
+
159
142
  <div className="flex items-center mb-2 last:mb-0">
160
- <div className="flex flex-1 items-center flex-wrap text-xs text-[rgba(255,255,255,0.6)]">
143
+ <div className="flex items-center flex-wrap">
161
144
  <div className="flex items-center">
162
145
  {Number(transferredAmount) < MINIMUM_DISPLAYED_TOKEN_AMOUNT
163
146
  ? `<${MINIMUM_DISPLAYED_TOKEN_AMOUNT}`
164
147
  : transferredAmount}
165
- <div className="text-xs ml-1 text-rgba(255,255,255,0.6)">
166
- {tokenData?.symbol}
167
- </div>
148
+ <div className="ml-1">{tokenData?.symbol}</div>
168
149
  {tokenData?.image ? (
169
150
  <img
170
151
  src={tokenData?.image}
@@ -179,14 +160,12 @@ export const HistoryCard: FC<Props> = ({ transaction, supportedChains }) => {
179
160
  </div>
180
161
  {transaction.tokenOutAddress && (
181
162
  <>
182
- <div className="w-3.5 h-3.5 text-[rgba(255,255,255,0.6)] my-0 mx-1.5">
163
+ <div className="w-3.5 h-3.5 my-0 mx-1.5">
183
164
  <ArrowRightIcon />
184
165
  </div>
185
166
  <div className="flex items-center">
186
167
  {Number(receivedAmount) < 0.0001 ? "<0.0001" : receivedAmount}
187
- <div className="text-xs ml-1 text-[rgba(255,255,255,0.6)]">
188
- {tokenOutData?.symbol}
189
- </div>
168
+ <div className="ml-1">{tokenOutData?.symbol}</div>
190
169
 
191
170
  {tokenOutData?.image ? (
192
171
  <img
@@ -1,11 +1,13 @@
1
- import { FC } from "react";
1
+ import React, { FC, useEffect, useMemo, useState } from "react";
2
2
  import { weiToHumanReadable } from "@src/utils";
3
- import { Chain, Route, Token } from "@src/models";
3
+ import { Chain, Route, Token, TokenPrices } from "@src/models";
4
4
  import { ArrowDownIcon } from "../icons";
5
+ import { UsdPrice } from "@src/components/TxConfigForm/UsdPrice";
6
+ import { getPrices } from "@src/services";
7
+ import { Skeleton } from "@src/components";
5
8
 
6
9
  interface Props {
7
10
  isGettingRoute: boolean;
8
- amount: string;
9
11
  route: Route | undefined;
10
12
  dstToken: Token | undefined;
11
13
  dstChain: Chain | undefined;
@@ -13,40 +15,67 @@ interface Props {
13
15
 
14
16
  export const Summary: FC<Props> = ({
15
17
  isGettingRoute,
16
- amount,
17
18
  route,
18
19
  dstToken,
19
20
  dstChain,
20
21
  }) => {
22
+ const [prices, setPrices] = useState<TokenPrices>();
23
+
24
+ const amountReadable = useMemo(
25
+ () =>
26
+ weiToHumanReadable({
27
+ amount: route?.estAmountOut || "0",
28
+ decimals: dstToken?.decimals || 18,
29
+ precisionFractionalPlaces: 5,
30
+ }),
31
+ [route, dstToken],
32
+ );
33
+
34
+ useEffect(() => {
35
+ if (dstChain) {
36
+ getPrices({ chainId: dstChain.chainId, currency: "USD" }).then((prices) =>
37
+ setPrices(prices),
38
+ );
39
+ }
40
+ }, [dstChain]);
41
+
21
42
  return (
22
- <div className="flex justify-between globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4 relative">
23
- <div className="flex flex-col gap-2">
24
- <p className="text-[rgba(255,255,255,0.6)] text-sm">You receive</p>
25
- {isGettingRoute && amount.length > 0 ? (
26
- <div className="bg-current rounded animate-pulse w-[190px] h-7" />
27
- ) : (
28
- <div className="flex gap-2 items-center text-white text-2xl">
29
- {weiToHumanReadable({
30
- amount: route?.estAmountOut || "0",
31
- decimals: dstToken?.decimals || 18,
32
- precisionFractionalPlaces: 5,
33
- })}
34
- <div className="w-6 h-6">
35
- <img src={dstToken?.image} alt={dstToken?.name} />
43
+ <div className="flex flex-col globalBorder rounded-lg bg-[rgba(15,15,15,1)] p-4 relative">
44
+ <div className="flex justify-between">
45
+ <div className="flex flex-col gap-1">
46
+ <p className="text-[rgba(255,255,255,0.6)] text-sm">You receive</p>
47
+ {isGettingRoute ? (
48
+ <Skeleton
49
+ width="w-[100px]"
50
+ height="h-[36px]"
51
+ other="sm:w-[190px]"
52
+ />
53
+ ) : (
54
+ <div className="flex gap-2 items-center text-white text-xl sm:text-3xl">
55
+ {amountReadable}
56
+ <div className="w-4 sm:w-6 h-4 sm:h-6">
57
+ <img src={dstToken?.image} alt={dstToken?.name} />
58
+ </div>
59
+ <p className="text-base sm:text-xl">{dstToken?.symbol}</p>
36
60
  </div>
37
- <p>{dstToken?.symbol}</p>
38
- </div>
39
- )}
40
- </div>
41
- <div className="flex flex-col gap-1 text-sm">
42
- <p className="text-[rgba(255,255,255,0.6)] text-right">on chain:</p>
43
- <div className="flex gap-1 text-white">
44
- <div className="w-4 h-4">
45
- <img src={dstChain?.image} alt={dstChain?.name} />
61
+ )}
62
+ </div>
63
+ <div className="flex flex-col gap-1 text-sm">
64
+ <p className="text-[rgba(255,255,255,0.6)] text-right">on chain:</p>
65
+ <div className="flex items-center gap-1 text-white">
66
+ <div className="w-4 h-4">
67
+ <img src={dstChain?.image} alt={dstChain?.name} />
68
+ </div>
69
+ <div>{dstChain?.displayName}</div>
46
70
  </div>
47
- <div>{dstChain?.displayName}</div>
48
71
  </div>
49
72
  </div>
73
+ <UsdPrice
74
+ prices={prices}
75
+ token={dstToken}
76
+ amount={amountReadable}
77
+ loading={isGettingRoute}
78
+ />
50
79
  <div className="absolute right-[50%] top-0 translate-x-1/2 translate-y-[-60%] globalBorder rounded-xl p-[5px] bg-[rgba(15,15,15,1)]">
51
80
  <div className=" flex items-center justify-center rounded-lg w-8 h-8 bg-gradient-to-l from-[rgba(54,129,198,1)] to-[rgba(43,74,157,1)] ">
52
81
  <ArrowDownIcon />
@@ -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 BigNumber from "bignumber.js";
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 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>
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 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
- )}
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
- <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</>
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(".xswap");
27
+ const modalRoot = document.querySelector("#xswap-modal");
28
28
 
29
29
  const onBackdropClick = (e: MouseEvent<HTMLDivElement>) => {
30
30
  e.stopPropagation();
@@ -153,7 +153,7 @@ export const TokenPicker: FC<Props> = ({
153
153
  })}
154
154
  </div>
155
155
  ) : (
156
- <Skeleton w="w-12" h="h-3" />
156
+ <Skeleton width="w-12" height="h-3" />
157
157
  )}
158
158
  </>
159
159
  )}
@@ -16,8 +16,8 @@ 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">
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
21
  {signer
22
22
  ? `Connected wallet: ${shortAddress(signer)}`
23
23
  : `Wallet disconnected`}
@@ -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", "txConfigFormRoot");
118
- xswapElement.classList.add("xswap");
117
+ xswapElement.setAttribute("id", "xswap-modal");
119
118
  document.body.appendChild(xswapElement);
120
119
  txConfigFormRoot = createRoot(xswapElement);
121
120
  }