@xswap-link/sdk 0.8.6 → 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.
- package/.github/workflows/main.yml +2 -1
- package/.github/workflows/pr_agent.yml +22 -0
- package/CHANGELOG.md +12 -0
- package/babel.config.cjs +8 -0
- package/dist/index.global.js +130 -133
- package/dist/index.js +10 -10
- 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 +5 -7
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/ChainPicker/index.tsx +18 -2
- package/src/components/Swap/SwapView/SwapPanel/ChainPanel/index.tsx +1 -24
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/QuickPickTokenItem/index.tsx +2 -11
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/TokenItem/index.tsx +12 -3
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +124 -34
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/index.tsx +1 -1
- package/src/components/Swap/SwapView/index.tsx +1 -1
- package/src/constants/index.ts +1 -1
- package/src/context/HistoryProvider.tsx +2 -2
- package/src/context/SwapProvider.tsx +163 -53
- package/src/context/TransactionProvider.tsx +1 -1
- package/src/utils/validation.ts +239 -186
- 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
|
@@ -19,10 +19,11 @@ 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;
|
|
25
|
-
chain
|
|
26
|
+
chain?: Chain;
|
|
26
27
|
tokens: TokenOption[];
|
|
27
28
|
otherTokens: TokenOption[];
|
|
28
29
|
selectedTokenAddress?: string;
|
|
@@ -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(() => {
|
|
@@ -59,7 +62,7 @@ export const TokenPicker = ({
|
|
|
59
62
|
}, [isOpen]);
|
|
60
63
|
|
|
61
64
|
const importCustomToken = useCallback(() => {
|
|
62
|
-
if (customTokenData && isETHAddressValid(searchValue)) {
|
|
65
|
+
if (chain && customTokenData && isETHAddressValid(searchValue)) {
|
|
63
66
|
let customTokens = {};
|
|
64
67
|
if (!isServer) {
|
|
65
68
|
customTokens = JSON.parse(
|
|
@@ -97,7 +100,7 @@ export const TokenPicker = ({
|
|
|
97
100
|
currentRouteRequestNonce.current = nonce;
|
|
98
101
|
const rpcUrl = supportedChains.find(
|
|
99
102
|
({ ecosystem, chainId }) =>
|
|
100
|
-
ecosystem === Ecosystem.EVM && chainId === chain
|
|
103
|
+
ecosystem === Ecosystem.EVM && chainId === chain?.chainId,
|
|
101
104
|
)?.publicRpcUrls[0];
|
|
102
105
|
const customTokenContract = new ethers.Contract(
|
|
103
106
|
searchValue.toLowerCase(),
|
|
@@ -134,6 +137,7 @@ export const TokenPicker = ({
|
|
|
134
137
|
if (
|
|
135
138
|
!bridgeUI &&
|
|
136
139
|
isETHAddressValid(searchValue) &&
|
|
140
|
+
chain &&
|
|
137
141
|
!findTokenDataByAddressAndChain(searchValue.toLowerCase(), chain.chainId)
|
|
138
142
|
) {
|
|
139
143
|
getCustomTokenData();
|
|
@@ -187,35 +191,63 @@ export const TokenPicker = ({
|
|
|
187
191
|
}, [isOpen]);
|
|
188
192
|
|
|
189
193
|
const allTokens = useMemo(() => {
|
|
190
|
-
const
|
|
191
|
-
...filteredTokens
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}),
|
|
213
|
-
];
|
|
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
|
+
);
|
|
204
|
+
|
|
205
|
+
const currentChainTokens = filteredTokens
|
|
206
|
+
.filter((token) => BigNumber.from(token?.balance || "0").eq(0))
|
|
207
|
+
.sort(
|
|
208
|
+
(tokenDataA, tokenDataB) => tokenDataB.priority - tokenDataA.priority,
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const otherChainTokens = otherFilteredTokens
|
|
212
|
+
.filter((token) => BigNumber.from(token?.balance || "0").eq(0))
|
|
213
|
+
.sort(
|
|
214
|
+
(tokenDataA, tokenDataB) => tokenDataB.priority - tokenDataA.priority,
|
|
215
|
+
);
|
|
214
216
|
|
|
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
|
+
];
|
|
217
232
|
|
|
218
|
-
|
|
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
|
+
];
|
|
219
251
|
|
|
220
252
|
return showImportWarning ? (
|
|
221
253
|
<>
|
|
@@ -312,6 +344,7 @@ export const TokenPicker = ({
|
|
|
312
344
|
<CloseIcon />
|
|
313
345
|
</div>
|
|
314
346
|
</div>
|
|
347
|
+
|
|
315
348
|
<TextInput
|
|
316
349
|
ref={tokenSearchRef}
|
|
317
350
|
inputIcon={
|
|
@@ -323,7 +356,13 @@ export const TokenPicker = ({
|
|
|
323
356
|
value={searchValue}
|
|
324
357
|
handleChange={setSearchValue}
|
|
325
358
|
/>
|
|
326
|
-
<div
|
|
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
|
+
>
|
|
327
366
|
{tokens
|
|
328
367
|
.filter((token) => token?.quickPick)
|
|
329
368
|
.map((token) => (
|
|
@@ -382,12 +421,62 @@ export const TokenPicker = ({
|
|
|
382
421
|
<GroupedVirtuoso
|
|
383
422
|
style={{ maxHeight: "400px", height: "100%" }}
|
|
384
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
|
+
}}
|
|
385
432
|
groupContent={(index) => (
|
|
386
433
|
<div className="py-1 bg-t_bg_primary text-t_text_primary">
|
|
387
434
|
<div className="px-4 text-left">
|
|
388
|
-
{
|
|
389
|
-
|
|
390
|
-
|
|
435
|
+
{(() => {
|
|
436
|
+
if (chain) {
|
|
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
|
+
}
|
|
478
|
+
}
|
|
479
|
+
})()}
|
|
391
480
|
</div>
|
|
392
481
|
<div className="horizontal-separator" />
|
|
393
482
|
{!tokenGroups[index]?.length && (
|
|
@@ -400,6 +489,7 @@ export const TokenPicker = ({
|
|
|
400
489
|
allTokens[index] && (
|
|
401
490
|
<TokenItem
|
|
402
491
|
token={allTokens[index]}
|
|
492
|
+
selectedChainId={chain?.chainId}
|
|
403
493
|
selectedTokenAddress={selectedTokenAddress}
|
|
404
494
|
onClick={() => {
|
|
405
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-
|
|
11
|
+
<div className="flex flex-col gap-[10px]">
|
|
12
12
|
<SwapPanel type="source" />
|
|
13
13
|
<ReorderButton />
|
|
14
14
|
<SwapPanel type="destination" />
|
package/src/constants/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const ROUTE_TIMEOUT_MS = 10000;
|
|
|
12
12
|
export const MINIMUM_DISPLAYED_TOKEN_AMOUNT = 0.0001;
|
|
13
13
|
export const DEFAULT_SOURCE_CHAIN_ID = "1";
|
|
14
14
|
export const DEFAULT_SOURCE_TOKEN_ADDR = constants.AddressZero;
|
|
15
|
-
export const DEFAULT_DESTINATION_CHAIN_ID = "
|
|
15
|
+
export const DEFAULT_DESTINATION_CHAIN_ID = "1";
|
|
16
16
|
export const DEFAULT_DESTINATION_TOKEN_ADDR =
|
|
17
17
|
"0x8fe815417913a93ea99049fc0718ee1647a2a07c"; // XSWAP token
|
|
18
18
|
export const CCIP_EXPLORER = "https://ccip.chain.link/";
|
|
@@ -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 {
|
|
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
|
});
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { TxConfigFormPayload } from "@src/components";
|
|
2
2
|
import {
|
|
3
3
|
DEFAULT_DESTINATION_CHAIN_ID,
|
|
4
|
-
DEFAULT_DESTINATION_TOKEN_ADDR,
|
|
5
4
|
DEFAULT_REQUEST_ABORT_MSG,
|
|
6
5
|
DEFAULT_SOURCE_CHAIN_ID,
|
|
7
|
-
DEFAULT_SOURCE_TOKEN_ADDR,
|
|
8
6
|
} from "@src/constants";
|
|
9
7
|
import { ADDRESSES } from "@src/contracts";
|
|
10
8
|
import {
|
|
@@ -32,9 +30,8 @@ import {
|
|
|
32
30
|
safeBigNumberFrom,
|
|
33
31
|
weiToHumanReadable,
|
|
34
32
|
} from "@src/utils";
|
|
35
|
-
import { mapToValidPath } from "@src/utils/validation";
|
|
36
33
|
import BigNumberJS from "bignumber.js";
|
|
37
|
-
import { BigNumber, constants } from "ethers";
|
|
34
|
+
import { BigNumber, constants, ethers } from "ethers";
|
|
38
35
|
import {
|
|
39
36
|
createContext,
|
|
40
37
|
Dispatch,
|
|
@@ -49,6 +46,7 @@ import {
|
|
|
49
46
|
} from "react";
|
|
50
47
|
import { Config, useAccount } from "wagmi";
|
|
51
48
|
import { useHistory } from "./HistoryProvider";
|
|
49
|
+
import { mapToValidPath } from "@src/utils/validation";
|
|
52
50
|
|
|
53
51
|
type Tab = "Swap" | "History" | "Settings";
|
|
54
52
|
type SwapContext = {
|
|
@@ -150,7 +148,7 @@ const init: SwapContext = {
|
|
|
150
148
|
dstValueMin: "",
|
|
151
149
|
lastTx: undefined,
|
|
152
150
|
expressDelivery: true,
|
|
153
|
-
infiniteApproval:
|
|
151
|
+
infiniteApproval: true,
|
|
154
152
|
slippage: "1.5",
|
|
155
153
|
route: undefined,
|
|
156
154
|
isAsyncDataLoaded: false,
|
|
@@ -194,7 +192,7 @@ export const SwapProvider = ({
|
|
|
194
192
|
wagmiConfig,
|
|
195
193
|
overlay,
|
|
196
194
|
}: Props) => {
|
|
197
|
-
const { address } = useAccount();
|
|
195
|
+
const { address, chainId: walletChainId } = useAccount();
|
|
198
196
|
const [tab, setTab] = useState(init.tab);
|
|
199
197
|
const [error, setError] = useState(init.error);
|
|
200
198
|
const [expressDelivery, setExpressDelivery] = useState(init.expressDelivery);
|
|
@@ -344,8 +342,8 @@ export const SwapProvider = ({
|
|
|
344
342
|
}, [bridgeTokens]);
|
|
345
343
|
|
|
346
344
|
const getTokenOptions = useCallback(
|
|
347
|
-
(chainId: string | undefined
|
|
348
|
-
if (!chainId
|
|
345
|
+
(chainId: string | undefined) => {
|
|
346
|
+
if (!chainId) {
|
|
349
347
|
return [];
|
|
350
348
|
}
|
|
351
349
|
|
|
@@ -560,16 +558,12 @@ export const SwapProvider = ({
|
|
|
560
558
|
}, [quoteRoute]);
|
|
561
559
|
|
|
562
560
|
useEffect(() => {
|
|
563
|
-
if (!srcChain || !dstChain) {
|
|
564
|
-
return;
|
|
565
|
-
}
|
|
566
561
|
(async () => {
|
|
567
562
|
// source chain
|
|
568
563
|
const srcTokenOptions: TokenOption[] = getTokenOptions(
|
|
569
|
-
srcChain
|
|
570
|
-
dstChain.chainId,
|
|
564
|
+
srcChain?.chainId,
|
|
571
565
|
).filter(({ symbol }) =>
|
|
572
|
-
dstChain
|
|
566
|
+
dstChain?.chainId === srcChain?.chainId
|
|
573
567
|
? symbol !== dstToken?.symbol
|
|
574
568
|
: true,
|
|
575
569
|
);
|
|
@@ -577,10 +571,9 @@ export const SwapProvider = ({
|
|
|
577
571
|
|
|
578
572
|
// destination chain
|
|
579
573
|
const dstTokenOptions: TokenOption[] = getTokenOptions(
|
|
580
|
-
dstChain
|
|
581
|
-
srcChain.chainId,
|
|
574
|
+
dstChain?.chainId,
|
|
582
575
|
).filter(({ symbol }) =>
|
|
583
|
-
dstChain
|
|
576
|
+
dstChain?.chainId === srcChain?.chainId
|
|
584
577
|
? symbol !== srcToken?.symbol
|
|
585
578
|
: true,
|
|
586
579
|
);
|
|
@@ -591,7 +584,7 @@ export const SwapProvider = ({
|
|
|
591
584
|
const srcOtherChains = supportedChains.filter(
|
|
592
585
|
({ chainId, web3Environment, swapSupported, bridgeSupported }) => {
|
|
593
586
|
if (
|
|
594
|
-
chainId === srcChain
|
|
587
|
+
chainId === srcChain?.chainId ||
|
|
595
588
|
web3Environment !== Web3Environment.MAINNET
|
|
596
589
|
) {
|
|
597
590
|
return false;
|
|
@@ -606,8 +599,8 @@ export const SwapProvider = ({
|
|
|
606
599
|
);
|
|
607
600
|
for (const { chainId } of srcOtherChains) {
|
|
608
601
|
srcOtherTokenOptions.push(
|
|
609
|
-
...getTokenOptions(chainId
|
|
610
|
-
chainId === dstChain
|
|
602
|
+
...getTokenOptions(chainId).filter(({ symbol }) =>
|
|
603
|
+
chainId === dstChain?.chainId ? symbol !== dstToken?.symbol : true,
|
|
611
604
|
),
|
|
612
605
|
);
|
|
613
606
|
}
|
|
@@ -617,14 +610,14 @@ export const SwapProvider = ({
|
|
|
617
610
|
const dstOtherTokenOptions: TokenOption[] = [];
|
|
618
611
|
const dstOtherChains = supportedChains.filter(
|
|
619
612
|
({ chainId, web3Environment, swapSupported }) =>
|
|
620
|
-
chainId !== dstChain
|
|
613
|
+
chainId !== dstChain?.chainId &&
|
|
621
614
|
web3Environment === Web3Environment.MAINNET &&
|
|
622
615
|
swapSupported,
|
|
623
616
|
);
|
|
624
617
|
for (const { chainId } of dstOtherChains) {
|
|
625
618
|
dstOtherTokenOptions.push(
|
|
626
|
-
...getTokenOptions(chainId
|
|
627
|
-
chainId === srcChain
|
|
619
|
+
...getTokenOptions(chainId).filter(({ symbol }) =>
|
|
620
|
+
chainId === srcChain?.chainId ? symbol !== srcToken?.symbol : true,
|
|
628
621
|
),
|
|
629
622
|
);
|
|
630
623
|
}
|
|
@@ -657,6 +650,7 @@ export const SwapProvider = ({
|
|
|
657
650
|
}
|
|
658
651
|
})();
|
|
659
652
|
}, [
|
|
653
|
+
bridgeUI,
|
|
660
654
|
srcChain,
|
|
661
655
|
dstChain,
|
|
662
656
|
address,
|
|
@@ -716,37 +710,149 @@ export const SwapProvider = ({
|
|
|
716
710
|
);
|
|
717
711
|
}, [srcChain]);
|
|
718
712
|
|
|
713
|
+
// Add new state to track initial setup
|
|
714
|
+
const [isInitialStateSet, setIsInitialStateSet] = useState(false);
|
|
715
|
+
|
|
719
716
|
useEffect(() => {
|
|
717
|
+
// apply wallet chain only after initial state is set
|
|
718
|
+
if (!walletChainId || !isInitialStateSet || integrationConfig?.srcChainId) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
720
721
|
const initSrcChain = supportedChains.find(
|
|
721
|
-
({ chainId }) =>
|
|
722
|
-
chainId === (integrationConfig?.srcChainId || DEFAULT_SOURCE_CHAIN_ID),
|
|
723
|
-
);
|
|
724
|
-
const initSrcToken = initSrcChain?.tokens.find(
|
|
725
|
-
({ address }) =>
|
|
726
|
-
address.toLowerCase() ===
|
|
727
|
-
(integrationConfig?.srcTokenAddr?.toLowerCase() ||
|
|
728
|
-
DEFAULT_SOURCE_TOKEN_ADDR.toLocaleLowerCase()),
|
|
722
|
+
({ chainId }) => chainId === `${walletChainId}`,
|
|
729
723
|
);
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
724
|
+
|
|
725
|
+
if (initSrcChain) {
|
|
726
|
+
setSrcChain(initSrcChain);
|
|
727
|
+
}
|
|
728
|
+
}, [
|
|
729
|
+
walletChainId,
|
|
730
|
+
isInitialStateSet,
|
|
731
|
+
supportedChains,
|
|
732
|
+
integrationConfig?.srcChainId,
|
|
733
|
+
]);
|
|
734
|
+
|
|
735
|
+
useEffect(() => {
|
|
736
|
+
if (isInitialStateSet) {
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
setSrcToken(undefined);
|
|
740
|
+
setDstToken(undefined);
|
|
741
|
+
setSrcChain(undefined);
|
|
742
|
+
setDstChain(undefined);
|
|
743
|
+
|
|
744
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
745
|
+
}, [bridgeUI]);
|
|
746
|
+
|
|
747
|
+
useEffect(() => {
|
|
748
|
+
// Skip if initial state was already set or wait if supported chains are not loaded yet
|
|
749
|
+
|
|
750
|
+
if (
|
|
751
|
+
isInitialStateSet ||
|
|
752
|
+
!supportedChains ||
|
|
753
|
+
supportedChains.length === 0 ||
|
|
754
|
+
!bridgeTokensDictionary
|
|
755
|
+
) {
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
|
|
759
|
+
let srcTokenAddress = integrationConfig.srcTokenAddr;
|
|
760
|
+
let dstTokenAddress = integrationConfig.dstTokenAddr;
|
|
761
|
+
// is it a valid eth address? if not try to find it by symbol / tokenId
|
|
762
|
+
if (srcTokenAddress && !ethers.utils.isAddress(srcTokenAddress)) {
|
|
763
|
+
srcTokenAddress = supportedChains
|
|
764
|
+
.find((chain) => chain.chainId === integrationConfig?.srcChainId)
|
|
765
|
+
?.tokens.find(
|
|
766
|
+
(token) => token.tokenId === integrationConfig?.srcTokenAddr,
|
|
767
|
+
)
|
|
768
|
+
?.address?.toLowerCase();
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
if (dstTokenAddress && !ethers.utils.isAddress(dstTokenAddress)) {
|
|
772
|
+
dstTokenAddress = supportedChains
|
|
773
|
+
.find((chain) => chain.chainId === integrationConfig?.dstChainId)
|
|
774
|
+
?.tokens.find(
|
|
775
|
+
(token) => token.tokenId === integrationConfig?.dstTokenAddr,
|
|
776
|
+
)
|
|
777
|
+
?.address?.toLowerCase();
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const searchedChainId =
|
|
781
|
+
// currently set chain
|
|
782
|
+
srcChain ||
|
|
783
|
+
// chain set by the integrator
|
|
784
|
+
integrationConfig?.srcChainId ||
|
|
785
|
+
// wallet chain
|
|
786
|
+
(walletChainId ? `${walletChainId}` : undefined) ||
|
|
787
|
+
// default chain
|
|
788
|
+
(bridgeUI ? undefined : DEFAULT_SOURCE_CHAIN_ID);
|
|
789
|
+
|
|
790
|
+
const initSrcChain = supportedChains.find(
|
|
791
|
+
({ chainId }) => chainId === searchedChainId,
|
|
740
792
|
);
|
|
741
793
|
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
794
|
+
let initSrcToken: Token | undefined;
|
|
795
|
+
if (initSrcChain) {
|
|
796
|
+
initSrcToken = initSrcChain.tokens.find(
|
|
797
|
+
({ address }) =>
|
|
798
|
+
address.toLowerCase() === srcTokenAddress?.toLowerCase(),
|
|
799
|
+
);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
let initDstChain: Chain | undefined = bridgeUI
|
|
803
|
+
? undefined
|
|
804
|
+
: supportedChains.find(
|
|
805
|
+
({ chainId }) => chainId === DEFAULT_DESTINATION_CHAIN_ID,
|
|
806
|
+
);
|
|
807
|
+
|
|
808
|
+
// if integrator set it, use it
|
|
809
|
+
if (integrationConfig.dstChainId) {
|
|
810
|
+
initDstChain = supportedChains.find(
|
|
811
|
+
({ chainId }) => chainId === integrationConfig.dstChainId,
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
let initDstToken: Token | undefined;
|
|
816
|
+
if (initDstChain) {
|
|
817
|
+
initDstToken = initDstChain.tokens.find(
|
|
818
|
+
({ address }) => address.toLowerCase() === dstTokenAddress,
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// the values here should not be from state but from the code above
|
|
823
|
+
const { source, target } = mapToValidPath({
|
|
824
|
+
source: { chain: initSrcChain, token: initSrcToken },
|
|
825
|
+
target: { chain: initDstChain, token: initDstToken },
|
|
826
|
+
type: bridgeUI ? "BRIDGE" : "SWAP",
|
|
827
|
+
supportedChains,
|
|
828
|
+
bridgeTokensDictionary,
|
|
829
|
+
});
|
|
830
|
+
|
|
831
|
+
setSrcChain(source.chain);
|
|
832
|
+
setDstChain(target.chain);
|
|
833
|
+
setSrcToken(source.token);
|
|
834
|
+
setDstToken(target.token);
|
|
835
|
+
// Mark initial state as set
|
|
836
|
+
setIsInitialStateSet(true);
|
|
837
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
838
|
+
}, [
|
|
839
|
+
integrationConfig?.srcChainId,
|
|
840
|
+
integrationConfig?.srcTokenAddr,
|
|
841
|
+
integrationConfig?.dstChainId,
|
|
842
|
+
integrationConfig?.dstTokenAddr,
|
|
843
|
+
supportedChains,
|
|
844
|
+
bridgeUI,
|
|
845
|
+
walletChainId,
|
|
846
|
+
isInitialStateSet,
|
|
847
|
+
bridgeTokensDictionary,
|
|
848
|
+
]);
|
|
747
849
|
|
|
748
850
|
useEffect(() => {
|
|
749
|
-
if (
|
|
851
|
+
if (
|
|
852
|
+
!bridgeTokensDictionary ||
|
|
853
|
+
supportedChains.length === 0 ||
|
|
854
|
+
!isInitialStateSet
|
|
855
|
+
) {
|
|
750
856
|
return;
|
|
751
857
|
}
|
|
752
858
|
|
|
@@ -789,11 +895,12 @@ export const SwapProvider = ({
|
|
|
789
895
|
} else if (window.xPayOnDstChainChange) {
|
|
790
896
|
window.xPayOnDstChainChange(dstChain?.chainId);
|
|
791
897
|
}
|
|
792
|
-
|
|
898
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
899
|
+
}, [integrationConfig.onDstChainChange, dstChain]);
|
|
793
900
|
|
|
794
901
|
useEffect(() => {
|
|
795
902
|
const token = dstToken
|
|
796
|
-
? { address: dstToken.address.toLowerCase(), symbol: dstToken.
|
|
903
|
+
? { address: dstToken.address.toLowerCase(), symbol: dstToken.tokenId }
|
|
797
904
|
: undefined;
|
|
798
905
|
|
|
799
906
|
if (integrationConfig.onDstTokenChange) {
|
|
@@ -801,7 +908,8 @@ export const SwapProvider = ({
|
|
|
801
908
|
} else if (window.xPayOnDstTokenChange) {
|
|
802
909
|
window.xPayOnDstTokenChange(token);
|
|
803
910
|
}
|
|
804
|
-
|
|
911
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
912
|
+
}, [integrationConfig.onDstTokenChange, dstToken]);
|
|
805
913
|
|
|
806
914
|
useEffect(() => {
|
|
807
915
|
if (integrationConfig.onSrcChainChange) {
|
|
@@ -809,11 +917,12 @@ export const SwapProvider = ({
|
|
|
809
917
|
} else if (window.xPayOnSrcChainChange) {
|
|
810
918
|
window.xPayOnSrcChainChange(srcChain?.chainId);
|
|
811
919
|
}
|
|
812
|
-
|
|
920
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
921
|
+
}, [integrationConfig.onSrcChainChange, srcChain]);
|
|
813
922
|
|
|
814
923
|
useEffect(() => {
|
|
815
924
|
const token = srcToken
|
|
816
|
-
? { address: srcToken.address.toLowerCase(), symbol: srcToken.
|
|
925
|
+
? { address: srcToken.address.toLowerCase(), symbol: srcToken.tokenId }
|
|
817
926
|
: undefined;
|
|
818
927
|
|
|
819
928
|
if (integrationConfig.onSrcTokenChange) {
|
|
@@ -821,7 +930,8 @@ export const SwapProvider = ({
|
|
|
821
930
|
} else if (window.xPayOnSrcTokenChange) {
|
|
822
931
|
window.xPayOnSrcTokenChange(token);
|
|
823
932
|
}
|
|
824
|
-
|
|
933
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
934
|
+
}, [integrationConfig.onSrcTokenChange, srcToken]);
|
|
825
935
|
|
|
826
936
|
return (
|
|
827
937
|
<swapContext.Provider
|