@xswap-link/sdk 0.9.8 → 0.9.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.9.8",
3
+ "version": "0.9.9",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -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 = chain
261
- ? [
262
- ...(!!address && ownedTokens.length ? [ownedTokens] : []),
263
- filteredTokens,
264
- otherFilteredTokens,
265
- ]
266
- : [
267
- ...(!!address && ownedTokens.length ? [ownedTokens] : []),
268
- otherFilteredTokens,
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);
@@ -580,6 +601,7 @@ export const SwapProvider = ({
580
601
  // eslint-disable-next-line react-hooks/exhaustive-deps
581
602
  }, [
582
603
  address,
604
+ bridgeUI,
583
605
  dstChain,
584
606
  dstToken,
585
607
  getRoute,
@@ -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
- const isBridgeable =
350
- Object.keys(
351
- bridgeTokensDictionary[newSourceChain.chainId]?.[
352
- newSourceToken.address.toLowerCase()
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 (!isBridgeable) {
357
- newSourceToken = undefined;
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", () => {