@xswap-link/sdk 0.8.7 → 0.9.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/CHANGELOG.md +12 -0
- package/babel.config.cjs +8 -0
- package/dist/index.d.mts +4 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.global.js +132 -135
- package/dist/index.js +9 -9
- package/dist/index.mjs +10 -10
- package/jest.config.ts +37 -0
- package/package.json +17 -5
- package/src/components/Modal/index.tsx +1 -1
- package/src/components/Swap/Header/index.tsx +1 -1
- package/src/components/Swap/ReorderButton/ReorderButton.tsx +12 -10
- package/src/components/Swap/SwapView/FeesPanel/index.tsx +1 -1
- package/src/components/Swap/SwapView/SwapButton/index.tsx +23 -21
- package/src/components/Swap/SwapView/SwapPanel/AmountPanel/index.tsx +4 -2
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/ChainItem/index.tsx +51 -59
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +47 -13
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +138 -35
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +6 -2
- package/src/components/Swap/SwapView/SwapPanel/index.tsx +1 -1
- package/src/components/Swap/SwapView/index.tsx +2 -2
- package/src/components/Tooltip/index.tsx +1 -1
- package/src/context/HistoryProvider.tsx +2 -2
- package/src/context/SwapProvider.tsx +174 -100
- package/src/context/TransactionProvider.tsx +1 -1
- package/src/models/TokenData.ts +3 -1
- package/src/models/payloads/GetPricesPayload.ts +1 -1
- package/src/utils/validation.ts +50 -16
- package/tailwind.config.js +1 -0
- package/test/context/SwapProvider.test.tsx +851 -0
- package/test/fileMock.ts +1 -0
- package/test/fixtures/bridgeTokens.mock.ts +1318 -0
- package/test/fixtures/bridgeTokensDictionary.mock.ts +1272 -0
- package/test/fixtures/integrationConfig.mock.ts +10 -0
- package/test/fixtures/supportedChains.mock.ts +32950 -0
- package/test/{api.test.ts → services/getChains.test.ts} +6 -5
- package/test/setup.ts +13 -0
- package/test/styleMock.ts +1 -0
- package/jest.config.json +0 -8
- package/test/api.mock.ts +0 -106
- 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.
|
|
3
|
+
"version": "0.9.1",
|
|
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.
|
|
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.
|
|
47
|
-
"wagmi": "^2.
|
|
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
|
|
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
|
-
<
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
<div className="
|
|
32
|
-
<
|
|
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
|
-
</
|
|
35
|
-
</
|
|
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 (!
|
|
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
|
|
115
|
-
: "Connect
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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 && (
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { DotGreenIcon
|
|
1
|
+
import { DotGreenIcon } from "@src/assets/icons";
|
|
2
2
|
import { SwapPanelType } from "@src/components/Swap/SwapView";
|
|
3
|
-
import { Tooltip } from "@src/components/Tooltip";
|
|
4
3
|
import { useSwapContext } from "@src/context";
|
|
5
4
|
import useNetworks from "@src/hooks/networkManagement/useNetworks";
|
|
6
5
|
import { Chain } from "@src/models";
|
|
7
|
-
import { useMemo
|
|
6
|
+
import { useMemo } from "react";
|
|
8
7
|
|
|
9
8
|
type Props = {
|
|
10
9
|
chain: Chain;
|
|
@@ -18,8 +17,6 @@ export const ChainItem = ({ chain, type, onClick, disabled }: Props) => {
|
|
|
18
17
|
networks: { evm },
|
|
19
18
|
} = useNetworks();
|
|
20
19
|
|
|
21
|
-
const tooltipTriggerRef = useRef<SVGSVGElement>(null);
|
|
22
|
-
|
|
23
20
|
const { srcChain, dstChain, bridgeUI } = useSwapContext();
|
|
24
21
|
|
|
25
22
|
const currentChainId = useMemo(
|
|
@@ -40,66 +37,61 @@ export const ChainItem = ({ chain, type, onClick, disabled }: Props) => {
|
|
|
40
37
|
return type === "destination" ? srcChain?.chainId : dstChain?.chainId;
|
|
41
38
|
}, [bridgeUI, dstChain?.chainId, srcChain?.chainId, type]);
|
|
42
39
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
: ""
|
|
57
|
-
}`}
|
|
58
|
-
>
|
|
59
|
-
<div className="flex gap-3 items-center w-full">
|
|
60
|
-
<img src={chain.image} alt={chain.name} className="w-[34px] h-[34px]" />
|
|
61
|
-
<p>{chain.displayName}</p>
|
|
62
|
-
</div>
|
|
40
|
+
const getTooltipText = () => {
|
|
41
|
+
if (chain.chainId === sameChainId) {
|
|
42
|
+
return "Transfers between the same chains are not available";
|
|
43
|
+
}
|
|
44
|
+
if (disabled) {
|
|
45
|
+
return bridgeUI
|
|
46
|
+
? "Token is not available on this chain"
|
|
47
|
+
: `Swap from ${
|
|
48
|
+
srcChain?.displayName || "the source chain"
|
|
49
|
+
} to this chain is currently not supported.`;
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
};
|
|
63
53
|
|
|
64
|
-
|
|
65
|
-
<div className="flex items-baseline gap-1">
|
|
66
|
-
<DotGreenIcon />
|
|
67
|
-
<div className="text-t_text_green">connected</div>
|
|
68
|
-
</div>
|
|
69
|
-
)}
|
|
54
|
+
const tooltipText = getTooltipText();
|
|
70
55
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
56
|
+
return (
|
|
57
|
+
<div className="group relative">
|
|
58
|
+
<div
|
|
59
|
+
onClick={() => {
|
|
60
|
+
if (chain.chainId === sameChainId || disabled) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
onClick(chain.chainId);
|
|
64
|
+
}}
|
|
65
|
+
className={`flex justify-between items-center gap-2 mt-2 mb-2 cursor-pointer p-4 border border-solid border-white border-opacity-0 hover:selected-chain-item ${
|
|
66
|
+
currentChainId === chain.chainId ? "selected-chain-item" : ""
|
|
67
|
+
} ${
|
|
68
|
+
chain.chainId === sameChainId || disabled
|
|
69
|
+
? "disabled-chain-item cursor-default"
|
|
70
|
+
: ""
|
|
71
|
+
}`}
|
|
72
|
+
>
|
|
73
|
+
<div className="flex gap-3 items-center w-full">
|
|
74
|
+
<img
|
|
75
|
+
src={chain.image}
|
|
76
|
+
alt={chain.name}
|
|
77
|
+
className="w-[34px] h-[34px]"
|
|
84
78
|
/>
|
|
79
|
+
<p>{chain.displayName}</p>
|
|
85
80
|
</div>
|
|
86
|
-
)}
|
|
87
81
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
position="BOTTOM"
|
|
94
|
-
triggerRef={tooltipTriggerRef}
|
|
95
|
-
/>
|
|
96
|
-
</div>
|
|
97
|
-
)}
|
|
82
|
+
{tooltipText && (
|
|
83
|
+
<div className="w-full text-center opacity-0 group-hover:opacity-100 text-xs transition-opacity duration-200">
|
|
84
|
+
{tooltipText}
|
|
85
|
+
</div>
|
|
86
|
+
)}
|
|
98
87
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
88
|
+
{connected && (
|
|
89
|
+
<div className="flex items-baseline gap-1">
|
|
90
|
+
<DotGreenIcon />
|
|
91
|
+
<div className="text-t_text_green">connected</div>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
103
95
|
</div>
|
|
104
96
|
);
|
|
105
97
|
};
|
|
@@ -9,10 +9,16 @@ 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 = ({
|
|
15
|
-
|
|
15
|
+
export const TokenItem = ({
|
|
16
|
+
token,
|
|
17
|
+
selectedTokenAddress,
|
|
18
|
+
onClick,
|
|
19
|
+
selectedChainId,
|
|
20
|
+
}: Props) => {
|
|
21
|
+
const { supportedChains, tokenPrices } = useSwapContext();
|
|
16
22
|
const { address } = useAccount();
|
|
17
23
|
|
|
18
24
|
const chain = useMemo(
|
|
@@ -20,11 +26,34 @@ export const TokenItem = ({ token, selectedTokenAddress, onClick }: Props) => {
|
|
|
20
26
|
[supportedChains, token],
|
|
21
27
|
);
|
|
22
28
|
|
|
29
|
+
const tokenPrice = useMemo(() => {
|
|
30
|
+
const prices = tokenPrices[token.chainId];
|
|
31
|
+
return prices?.[token.address];
|
|
32
|
+
}, [token, tokenPrices]);
|
|
33
|
+
|
|
34
|
+
const tokenBalance = useMemo(() => {
|
|
35
|
+
if (!token.balance || !token.decimals) return null;
|
|
36
|
+
return Number(ethers.utils.formatUnits(token.balance, token.decimals));
|
|
37
|
+
}, [token.balance, token.decimals]);
|
|
38
|
+
|
|
39
|
+
const hasBalance = useMemo(
|
|
40
|
+
() => token.balance && !token.balance.eq(0),
|
|
41
|
+
[token.balance],
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const tokenValueUsd = useMemo(() => {
|
|
45
|
+
if (!tokenBalance || !tokenPrice || !hasBalance) return null;
|
|
46
|
+
return tokenBalance * +tokenPrice;
|
|
47
|
+
}, [tokenBalance, tokenPrice, hasBalance]);
|
|
48
|
+
|
|
23
49
|
return (
|
|
24
50
|
<div
|
|
25
51
|
className={`token-picker-container ${
|
|
26
52
|
token.address.toLowerCase() === selectedTokenAddress?.toLowerCase() &&
|
|
27
|
-
|
|
53
|
+
selectedChainId &&
|
|
54
|
+
token.chainId === selectedChainId
|
|
55
|
+
? "bg-t_bg_tertiary bg-opacity-10"
|
|
56
|
+
: ""
|
|
28
57
|
}`}
|
|
29
58
|
onClick={onClick}
|
|
30
59
|
>
|
|
@@ -46,16 +75,21 @@ export const TokenItem = ({ token, selectedTokenAddress, onClick }: Props) => {
|
|
|
46
75
|
</div>
|
|
47
76
|
|
|
48
77
|
{token.balance && token.decimals ? (
|
|
49
|
-
<div className="text-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
58
|
-
|
|
78
|
+
<div className="text-right">
|
|
79
|
+
<div className="text-xs text-t_text_primary">
|
|
80
|
+
{token.balance.eq(0) || tokenBalance! > 0.0001
|
|
81
|
+
? weiToHumanReadable({
|
|
82
|
+
amount: token.balance.toString(),
|
|
83
|
+
decimals: token.decimals,
|
|
84
|
+
precisionFractionalPlaces: 4,
|
|
85
|
+
})
|
|
86
|
+
: "<0.0001"}
|
|
87
|
+
</div>
|
|
88
|
+
{hasBalance && (
|
|
89
|
+
<div className="text-xs text-t_text_primary text-opacity-50">
|
|
90
|
+
{tokenValueUsd ? `$${tokenValueUsd.toFixed(2)}` : "$ -"}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
59
93
|
</div>
|
|
60
94
|
) : (
|
|
61
95
|
<>
|