@xswap-link/sdk 0.9.8 → 0.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.global.js +52 -52
- package/dist/index.js +6 -6
- package/dist/index.mjs +6 -6
- package/package.json +1 -1
- package/src/components/Swap/SwapView/SwapPanel/TokenPanel/TokenPicker/index.tsx +21 -15
- package/src/context/SwapProvider.tsx +27 -2
- package/src/utils/validation.ts +22 -8
- package/test/context/SwapProvider.test.tsx +55 -0
package/package.json
CHANGED
|
@@ -236,37 +236,43 @@ export const TokenPicker = ({
|
|
|
236
236
|
// Create groups based on chain and wallet connection status
|
|
237
237
|
const groups = chain
|
|
238
238
|
? [
|
|
239
|
-
...(!!address && ownedFilteredTokens.length
|
|
239
|
+
...(!!address && ownedFilteredTokens.length && type === "source"
|
|
240
240
|
? [ownedFilteredTokens]
|
|
241
241
|
: []),
|
|
242
242
|
currentChainTokens,
|
|
243
243
|
otherChainTokens,
|
|
244
244
|
]
|
|
245
245
|
: [
|
|
246
|
-
...(!!address && ownedFilteredTokens.length
|
|
246
|
+
...(!!address && ownedFilteredTokens.length && type === "source"
|
|
247
247
|
? [ownedFilteredTokens]
|
|
248
248
|
: []),
|
|
249
249
|
[...currentChainTokens, ...otherChainTokens],
|
|
250
250
|
];
|
|
251
251
|
|
|
252
252
|
return groups.flat();
|
|
253
|
-
}, [filteredTokens, otherFilteredTokens, chain, address, tokenPrices]);
|
|
253
|
+
}, [filteredTokens, otherFilteredTokens, chain, address, type, tokenPrices]);
|
|
254
254
|
|
|
255
255
|
const ownedTokens = useMemo(
|
|
256
256
|
() => allTokens.filter((token) => token.balance?.gt(0)),
|
|
257
257
|
[allTokens],
|
|
258
258
|
);
|
|
259
259
|
|
|
260
|
-
const tokenGroups =
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
260
|
+
const tokenGroups = useMemo(() => {
|
|
261
|
+
return chain
|
|
262
|
+
? [
|
|
263
|
+
...(type === "source" && !!address && ownedTokens.length
|
|
264
|
+
? [ownedTokens]
|
|
265
|
+
: []),
|
|
266
|
+
filteredTokens,
|
|
267
|
+
otherFilteredTokens,
|
|
268
|
+
]
|
|
269
|
+
: [
|
|
270
|
+
...(type === "source" && !!address && ownedTokens.length
|
|
271
|
+
? [ownedTokens]
|
|
272
|
+
: []),
|
|
273
|
+
otherFilteredTokens,
|
|
274
|
+
];
|
|
275
|
+
}, [chain, type, address, ownedTokens, filteredTokens, otherFilteredTokens]);
|
|
270
276
|
|
|
271
277
|
return showImportWarning ? (
|
|
272
278
|
<>
|
|
@@ -459,7 +465,7 @@ export const TokenPicker = ({
|
|
|
459
465
|
if (!address) {
|
|
460
466
|
return `${chain.displayName} network`;
|
|
461
467
|
}
|
|
462
|
-
return ownedTokens.length
|
|
468
|
+
return ownedTokens.length && type === "source"
|
|
463
469
|
? "My tokens"
|
|
464
470
|
: `${chain.displayName} network`;
|
|
465
471
|
}
|
|
@@ -483,7 +489,7 @@ export const TokenPicker = ({
|
|
|
483
489
|
if (!address) {
|
|
484
490
|
return "All networks";
|
|
485
491
|
}
|
|
486
|
-
return ownedTokens.length
|
|
492
|
+
return ownedTokens.length && type === "source"
|
|
487
493
|
? "My tokens"
|
|
488
494
|
: "All networks";
|
|
489
495
|
}
|
|
@@ -533,11 +533,32 @@ export const SwapProvider = ({
|
|
|
533
533
|
!srcToken ||
|
|
534
534
|
!dstToken ||
|
|
535
535
|
!srcValueWei ||
|
|
536
|
-
safeBigNumberFrom(srcValueWei).eq(0)
|
|
536
|
+
safeBigNumberFrom(srcValueWei).eq(0) ||
|
|
537
|
+
(bridgeUI && !bridgeTokensDictionary)
|
|
537
538
|
) {
|
|
538
539
|
setRoute(undefined);
|
|
539
540
|
return;
|
|
540
541
|
}
|
|
542
|
+
|
|
543
|
+
// Validate the current path before requesting a route
|
|
544
|
+
const { source, target } = mapToValidPath({
|
|
545
|
+
source: { chain: srcChain, token: srcToken },
|
|
546
|
+
target: { chain: dstChain, token: dstToken },
|
|
547
|
+
type: bridgeUI ? "BRIDGE" : "SWAP",
|
|
548
|
+
supportedChains,
|
|
549
|
+
bridgeTokensDictionary: bridgeTokensDictionary || {},
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// If any of the chains or tokens would be changed by validation, don't request a route
|
|
553
|
+
if (
|
|
554
|
+
source.chain?.chainId !== srcChain?.chainId ||
|
|
555
|
+
target.chain?.chainId !== dstChain?.chainId ||
|
|
556
|
+
source.token?.address !== srcToken?.address ||
|
|
557
|
+
target.token?.address !== dstToken?.address
|
|
558
|
+
) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
|
|
541
562
|
try {
|
|
542
563
|
setError("");
|
|
543
564
|
setFetchingRoute(true);
|
|
@@ -570,16 +591,20 @@ export const SwapProvider = ({
|
|
|
570
591
|
routeAbortController.current = null;
|
|
571
592
|
} catch (error) {
|
|
572
593
|
if (error !== DEFAULT_REQUEST_ABORT_MSG) {
|
|
594
|
+
routeAbortController.current = null;
|
|
573
595
|
console.error(error);
|
|
574
596
|
setError("Could not find route!");
|
|
575
597
|
setRoute(undefined);
|
|
576
598
|
}
|
|
577
599
|
} finally {
|
|
578
|
-
|
|
600
|
+
if (!routeAbortController.current) {
|
|
601
|
+
setFetchingRoute(false);
|
|
602
|
+
}
|
|
579
603
|
}
|
|
580
604
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
581
605
|
}, [
|
|
582
606
|
address,
|
|
607
|
+
bridgeUI,
|
|
583
608
|
dstChain,
|
|
584
609
|
dstToken,
|
|
585
610
|
getRoute,
|
package/src/utils/validation.ts
CHANGED
|
@@ -346,18 +346,32 @@ const mapToValidPathBridge = ({
|
|
|
346
346
|
// Setting tokens
|
|
347
347
|
// check if the source token is bridgeable anywhere
|
|
348
348
|
if (newSourceToken && newSourceChain) {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
] || {},
|
|
354
|
-
).length > 0;
|
|
349
|
+
// First validate if the token is supported in the source chain
|
|
350
|
+
newSourceToken = newSourceChain.tokens.find(
|
|
351
|
+
(token) => token.tokenId === newSourceToken?.tokenId && token.supported,
|
|
352
|
+
);
|
|
355
353
|
|
|
356
|
-
if (
|
|
357
|
-
|
|
354
|
+
if (newSourceToken) {
|
|
355
|
+
const isBridgeable =
|
|
356
|
+
Object.keys(
|
|
357
|
+
bridgeTokensDictionary[newSourceChain.chainId]?.[
|
|
358
|
+
newSourceToken.address.toLowerCase()
|
|
359
|
+
] || {},
|
|
360
|
+
).length > 0;
|
|
361
|
+
|
|
362
|
+
if (!isBridgeable) {
|
|
363
|
+
newSourceToken = undefined;
|
|
364
|
+
}
|
|
358
365
|
}
|
|
359
366
|
}
|
|
360
367
|
|
|
368
|
+
// Validate if target token is supported in the target chain
|
|
369
|
+
if (newTargetToken && newTargetChain) {
|
|
370
|
+
newTargetToken = newTargetChain.tokens.find(
|
|
371
|
+
(token) => token.tokenId === newTargetToken?.tokenId && token.supported,
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
|
|
361
375
|
if (!newSourceToken) {
|
|
362
376
|
const defaultToken = getDefaultBridgeTokenForChain({
|
|
363
377
|
chain: newSourceChain,
|
|
@@ -759,6 +759,61 @@ describe("SwapProvider", () => {
|
|
|
759
759
|
result.current.dstChain?.chainId,
|
|
760
760
|
);
|
|
761
761
|
});
|
|
762
|
+
|
|
763
|
+
it("should preserve token when changing destination chain if token exists on both chains", async () => {
|
|
764
|
+
// Set up initial state with Arbitrum as source chain
|
|
765
|
+
const arbitrumChain = supportedChains.find(
|
|
766
|
+
(chain) => chain.chainId === "42161", // Arbitrum One
|
|
767
|
+
);
|
|
768
|
+
if (!arbitrumChain) throw new Error("Arbitrum chain not found");
|
|
769
|
+
|
|
770
|
+
// Find WETH token on Base
|
|
771
|
+
const arbitrumWethToken = arbitrumChain.tokens.find(
|
|
772
|
+
(token) => token.tokenId === "WETH",
|
|
773
|
+
);
|
|
774
|
+
if (!arbitrumWethToken)
|
|
775
|
+
throw new Error("WETH token not found on arbitrum");
|
|
776
|
+
|
|
777
|
+
// Set Base as initial destination chain
|
|
778
|
+
const baseChain = supportedChains.find(
|
|
779
|
+
(chain) => chain.chainId === "8453", // Base
|
|
780
|
+
);
|
|
781
|
+
if (!baseChain) throw new Error("Base chain not found");
|
|
782
|
+
|
|
783
|
+
const { result } = renderHook(() => useSwapContext(), {
|
|
784
|
+
wrapper: getWrapper(
|
|
785
|
+
{
|
|
786
|
+
...integrationConfig,
|
|
787
|
+
bridge: true,
|
|
788
|
+
srcChainId: arbitrumChain.chainId,
|
|
789
|
+
dstChainId: baseChain.chainId,
|
|
790
|
+
srcTokenAddr: arbitrumWethToken.address,
|
|
791
|
+
},
|
|
792
|
+
supportedChains,
|
|
793
|
+
wagmiConfig,
|
|
794
|
+
),
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
await act(async () => {
|
|
798
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
799
|
+
});
|
|
800
|
+
|
|
801
|
+
// Find Ethereum chain
|
|
802
|
+
const ethereumChain = supportedChains.find(
|
|
803
|
+
(chain) => chain.chainId === "1", // Ethereum
|
|
804
|
+
);
|
|
805
|
+
if (!ethereumChain) throw new Error("Ethereum chain not found");
|
|
806
|
+
|
|
807
|
+
// Change destination chain to Ethereum
|
|
808
|
+
await act(async () => {
|
|
809
|
+
result.current.setDstChain(ethereumChain);
|
|
810
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
// Verify that WETH is still selected but with Ethereum's address
|
|
814
|
+
expect(result.current.dstToken?.tokenId).toBe("WETH");
|
|
815
|
+
expect(result.current.dstChain?.chainId).toBe("1");
|
|
816
|
+
});
|
|
762
817
|
});
|
|
763
818
|
|
|
764
819
|
describe("Swap mode validation", () => {
|