@xswap-link/sdk 0.8.7 → 0.9.0

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/.github/workflows/main.yml +2 -1
  2. package/CHANGELOG.md +6 -0
  3. package/babel.config.cjs +8 -0
  4. package/dist/index.global.js +128 -131
  5. package/dist/index.js +9 -9
  6. package/dist/index.mjs +10 -10
  7. package/jest.config.ts +37 -0
  8. package/package.json +17 -5
  9. package/src/components/Modal/index.tsx +1 -1
  10. package/src/components/Swap/Header/index.tsx +1 -1
  11. package/src/components/Swap/ReorderButton/ReorderButton.tsx +12 -10
  12. package/src/components/Swap/SwapView/FeesPanel/index.tsx +1 -1
  13. package/src/components/Swap/SwapView/SwapButton/index.tsx +23 -21
  14. package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +4 -2
  15. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +11 -2
  16. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +116 -33
  17. package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +1 -1
  18. package/src/components/Swap/SwapView/index.tsx +1 -1
  19. package/src/context/HistoryProvider.tsx +2 -2
  20. package/src/context/SwapProvider.tsx +147 -62
  21. package/src/context/TransactionProvider.tsx +1 -1
  22. package/src/utils/validation.ts +50 -16
  23. package/tailwind.config.js +1 -0
  24. package/test/context/SwapProvider.test.tsx +851 -0
  25. package/test/fileMock.ts +1 -0
  26. package/test/fixtures/bridgeTokens.mock.ts +1318 -0
  27. package/test/fixtures/bridgeTokensDictionary.mock.ts +1272 -0
  28. package/test/fixtures/integrationConfig.mock.ts +10 -0
  29. package/test/fixtures/supportedChains.mock.ts +32950 -0
  30. package/test/{api.test.ts → services/getChains.test.ts} +6 -5
  31. package/test/setup.ts +13 -0
  32. package/test/styleMock.ts +1 -0
  33. package/jest.config.json +0 -8
  34. package/test/api.mock.ts +0 -106
  35. package/test/setupTests.ts +0 -3
package/jest.config.ts ADDED
@@ -0,0 +1,37 @@
1
+ import type { JestConfigWithTsJest } from "ts-jest";
2
+
3
+ const esmModules = [
4
+ "uuid",
5
+ "wagmi",
6
+ "@wagmi",
7
+ "viem",
8
+ "@viem",
9
+ "@tanstack/react-query",
10
+ ];
11
+
12
+ const config: JestConfigWithTsJest = {
13
+ extensionsToTreatAsEsm: [".ts", ".tsx"],
14
+ verbose: true,
15
+ preset: "ts-jest",
16
+ testEnvironment: "jsdom",
17
+ setupFilesAfterEnv: ["<rootDir>/test/setup.ts"],
18
+ transform: {
19
+ "^.+\\.tsx?$": ["ts-jest", { useESM: true }],
20
+ "\\.jsx?$": "babel-jest",
21
+ },
22
+ transformIgnorePatterns: [
23
+ `node_modules/(?!(?:.pnpm/)?(${esmModules.join("|")}))`,
24
+ ],
25
+ testPathIgnorePatterns: ["./dist"],
26
+ moduleNameMapper: {
27
+ "^@src/(.*)$": "<rootDir>/src/$1",
28
+ "^@/(.*)$": "<rootDir>/src/$1",
29
+ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$":
30
+ "<rootDir>/test/fileMock.ts",
31
+ "\\.(css)$": "<rootDir>/test/styleMock.ts",
32
+ },
33
+ automock: false,
34
+ resetMocks: false,
35
+ };
36
+
37
+ export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -35,7 +35,7 @@
35
35
  "@fortawesome/free-solid-svg-icons": "^6.4.2",
36
36
  "@fortawesome/react-fontawesome": "^0.2.0",
37
37
  "@r2wc/core": "^1.1.0",
38
- "@tanstack/react-query": "^5.35.5",
38
+ "@tanstack/react-query": "^5.64.2",
39
39
  "async-retry": "^1.3.3",
40
40
  "bignumber.js": "4.0.4",
41
41
  "date-fns": "^3.6.0",
@@ -43,11 +43,18 @@
43
43
  "notistack": "^3.0.1",
44
44
  "react-error-boundary": "^4.1.0",
45
45
  "react-virtuoso": "^4.7.12",
46
- "viem": "2.x",
47
- "wagmi": "^2.8.5"
46
+ "viem": "~2.22.12",
47
+ "wagmi": "^2.14.9"
48
48
  },
49
49
  "devDependencies": {
50
+ "@babel/core": "^7.26.0",
51
+ "@babel/preset-env": "^7.26.0",
52
+ "@babel/preset-react": "^7.26.3",
53
+ "@babel/preset-typescript": "^7.26.0",
50
54
  "@changesets/cli": "^2.27.1",
55
+ "@testing-library/jest-dom": "^6.4.2",
56
+ "@testing-library/react": "^14.2.1",
57
+ "@testing-library/user-event": "^14.5.2",
51
58
  "@types/async-retry": "^1.4.8",
52
59
  "@types/jest": "^29.5.12",
53
60
  "@types/node": "^20.11.17",
@@ -55,9 +62,13 @@
55
62
  "@types/react-dom": "^18.2.0",
56
63
  "@typescript-eslint/eslint-plugin": "^7.1.0",
57
64
  "@typescript-eslint/parser": "^7.1.0",
65
+ "babel-jest": "^29.7.0",
66
+ "cross-fetch": "^4.1.0",
58
67
  "eslint": "^8.57.0",
59
68
  "eslint-plugin-react-hooks": "^4.6.2",
69
+ "identity-obj-proxy": "^3.0.0",
60
70
  "jest": "^29.7.0",
71
+ "jest-environment-jsdom": "^29.7.0",
61
72
  "jest-fetch": "^1.1.1",
62
73
  "jest-fetch-mock": "^3.0.3",
63
74
  "nodemon": "^3.1.0",
@@ -67,6 +78,7 @@
67
78
  "react-dom": "^18.2.0",
68
79
  "tailwindcss": "^3.4.3",
69
80
  "ts-jest": "^29.1.2",
81
+ "ts-node": "^10.9.2",
70
82
  "tsup": "^8.1.0",
71
83
  "typescript": "^5.3.3"
72
84
  },
@@ -84,6 +96,6 @@
84
96
  "watch": "tsup && nodemon",
85
97
  "release": "tsup && changeset publish",
86
98
  "lint": "tsc && eslint . --ext .ts",
87
- "test": "jest"
99
+ "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
88
100
  }
89
101
  }
@@ -45,7 +45,7 @@ export const Modal: FC<{
45
45
  }}
46
46
  >
47
47
  <div
48
- className="flex flex-col bg-t_bg_primary x-border rounded-3xl p-4 w-full min-w-[340px]"
48
+ className="flex flex-col bg-t_bg_primary rounded-3xl p-4 w-full min-w-[340px]"
49
49
  style={{
50
50
  height: overlay ? "600px" : "100%",
51
51
  maxWidth: "480px",
@@ -9,7 +9,7 @@ export const Header = ({ onClose }: Props) => {
9
9
 
10
10
  return (
11
11
  <div className="flex justify-between">
12
- <div className="text-xl">
12
+ <div className="text-xl font-sans-bold font-bold">
13
13
  {tab === "Swap" && bridgeUI ? "Bridge" : tab}
14
14
  </div>
15
15
  <Controls onClose={onClose} />
@@ -22,16 +22,18 @@ export const ReorderButton: FC = () => {
22
22
  };
23
23
 
24
24
  return (
25
- <button
26
- onClick={onReorderClick}
27
- aria-label="Switch target token with the source token"
28
- className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-14 p-1 border border-solid border-t_text_primary border-opacity-10 rounded-xl bg-t_bg_secondary"
29
- >
30
- <div className="relative bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark w-8 h-8 rounded-lg">
31
- <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-white">
32
- <ArrowsExchange />
25
+ <div className="h-[1px] w-full">
26
+ <button
27
+ onClick={onReorderClick}
28
+ aria-label="Switch target token with the source token"
29
+ className="absolute -translate-y-5 left-1/2 -translate-x-5 p-1 border border-solid border-t_text_primary border-opacity-10 rounded-xl bg-t_bg_secondary"
30
+ >
31
+ <div className="relative bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark w-8 h-8 rounded-lg">
32
+ <div className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 fill-white">
33
+ <ArrowsExchange />
34
+ </div>
33
35
  </div>
34
- </div>
35
- </button>
36
+ </button>
37
+ </div>
36
38
  );
37
39
  };
@@ -42,7 +42,7 @@ export const FeesPanel = () => {
42
42
  {isFetchingRoute ? (
43
43
  <Skeleton className="w-[100px] h-full" />
44
44
  ) : (
45
- <p>{`${totalFee} ${feeToken?.symbol}`}</p>
45
+ <p>{`${totalFee} ${feeToken?.symbol || ""}`}</p>
46
46
  )}
47
47
  </div>
48
48
  <div className="flex gap-2 items-center fill-t_text_primary">
@@ -33,6 +33,7 @@ export const SwapButton = () => {
33
33
  srcValueWei,
34
34
  srcTokenBalanceWei,
35
35
  isFetchingRoute,
36
+ isAsyncDataLoaded,
36
37
  } = useSwapContext();
37
38
 
38
39
  const { setTxStatus, setTxMsg, setTxModalOpen, enqueueTransaction } =
@@ -92,7 +93,7 @@ export const SwapButton = () => {
92
93
  }, [setTxModalOpen, setTxMsg, setTxStatus]);
93
94
 
94
95
  const button: Button = useMemo(() => {
95
- if (!srcChainId || !dstChainId || !srcTokenAddress || !dstTokenAddress) {
96
+ if (!isAsyncDataLoaded) {
96
97
  return {
97
98
  text: "Loading XPay...",
98
99
  fn: () => {},
@@ -111,8 +112,8 @@ export const SwapButton = () => {
111
112
  if (!address) {
112
113
  return {
113
114
  text: integrationConfig.defaultWalletPicker
114
- ? "Connect wallet"
115
- : "Connect wallet first",
115
+ ? "Connect Wallet"
116
+ : "Connect Wallet First",
116
117
  fn: () => {
117
118
  setConnectWalletModalOpen(true);
118
119
  },
@@ -120,12 +121,20 @@ export const SwapButton = () => {
120
121
  };
121
122
  }
122
123
 
124
+ if (!srcChainId || !dstChainId || !srcTokenAddress || !dstTokenAddress) {
125
+ return {
126
+ text: "Select Tokens",
127
+ fn: () => {},
128
+ disabled: true,
129
+ };
130
+ }
131
+
123
132
  if (
124
133
  srcChainId === dstChainId &&
125
134
  srcTokenAddress.toLowerCase() === dstTokenAddress.toLowerCase()
126
135
  ) {
127
136
  return {
128
- text: "Change token",
137
+ text: "Change Token",
129
138
  fn: () => {},
130
139
  disabled: true,
131
140
  };
@@ -133,7 +142,7 @@ export const SwapButton = () => {
133
142
 
134
143
  if (chainId?.toString() !== srcChainId) {
135
144
  return {
136
- text: "Change chain",
145
+ text: "Change Chain",
137
146
  fn: async () => await switchChainAsync({ chainId: Number(srcChainId) }),
138
147
  disabled: false,
139
148
  };
@@ -145,15 +154,7 @@ export const SwapButton = () => {
145
154
  safeBigNumberFrom(srcValueWei).gt(safeBigNumberFrom(srcTokenBalanceWei))
146
155
  ) {
147
156
  return {
148
- text: "Insufficient balance",
149
- fn: () => {},
150
- disabled: true,
151
- };
152
- }
153
-
154
- if (!srcTokenAddress || !dstTokenAddress) {
155
- return {
156
- text: `Select token`,
157
+ text: "Insufficient Balance",
157
158
  fn: () => {},
158
159
  disabled: true,
159
160
  };
@@ -161,7 +162,7 @@ export const SwapButton = () => {
161
162
 
162
163
  if (safeBigNumberFrom(srcValueWei || "0").lte(safeBigNumberFrom("0"))) {
163
164
  return {
164
- text: `Enter token amount`,
165
+ text: `Enter Token Amount`,
165
166
  fn: () => {},
166
167
  disabled: true,
167
168
  };
@@ -169,7 +170,7 @@ export const SwapButton = () => {
169
170
 
170
171
  if (isFetchingRoute) {
171
172
  return {
172
- text: `Fetching route`,
173
+ text: `Fetching Route`,
173
174
  fn: () => {},
174
175
  disabled: true,
175
176
  };
@@ -181,7 +182,7 @@ export const SwapButton = () => {
181
182
  BigNumber.from(currentTokenAllowance).lt(BigNumber.from(srcValueWei))
182
183
  ) {
183
184
  return {
184
- text: "Reset approval",
185
+ text: "Reset Approval",
185
186
  fn: async () => {
186
187
  enqueueTransaction({
187
188
  executeTransaction: () => approve("0", routerAddress),
@@ -207,18 +208,19 @@ export const SwapButton = () => {
207
208
  disabled: false,
208
209
  };
209
210
  }, [
210
- bridgeUI,
211
+ isAsyncDataLoaded,
212
+ error,
213
+ address,
211
214
  srcChainId,
212
215
  dstChainId,
213
216
  srcTokenAddress,
214
217
  dstTokenAddress,
215
- error,
216
- address,
217
218
  chainId,
218
219
  srcValueWei,
219
220
  srcTokenBalanceWei,
220
221
  isFetchingRoute,
221
222
  currentTokenAllowance,
223
+ bridgeUI,
222
224
  integrationConfig.defaultWalletPicker,
223
225
  switchChainAsync,
224
226
  enqueueTransaction,
@@ -241,7 +243,7 @@ export const SwapButton = () => {
241
243
  type="button"
242
244
  disabled={button.disabled}
243
245
  onClick={button.fn}
244
- className={`text-xl w-full py-5 cursor-pointer disabled:cursor-not-allowed rounded-2xl border-none ${
246
+ className={`text-xl font-sans-bold font-bold w-full py-5 cursor-pointer disabled:cursor-not-allowed rounded-2xl border-none ${
245
247
  button.disabled
246
248
  ? "bg-t_button_pr_off_bg text-t_button_pr_off_text"
247
249
  : "bg-gradient-to-r from-t_main_accent_light to-t_main_accent_dark text-t_button_pr_text"
@@ -31,7 +31,7 @@ export const AmountPanel = ({ type }: Props) => {
31
31
  <>
32
32
  <SafeInput
33
33
  id="swap-amount-input"
34
- className={`bg-transparent border-none p-0 text-xl outline-0`}
34
+ className={`bg-transparent border-none p-0 text-xl outline-0 font-sans-bold font-bold`}
35
35
  regex={NUMBER_INPUT_REGEX}
36
36
  value={srcValue}
37
37
  onChange={(value) => setSrcValue(value)}
@@ -47,7 +47,9 @@ export const AmountPanel = ({ type }: Props) => {
47
47
  </div>
48
48
  ) : (
49
49
  <>
50
- <div className={`text-xl text-t_text_secondary`}>
50
+ <div
51
+ className={`text-xl text-t_text_secondary font-sans-bold font-bold`}
52
+ >
51
53
  {dstValue || 0}
52
54
  </div>
53
55
  {valueUsd && (
@@ -9,9 +9,15 @@ import { useAccount } from "wagmi";
9
9
  type Props = {
10
10
  token: TokenOption;
11
11
  selectedTokenAddress: string | undefined;
12
+ selectedChainId: string | undefined;
12
13
  onClick: () => void;
13
14
  };
14
- export const TokenItem = ({ token, selectedTokenAddress, onClick }: Props) => {
15
+ export const TokenItem = ({
16
+ token,
17
+ selectedTokenAddress,
18
+ onClick,
19
+ selectedChainId,
20
+ }: Props) => {
15
21
  const { supportedChains } = useSwapContext();
16
22
  const { address } = useAccount();
17
23
 
@@ -24,7 +30,10 @@ export const TokenItem = ({ token, selectedTokenAddress, onClick }: Props) => {
24
30
  <div
25
31
  className={`token-picker-container ${
26
32
  token.address.toLowerCase() === selectedTokenAddress?.toLowerCase() &&
27
- "bg-t_bg_tertiary bg-opacity-10"
33
+ selectedChainId &&
34
+ token.chainId === selectedChainId
35
+ ? "bg-t_bg_tertiary bg-opacity-10"
36
+ : ""
28
37
  }`}
29
38
  onClick={onClick}
30
39
  >
@@ -19,6 +19,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
19
19
  import { GroupedVirtuoso } from "react-virtuoso";
20
20
  import { QuickPickTokenItem } from "./QuickPickTokenItem";
21
21
  import { TokenItem } from "./TokenItem";
22
+ import { useAccount } from "wagmi";
22
23
 
23
24
  type Props = {
24
25
  type: SwapPanelType;
@@ -46,11 +47,13 @@ export const TokenPicker = ({
46
47
  const [loadingCustomTokenData, setLoadingCustomTokenData] = useState(false);
47
48
  const [customTokenData, setCustomTokenData] =
48
49
  useState<ImportedTokenData | null>(null);
50
+ const [showQuickPicks, setShowQuickPicks] = useState(true);
49
51
  const currentRouteRequestNonce = useRef<number>(0);
50
52
  const tokenSearchRef = useRef<HTMLInputElement | null>(null);
51
53
 
52
54
  const { bridgeUI, supportedChains, findTokenDataByAddressAndChain } =
53
55
  useSwapContext();
56
+ const { address } = useAccount();
54
57
  const evmContractApi = useEvmContractApi();
55
58
 
56
59
  useEffect(() => {
@@ -188,35 +191,63 @@ export const TokenPicker = ({
188
191
  }, [isOpen]);
189
192
 
190
193
  const allTokens = useMemo(() => {
191
- const filteredTokensSorted = [
192
- ...filteredTokens
193
- .filter((token) => BigNumber.from(token?.balance || "0").gt(0))
194
- .sort((tokenDataA, tokenDataB) => {
195
- return tokenDataB.priority - tokenDataA.priority;
196
- }),
197
- ...filteredTokens
198
- .filter((token) => BigNumber.from(token?.balance || "0").eq(0))
199
- .sort((tokenDataA, tokenDataB) => {
200
- return tokenDataB.priority - tokenDataA.priority;
201
- }),
202
- ];
203
- const otherFilteredTokensSorted = [
204
- ...otherFilteredTokens
205
- .filter((token) => BigNumber.from(token?.balance || "0").gt(0))
206
- .sort((tokenDataA, tokenDataB) => {
207
- return tokenDataB.priority - tokenDataA.priority;
208
- }),
209
- ...otherFilteredTokens
210
- .filter((token) => BigNumber.from(token?.balance || "0").eq(0))
211
- .sort((tokenDataA, tokenDataB) => {
212
- return tokenDataB.priority - tokenDataA.priority;
213
- }),
214
- ];
194
+ const ownedFilteredTokens = [
195
+ ...filteredTokens.filter((token) =>
196
+ BigNumber.from(token?.balance || "0").gt(0),
197
+ ),
198
+ ...otherFilteredTokens.filter((token) =>
199
+ BigNumber.from(token?.balance || "0").gt(0),
200
+ ),
201
+ ].sort(
202
+ (tokenDataA, tokenDataB) => tokenDataB.priority - tokenDataA.priority,
203
+ );
215
204
 
216
- return [...filteredTokensSorted, ...otherFilteredTokensSorted];
217
- }, [filteredTokens, otherFilteredTokens]);
205
+ const currentChainTokens = filteredTokens
206
+ .filter((token) => BigNumber.from(token?.balance || "0").eq(0))
207
+ .sort(
208
+ (tokenDataA, tokenDataB) => tokenDataB.priority - tokenDataA.priority,
209
+ );
218
210
 
219
- const tokenGroups = [filteredTokens, otherFilteredTokens];
211
+ const otherChainTokens = otherFilteredTokens
212
+ .filter((token) => BigNumber.from(token?.balance || "0").eq(0))
213
+ .sort(
214
+ (tokenDataA, tokenDataB) => tokenDataB.priority - tokenDataA.priority,
215
+ );
216
+
217
+ // Create groups based on chain and wallet connection status
218
+ const groups = chain
219
+ ? [
220
+ ...(!!address && ownedFilteredTokens.length
221
+ ? [ownedFilteredTokens]
222
+ : []),
223
+ currentChainTokens,
224
+ otherChainTokens,
225
+ ]
226
+ : [
227
+ ...(!!address && ownedFilteredTokens.length
228
+ ? [ownedFilteredTokens]
229
+ : []),
230
+ [...currentChainTokens, ...otherChainTokens],
231
+ ];
232
+
233
+ return groups.flat();
234
+ }, [filteredTokens, otherFilteredTokens, chain, address]);
235
+
236
+ const ownedTokens = useMemo(
237
+ () => allTokens.filter((token) => token.balance?.gt(0)),
238
+ [allTokens],
239
+ );
240
+
241
+ const tokenGroups = chain
242
+ ? [
243
+ ...(!!address && ownedTokens.length ? [ownedTokens] : []),
244
+ filteredTokens,
245
+ otherFilteredTokens,
246
+ ]
247
+ : [
248
+ ...(!!address && ownedTokens.length ? [ownedTokens] : []),
249
+ otherFilteredTokens,
250
+ ];
220
251
 
221
252
  return showImportWarning ? (
222
253
  <>
@@ -313,6 +344,7 @@ export const TokenPicker = ({
313
344
  <CloseIcon />
314
345
  </div>
315
346
  </div>
347
+
316
348
  <TextInput
317
349
  ref={tokenSearchRef}
318
350
  inputIcon={
@@ -324,7 +356,13 @@ export const TokenPicker = ({
324
356
  value={searchValue}
325
357
  handleChange={setSearchValue}
326
358
  />
327
- <div className="flex flex-wrap gap-1 my-4 mx-0">
359
+ <div
360
+ className={`flex flex-wrap gap-1 my-4 mx-0 transition-all duration-300 ${
361
+ showQuickPicks
362
+ ? "opacity-100 max-h-[200px]"
363
+ : "opacity-0 max-h-0 overflow-hidden"
364
+ }`}
365
+ >
328
366
  {tokens
329
367
  .filter((token) => token?.quickPick)
330
368
  .map((token) => (
@@ -383,17 +421,61 @@ export const TokenPicker = ({
383
421
  <GroupedVirtuoso
384
422
  style={{ maxHeight: "400px", height: "100%" }}
385
423
  groupCounts={tokenGroups.map((group) => group?.length)}
424
+ scrollerRef={(ref) => {
425
+ if (ref) {
426
+ ref.addEventListener("scroll", (e) => {
427
+ const target = e.target as HTMLDivElement;
428
+ setShowQuickPicks(target.scrollTop < 10);
429
+ });
430
+ }
431
+ }}
386
432
  groupContent={(index) => (
387
433
  <div className="py-1 bg-t_bg_primary text-t_text_primary">
388
434
  <div className="px-4 text-left">
389
435
  {(() => {
390
436
  if (chain) {
391
- return index === 0
392
- ? `${chain.displayName} network`
393
- : "Other networks";
437
+ // chain is selected
438
+ switch (index) {
439
+ case 0: {
440
+ if (!address) {
441
+ return `${chain.displayName} network`;
442
+ }
443
+ return ownedTokens.length
444
+ ? "My tokens"
445
+ : `${chain.displayName} network`;
446
+ }
447
+ case 1: {
448
+ if (!address) {
449
+ return "Other networks";
450
+ }
451
+ return `${chain.displayName} network`;
452
+ }
453
+ case 2: {
454
+ return "Other networks";
455
+ }
456
+ default: {
457
+ return "Undefined group";
458
+ }
459
+ }
460
+ } else {
461
+ // chain is not selected
462
+ switch (index) {
463
+ case 0: {
464
+ if (!address) {
465
+ return "All networks";
466
+ }
467
+ return ownedTokens.length
468
+ ? "My tokens"
469
+ : "All networks";
470
+ }
471
+ case 1: {
472
+ return "All networks";
473
+ }
474
+ default: {
475
+ return "Undefined group";
476
+ }
477
+ }
394
478
  }
395
-
396
- return "All networks";
397
479
  })()}
398
480
  </div>
399
481
  <div className="horizontal-separator" />
@@ -407,6 +489,7 @@ export const TokenPicker = ({
407
489
  allTokens[index] && (
408
490
  <TokenItem
409
491
  token={allTokens[index]}
492
+ selectedChainId={chain?.chainId}
410
493
  selectedTokenAddress={selectedTokenAddress}
411
494
  onClick={() => {
412
495
  onSelect(allTokens[index]!);
@@ -77,7 +77,7 @@ export const TokenPanel = ({ chain, token, type, className }: Props) => {
77
77
  return (
78
78
  <>
79
79
  {tokenPickerModalOpen && (
80
- <Modal onClose={() => setTokenPickerModalOpen(false)}>
80
+ <Modal onClose={() => setTokenPickerModalOpen(false)} className="z-50">
81
81
  <TokenPicker
82
82
  type={type}
83
83
  chain={chain!}
@@ -8,7 +8,7 @@ export type SwapPanelType = "source" | "destination";
8
8
  export const SwapView = () => {
9
9
  return (
10
10
  <div className="flex flex-col gap-2 fill-t_text_primary">
11
- <div className="flex flex-col gap-5">
11
+ <div className="flex flex-col gap-[10px]">
12
12
  <SwapPanel type="source" />
13
13
  <ReorderButton />
14
14
  <SwapPanel type="destination" />
@@ -10,7 +10,7 @@ import { Transaction, TransactionStatus } from "@src/models";
10
10
  import { getHistory } from "@src/services";
11
11
  import { isServer } from "@src/utils";
12
12
  import BigNumberJS from "bignumber.js";
13
- import { closeSnackbar, useSnackbar } from "notistack";
13
+ import { useSnackbar } from "notistack";
14
14
  import {
15
15
  Context,
16
16
  ReactNode,
@@ -69,7 +69,7 @@ export const HistoryProvider = ({ children }: HistoryProviderProps) => {
69
69
  const [history, setHistory] = useState<Transaction[]>([]);
70
70
  const [historyLoadedOnce, setHistoryLoadedOnce] = useState(false);
71
71
 
72
- const { enqueueSnackbar } = useSnackbar();
72
+ const { enqueueSnackbar, closeSnackbar } = useSnackbar();
73
73
  const { debounce: getHistoryDebounce } = useDebounce({
74
74
  delayMs: 600,
75
75
  });