@xswap-link/sdk 0.12.1 → 0.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xswap-link/sdk",
3
- "version": "0.12.1",
3
+ "version": "0.13.0",
4
4
  "description": "JavaScript SDK for XSwap platform",
5
5
  "homepage": "https://github.com/xswap-link/xswap-sdk",
6
6
  "repository": {
@@ -11,11 +11,7 @@ import {
11
11
  crosschainEstimationTimes,
12
12
  } from "@src/constants";
13
13
  import { useHistory, useSwapContext, useTxUIWrapper } from "@src/context";
14
- import {
15
- ADDRESSES,
16
- CustomXSwapRouterAbi,
17
- XSwapRouterAbi,
18
- } from "@src/contracts";
14
+ import { ADDRESSES, XSwapRouterAbi } from "@src/contracts";
19
15
  import { useEvmContractApi } from "@src/hooks";
20
16
  import useNetworks from "@src/hooks/networkManagement/useNetworks";
21
17
  import {
@@ -131,11 +127,6 @@ export const TxOverview = ({ onCloseClick }: Props) => {
131
127
  "MessageSent(bytes32,uint64,address,bytes,address,uint256,uint256,address,uint256)"; // TODO think of getting this signature in a different way becase its easy to forgot about it when we will change event on smartcontract
132
128
  const messageReceivedEventSignature =
133
129
  "MessageReceived(bytes32,uint64,address,bytes,address,uint256)"; // TODO think of getting this signature in a different way because its easy to forgot about it when we will change event on smartcontract
134
- // cross-chain bridge via custom xswap router
135
- const messageSentXSwapCustomRouterEventSignature =
136
- "MessageSent(bytes32,uint64,address,address,address,address,uint256)"; // TODO think of getting this signature in a different way becase its easy to forgot about it when we will change event on smartcontract
137
- const messageReceivedXSwapCustomRouterEventSignature =
138
- "MessageReceived(bytes32,uint64,address,bytes)"; // TODO think of getting this signature in a different way because its easy to forgot about it when we will change event on smartcontract
139
130
 
140
131
  const destinationChainProvider = ethers.getDefaultProvider(
141
132
  supportedChains.find(
@@ -146,7 +137,6 @@ export const TxOverview = ({ onCloseClick }: Props) => {
146
137
  );
147
138
 
148
139
  const handleMessageEvent = async (
149
- contractKey: "XSwapRouter" | "CustomXSwapRouter",
150
140
  messageSentEventSignature: string,
151
141
  messageReceivedEventSignature: string,
152
142
  ) => {
@@ -159,15 +149,13 @@ export const TxOverview = ({ onCloseClick }: Props) => {
159
149
  transactionData.messageId = messageId;
160
150
 
161
151
  const xSwapRouterOnDestination = new ethers.Contract(
162
- ADDRESSES[dstChain!.chainId]![contractKey]!,
163
- contractKey === "XSwapRouter"
164
- ? XSwapRouterAbi
165
- : CustomXSwapRouterAbi,
152
+ ADDRESSES[dstChain!.chainId]!.XSwapRouter!,
153
+ XSwapRouterAbi,
166
154
  destinationChainProvider,
167
155
  );
168
156
 
169
157
  const eventFilter = {
170
- address: ADDRESSES[dstChain.chainId]?.[contractKey],
158
+ address: ADDRESSES[dstChain.chainId]?.XSwapRouter,
171
159
  topics: [
172
160
  ethers.utils.id(messageReceivedEventSignature),
173
161
  messageId,
@@ -194,16 +182,9 @@ export const TxOverview = ({ onCloseClick }: Props) => {
194
182
  };
195
183
 
196
184
  await handleMessageEvent(
197
- "XSwapRouter",
198
185
  messageSentEventSignature,
199
186
  messageReceivedEventSignature,
200
187
  );
201
-
202
- await handleMessageEvent(
203
- "CustomXSwapRouter",
204
- messageSentXSwapCustomRouterEventSignature,
205
- messageReceivedXSwapCustomRouterEventSignature,
206
- );
207
188
  }
208
189
 
209
190
  addTransactionToLocalHistory(transactionData, txReceipt.from);
@@ -110,6 +110,7 @@ export const TxConfigForm = (props: TxConfigFormProps) => {
110
110
  integratorFee,
111
111
  integratorFeeReceiverAddress,
112
112
  highlightedDstTokens,
113
+ wagmiConfig: providedWagmiConfig,
113
114
  } = normalizedProps;
114
115
 
115
116
  const onBackdropClick = (e: MouseEvent) => {
@@ -122,8 +123,11 @@ export const TxConfigForm = (props: TxConfigFormProps) => {
122
123
  };
123
124
 
124
125
  const wagmiConfig = useMemo(
125
- () => getWagmiConfig(supportedChains),
126
- [supportedChains],
126
+ () =>
127
+ providedWagmiConfig ??
128
+ window.xPayWagmiConfig ??
129
+ getWagmiConfig(supportedChains),
130
+ [providedWagmiConfig, supportedChains],
127
131
  );
128
132
 
129
133
  useEffect(() => {
@@ -223,6 +227,7 @@ export const openTxConfigForm = async ({
223
227
  srcTokenLocked,
224
228
  srcChainLocked,
225
229
  override,
230
+ wagmiConfig,
226
231
  }: TxConfigFormPayload): Promise<UnifiedRoute> => {
227
232
  try {
228
233
  if (xpayRoot === null) {
@@ -267,6 +272,7 @@ export const openTxConfigForm = async ({
267
272
  srcTokenLocked={srcTokenLocked}
268
273
  srcChainLocked={srcChainLocked}
269
274
  override={override}
275
+ wagmiConfig={wagmiConfig}
270
276
  />,
271
277
  );
272
278
  });
@@ -51,7 +51,13 @@ export const TxWidgetWCWrapped = ({
51
51
  integratorFee,
52
52
  integratorFeeReceiverAddress,
53
53
  highlightedDstTokens,
54
+ wagmiConfig,
54
55
  }: WidgetIntegrationPayload) => {
56
+ // Must be synchronous: the WC mounts its inner React tree before useEffect fires.
57
+ if (typeof window !== "undefined") {
58
+ window.xPayWagmiConfig = wagmiConfig;
59
+ }
60
+
55
61
  useEffect(() => {
56
62
  // We couldn't find a way to pass a function to the web component created by @r2wc/core.
57
63
  // It is assigned to window to be accessed later from within the web component.
@@ -4,7 +4,7 @@ import { defineChain, Transport } from "viem";
4
4
  import * as WagmiChains from "viem/chains";
5
5
  import { mainnet } from "viem/chains";
6
6
  import { Config, createConfig, createStorage, http } from "@wagmi/core";
7
- import { walletConnect } from "@wagmi/connectors";
7
+ import { injected, walletConnect } from "@wagmi/connectors";
8
8
 
9
9
  const storage = createStorage({
10
10
  key: "xpay.wagmi.store",
@@ -18,6 +18,7 @@ export const getWagmiConfig = (supportedChains: Chain[]): Config => {
18
18
  chains,
19
19
  transports,
20
20
  connectors: [
21
+ injected(),
21
22
  walletConnect({
22
23
  projectId: "c392898b45ac587a280b5eb33e92aeb0",
23
24
  showQrModal: true,
@@ -8,7 +8,7 @@ import {
8
8
  SOLANA_TOKEN_2022_PROGRAM_ID,
9
9
  SOLANA_TOKEN_PROGRAM_ID,
10
10
  } from "@src/constants";
11
- import { ADDRESSES } from "@src/contracts";
11
+ import { ADDRESSES, resolveBridgeDisplayToken } from "@src/contracts";
12
12
  import {
13
13
  BridgeToken,
14
14
  BridgeTokensDictionary,
@@ -383,8 +383,15 @@ export const SwapProvider = ({
383
383
  // Skip invalid token pairs.
384
384
  continue;
385
385
  }
386
- const [chainId0, address0] = entries[0]!;
387
- const [chainId1, address1] = entries[1]!;
386
+ const [chainId0, rawAddress0] = entries[0]!;
387
+ const [chainId1, rawAddress1] = entries[1]!;
388
+
389
+ // Some pairs use an internal, CCIP-connected token (e.g. a wrapped
390
+ // variant) that the backend relies on for routing. In the bridge UI we
391
+ // display/select the user-facing token instead; the backend wraps it into
392
+ // the CCIP token when building the route.
393
+ const address0 = resolveBridgeDisplayToken(chainId0, rawAddress0);
394
+ const address1 = resolveBridgeDisplayToken(chainId1, rawAddress1);
388
395
 
389
396
  addMapping(chainId0, address0, chainId1, address1);
390
397
  addMapping(chainId1, address1, chainId0, address0);
@@ -673,19 +680,24 @@ export const SwapProvider = ({
673
680
 
674
681
  const nonce = crypto.randomUUID();
675
682
  refreshSelectedTokenBalanceNonce.current = nonce;
676
-
677
683
  setIsFetchingBalance(true);
678
684
  let balance = "0";
679
685
  if (srcChain?.ecosystem === Ecosystem.EVM && evmAddress) {
680
- const balanceUI = await getBalanceOf(
681
- evmAddress,
682
- srcToken.address,
683
- srcChain?.publicRpcUrls[0],
684
- );
685
- balance = humanReadableToWei({
686
- amount: balanceUI,
687
- decimals: srcToken.decimals,
688
- });
686
+ const balanceFromBackend =
687
+ tokenBalances[srcChain?.chainId]?.[srcToken.address];
688
+ if (balanceFromBackend === undefined) {
689
+ const balanceUI = await getBalanceOf(
690
+ evmAddress,
691
+ srcToken.address,
692
+ srcChain?.publicRpcUrls[0],
693
+ );
694
+ balance = humanReadableToWei({
695
+ amount: balanceUI,
696
+ decimals: srcToken.decimals,
697
+ });
698
+ } else {
699
+ balance = balanceFromBackend;
700
+ }
689
701
  } else if (srcChain?.ecosystem === Ecosystem.SOLANA && solanaAddress) {
690
702
  const solanaPublicKey = new PublicKey(solanaAddress);
691
703
  const connection = new Connection(srcChain.publicRpcUrls[0]!);
@@ -719,7 +731,6 @@ export const SwapProvider = ({
719
731
  }
720
732
  }
721
733
  }
722
-
723
734
  if (nonce !== refreshSelectedTokenBalanceNonce.current) {
724
735
  return;
725
736
  }
@@ -727,7 +738,7 @@ export const SwapProvider = ({
727
738
  setSrcTokenBalanceWei(balance);
728
739
 
729
740
  setIsFetchingBalance(false);
730
- }, [evmAddress, solanaAddress, srcChain, srcToken]);
741
+ }, [evmAddress, solanaAddress, srcChain, srcToken, tokenBalances]);
731
742
  // retrieves token details for a given token address and chain from supported tokens mapping. (including imported tokens)
732
743
  const findTokenDataByAddressAndChain = useCallback(
733
744
  (tokenAddress: string, chainId: string): Token | undefined => {
@@ -796,10 +807,8 @@ export const SwapProvider = ({
796
807
  // fetches user all token balances when a wallet address is available and supported chains are loaded.
797
808
  useEffect(() => {
798
809
  let isCancelled = false;
799
-
800
810
  if ((evmAddress || solanaAddress) && supportedChains.length > 0) {
801
811
  setIsFetchingBalances(true);
802
-
803
812
  getBalances({
804
813
  walletAddress: evmAddress,
805
814
  solanaWalletAddress: solanaAddress,
@@ -2,12 +2,10 @@ import BatchQuery from "./BatchQuery.json";
2
2
  import ERC20 from "./ERC20.json";
3
3
  import XSwapRouter from "./XSwapRouter.json";
4
4
  import XSwapTokenFeeDistributor from "./XSwapTokenFeeDistributor.json";
5
- import CustomXSwapRouter from "./CustomXSwapRouter.json";
6
5
  import StakingStableAPR from "./StakingStableAPR.json";
7
6
 
8
7
  export const BatchQueryAbi = BatchQuery;
9
8
  export const ERC20Abi = ERC20;
10
9
  export const XSwapRouterAbi = XSwapRouter;
11
10
  export const XSwapTokenFeeDistributorAbi = XSwapTokenFeeDistributor;
12
- export const CustomXSwapRouterAbi = CustomXSwapRouter;
13
11
  export const StakingStableAPRAbi = StakingStableAPR;
@@ -23,8 +23,6 @@ export const ADDRESSES: Addresses = {
23
23
  "8453": {
24
24
  [ContractName.USDC]: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
25
25
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
26
- [ContractName.CustomXSwapRouter]:
27
- "0x9B64aC0aD647768838a3957Bc12c40E1C539cb93",
28
26
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
29
27
  [ContractName.StakingCosmicCadets]:
30
28
  "0x67798736bFb7d9A1F143215E258fe15Da68c85F0",
@@ -41,8 +39,6 @@ export const ADDRESSES: Addresses = {
41
39
  "1": {
42
40
  [ContractName.USDC]: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
43
41
  [ContractName.BatchQuery]: "0xb5f9934ea0810ef5faa677e8c68c6983b88664b1",
44
- [ContractName.CustomXSwapRouter]:
45
- "0x9B64aC0aD647768838a3957Bc12c40E1C539cb93",
46
42
  [ContractName.FeeCollector]: "0xb37275558f02f05104c2ba35199d16adeb43432f",
47
43
  [ContractName.XSwapRouter]: "0xe1c14b9f065dead2e89ee35382f8bd42bdb87a04",
48
44
  [ContractName.XSwapToken]: "0x8fe815417913a93ea99049fc0718ee1647a2a07c",
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Bridge UI display overrides.
3
+ *
4
+ * Some tokens returned by the `/bridgeTokens` endpoint are internal,
5
+ * directly-CCIP-connected tokens (e.g. a wrapped variant) that the backend
6
+ * relies on for routing, but that we do NOT want users to see or pick in the
7
+ * bridge UI. This map replaces such a CCIP token with the user-facing token we
8
+ * display and let users select / hold instead.
9
+ *
10
+ * The backend accepts the user-facing token in `/route` and wraps it into the
11
+ * CCIP token under the hood (e.g. real USDC -> XSwapUSDC via the wrapper), so
12
+ * the selected token is what flows through balances, approval and the route
13
+ * request, while the actual cross-chain hop still uses the CCIP token.
14
+ *
15
+ * Shape: { [chainId]: { [ccipTokenAddressLowercased]: displayTokenAddressLowercased } }
16
+ *
17
+ * IMPORTANT: addresses must be lowercased on both sides — the bridge dictionary
18
+ * and its consumers compare EVM addresses lowercased.
19
+ */
20
+ export const bridgeDisplayTokenOverrides: Record<
21
+ string,
22
+ Record<string, string>
23
+ > = {
24
+ // base: XSwapUSDC -> real USDC
25
+ "8453": {
26
+ "0x5426188a207fd0365dca5cff345059ff531d1aa7":
27
+ "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
28
+ },
29
+ };
30
+
31
+ /**
32
+ * Returns the user-facing token address to display in the bridge UI for a given
33
+ * `/bridgeTokens` entry, or the original address when there is no override.
34
+ *
35
+ * EVM addresses are matched case-insensitively; non-EVM addresses (e.g. Solana)
36
+ * are returned untouched.
37
+ */
38
+ export const resolveBridgeDisplayToken = (
39
+ chainId: string,
40
+ tokenAddress: string,
41
+ ): string => {
42
+ return (
43
+ bridgeDisplayTokenOverrides[chainId]?.[tokenAddress.toLowerCase()] ??
44
+ tokenAddress
45
+ );
46
+ };
@@ -1,2 +1,3 @@
1
1
  export * from "./abi";
2
2
  export * from "./addresses";
3
+ export * from "./bridgeDisplayTokens";
@@ -9,7 +9,6 @@ export type Contracts = Partial<{
9
9
  export enum ContractName {
10
10
  USDC = "USDC",
11
11
  BatchQuery = "BatchQuery",
12
- CustomXSwapRouter = "CustomXSwapRouter",
13
12
  FeeCollector = "FeeCollector",
14
13
  StakingCosmicCadets = "StakingCosmicCadets",
15
14
  StakingSolarDivision = "StakingSolarDivision",
@@ -1,4 +1,5 @@
1
1
  import { ContractCall, Transaction } from "@src/models";
2
+ import { Config } from "@wagmi/core";
2
3
 
3
4
  export type ModalIntegrationThemeStyles = {
4
5
  mainAccentLight?: string;
@@ -71,4 +72,5 @@ export type ModalIntegrationPayload = {
71
72
  integratorFee?: number;
72
73
  integratorFeeReceiverAddress?: string;
73
74
  highlightedDstTokens?: string[];
75
+ wagmiConfig?: Config;
74
76
  };
@@ -1,4 +1,5 @@
1
1
  import { ContractCall, ModalIntegrationStyles, Transaction } from "@src/models";
2
+ import { Config } from "@wagmi/core";
2
3
 
3
4
  export type WidgetIntegrationPayload = {
4
5
  integratorId: string;
@@ -33,4 +34,5 @@ export type WidgetIntegrationPayload = {
33
34
  integratorFee?: number;
34
35
  integratorFeeReceiverAddress?: string;
35
36
  highlightedDstTokens?: string[];
37
+ wagmiConfig?: Config;
36
38
  };
@@ -26,6 +26,7 @@ export const openTransactionModal = async ({
26
26
  onSrcChainChange,
27
27
  bridge,
28
28
  highlightedDstTokens,
29
+ wagmiConfig,
29
30
  }: ModalIntegrationPayload) => {
30
31
  const supportedChains: Chain[] = (await getChains()).filter(
31
32
  ({ web3Environment, swapSupported, bridgeSupported }) =>
@@ -66,6 +67,7 @@ export const openTransactionModal = async ({
66
67
  onSrcChainChange,
67
68
  bridge,
68
69
  highlightedDstTokens,
70
+ wagmiConfig,
69
71
  });
70
72
  };
71
73
 
@@ -1,4 +1,5 @@
1
1
  import { Transaction } from "@src/models";
2
+ import { Config } from "@wagmi/core";
2
3
 
3
4
  declare global {
4
5
  interface Window {
@@ -12,5 +13,6 @@ declare global {
12
13
  ) => void;
13
14
  xPayOnSrcChainChange?: (chain: string | undefined) => void;
14
15
  xPayOnConnectWallet?: () => void;
16
+ xPayWagmiConfig?: Config;
15
17
  }
16
18
  }