@sodax/wallet-sdk-react 0.0.1-rc.2

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.
Files changed (66) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +233 -0
  3. package/dist/index.cjs +1589 -0
  4. package/dist/index.cjs.map +1 -0
  5. package/dist/index.d.cts +15970 -0
  6. package/dist/index.d.ts +15970 -0
  7. package/dist/index.mjs +1513 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +74 -0
  10. package/src/SodaxWalletProvider.tsx +55 -0
  11. package/src/actions/getXChainType.ts +10 -0
  12. package/src/actions/getXService.ts +25 -0
  13. package/src/actions/index.ts +2 -0
  14. package/src/assets/wallets/hana.svg +6 -0
  15. package/src/assets/wallets/havah.svg +76 -0
  16. package/src/assets/wallets/keplr.svg +30 -0
  17. package/src/assets/wallets/metamask.svg +60 -0
  18. package/src/assets/wallets/phantom.svg +4 -0
  19. package/src/assets/wallets/sui.svg +20 -0
  20. package/src/constants/index.ts +1 -0
  21. package/src/constants/xChains.ts +164 -0
  22. package/src/core/XConnector.ts +54 -0
  23. package/src/core/XService.ts +91 -0
  24. package/src/core/index.ts +2 -0
  25. package/src/hooks/index.ts +11 -0
  26. package/src/hooks/useEthereumChainId.ts +36 -0
  27. package/src/hooks/useEvmSwitchChain.ts +92 -0
  28. package/src/hooks/useWalletProvider.ts +149 -0
  29. package/src/hooks/useWalletProviderOptions.ts +51 -0
  30. package/src/hooks/useXAccount.ts +51 -0
  31. package/src/hooks/useXAccounts.ts +56 -0
  32. package/src/hooks/useXBalances.ts +66 -0
  33. package/src/hooks/useXConnect.ts +119 -0
  34. package/src/hooks/useXConnection.ts +72 -0
  35. package/src/hooks/useXConnectors.ts +56 -0
  36. package/src/hooks/useXDisconnect.ts +65 -0
  37. package/src/hooks/useXService.ts +8 -0
  38. package/src/index.ts +18 -0
  39. package/src/types/index.ts +44 -0
  40. package/src/useXWagmiStore.tsx +164 -0
  41. package/src/utils/index.ts +33 -0
  42. package/src/xchains/evm/EvmXConnector.ts +27 -0
  43. package/src/xchains/evm/EvmXService.ts +189 -0
  44. package/src/xchains/evm/index.ts +2 -0
  45. package/src/xchains/icon/IconHanaXConnector.ts +39 -0
  46. package/src/xchains/icon/IconXService.ts +115 -0
  47. package/src/xchains/icon/iconex/index.tsx +46 -0
  48. package/src/xchains/icon/index.ts +2 -0
  49. package/src/xchains/injective/InjectiveKelprXConnector.ts +37 -0
  50. package/src/xchains/injective/InjectiveMetamaskXConnector.ts +40 -0
  51. package/src/xchains/injective/InjectiveXService.ts +71 -0
  52. package/src/xchains/injective/index.ts +4 -0
  53. package/src/xchains/injective/utils.ts +17 -0
  54. package/src/xchains/solana/SolanaXConnector.ts +26 -0
  55. package/src/xchains/solana/SolanaXService.ts +50 -0
  56. package/src/xchains/solana/hooks/useAnchorProvider.tsx +9 -0
  57. package/src/xchains/solana/index.ts +2 -0
  58. package/src/xchains/stellar/CustomSorobanServer.ts +93 -0
  59. package/src/xchains/stellar/StellarWalletsKitXConnector.ts +45 -0
  60. package/src/xchains/stellar/StellarXService.ts +61 -0
  61. package/src/xchains/stellar/index.tsx +2 -0
  62. package/src/xchains/stellar/useStellarXConnectors.ts +30 -0
  63. package/src/xchains/stellar/utils.ts +49 -0
  64. package/src/xchains/sui/SuiXConnector.ts +28 -0
  65. package/src/xchains/sui/SuiXService.ts +56 -0
  66. package/src/xchains/sui/index.ts +2 -0
@@ -0,0 +1,36 @@
1
+ import type { InjectiveXService } from '@/xchains/injective';
2
+ import { Wallet } from '@injectivelabs/wallet-base';
3
+ import React from 'react';
4
+ import { useEffect } from 'react';
5
+ import { useXService } from './useXService';
6
+ import type { EvmWalletStrategy } from '@injectivelabs/wallet-evm';
7
+
8
+ /**
9
+ * React hook that returns the current Ethereum chain ID when using MetaMask wallet for Injective.
10
+ * Listens for chain changes and updates the state accordingly.
11
+ *
12
+ * @remarks
13
+ * This hook only works with MetaMask wallet and requires the window.ethereum provider to be available.
14
+ * For other wallets or when MetaMask is not available, it returns null.
15
+ *
16
+ * @returns The current Ethereum chain ID as a number, or null if not available/connected
17
+ */
18
+ export default function useEthereumChainId(): number | null {
19
+ const injectiveXService = useXService('INJECTIVE') as unknown as InjectiveXService;
20
+ const [ethereumChainId, setEthereumChainId] = React.useState<number | null>(null);
21
+ useEffect(() => {
22
+ if (!injectiveXService?.walletStrategy?.getWallet()) return;
23
+ const walletStrategy = injectiveXService.walletStrategy;
24
+ if (walletStrategy.getWallet() !== Wallet.Metamask) return;
25
+
26
+ const getEthereumChainId = async () => {
27
+ const chainId = await walletStrategy.getEthereumChainId();
28
+ setEthereumChainId(Number.parseInt(chainId));
29
+ };
30
+ getEthereumChainId();
31
+
32
+ (walletStrategy.getStrategy() as EvmWalletStrategy).onChainIdChanged(getEthereumChainId);
33
+ }, [injectiveXService?.walletStrategy]);
34
+
35
+ return ethereumChainId;
36
+ }
@@ -0,0 +1,92 @@
1
+ import { xChainMap } from '@/constants/xChains';
2
+ import { useCallback, useMemo } from 'react';
3
+ import { useAccount, useSwitchChain } from 'wagmi';
4
+ import type { ChainId } from '@sodax/types';
5
+ import { getXChainType } from '@/actions';
6
+ import type { InjectiveXService } from '@/xchains/injective';
7
+ import { useXService } from '@/hooks/useXService';
8
+ import useEthereumChainId from './useEthereumChainId';
9
+ import { mainnet } from 'viem/chains';
10
+ import { Wallet } from '@injectivelabs/wallet-base';
11
+
12
+ interface UseEvmSwitchChainReturn {
13
+ isWrongChain: boolean;
14
+ handleSwitchChain: () => void;
15
+ }
16
+
17
+ /**
18
+ * Hook to handle EVM chain switching functionality
19
+ *
20
+ * @param expectedXChainId - The target chain ID to switch to (e.g. '0xa.optimism', '0x89.polygon')
21
+ * @returns {Object} Object containing:
22
+ * - isWrongChain: boolean indicating if current chain differs from expected chain
23
+ * - handleSwitchChain: function to trigger chain switch to expected chain
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * function ChainSwitchButton({ targetChain }: { targetChain: ChainId }) {
28
+ * const { isWrongChain, handleSwitchChain } = useEvmSwitchChain(targetChain);
29
+ *
30
+ * return (
31
+ * <Button onClick={handleSwitchChain} disabled={!isWrongChain}>
32
+ * Switch Network
33
+ * </Button>
34
+ * );
35
+ * }
36
+ * ```
37
+ */
38
+
39
+ export const switchEthereumChain = async () => {
40
+ const metamaskProvider = (window as any).ethereum as any;
41
+
42
+ return await Promise.race([
43
+ metamaskProvider.request({
44
+ method: 'wallet_switchEthereumChain',
45
+ params: [{ chainId: '0x1' }],
46
+ }),
47
+ new Promise<void>(resolve =>
48
+ metamaskProvider.on('change', ({ chain }: { chain: { id: number } }) => {
49
+ if (chain?.id === 1) {
50
+ resolve();
51
+ }
52
+ }),
53
+ ),
54
+ ]);
55
+ };
56
+
57
+ export const useEvmSwitchChain = (expectedXChainId: ChainId): UseEvmSwitchChainReturn => {
58
+ const xChainType = getXChainType(expectedXChainId);
59
+ const expectedChainId = xChainMap[expectedXChainId].id as number;
60
+
61
+ const injectiveXService = useXService('INJECTIVE') as unknown as InjectiveXService;
62
+ const ethereumChainId = useEthereumChainId();
63
+
64
+ const { chainId } = useAccount();
65
+ const isWrongChain = useMemo(() => {
66
+ return (
67
+ (xChainType === 'EVM' && chainId !== expectedChainId) ||
68
+ (xChainType === 'INJECTIVE' &&
69
+ injectiveXService &&
70
+ injectiveXService.walletStrategy.getWallet() === Wallet.Metamask &&
71
+ ethereumChainId !== mainnet.id)
72
+ );
73
+ }, [xChainType, chainId, expectedChainId, ethereumChainId, injectiveXService]);
74
+
75
+ const { switchChain } = useSwitchChain();
76
+
77
+ const handleSwitchChain = useCallback(() => {
78
+ if (xChainType === 'INJECTIVE') {
79
+ switchEthereumChain();
80
+ } else {
81
+ switchChain({ chainId: expectedChainId });
82
+ }
83
+ }, [switchChain, expectedChainId, xChainType]);
84
+
85
+ return useMemo(
86
+ () => ({
87
+ isWrongChain,
88
+ handleSwitchChain,
89
+ }),
90
+ [isWrongChain, handleSwitchChain],
91
+ );
92
+ };
@@ -0,0 +1,149 @@
1
+ import type { ChainId } from '@sodax/types';
2
+ import { useMemo } from 'react';
3
+ import {
4
+ EvmWalletProvider,
5
+ IconWalletProvider,
6
+ SuiWalletProvider,
7
+ InjectiveWalletProvider,
8
+ StellarWalletProvider,
9
+ SolanaWalletProvider,
10
+ } from '@sodax/wallet-sdk-core';
11
+ import { getXChainType } from '../actions';
12
+ import type { InjectiveEoaAddress } from '@sodax/types';
13
+ import { usePublicClient, useWalletClient } from 'wagmi';
14
+ import { getWagmiChainId } from '../utils';
15
+ import { type SolanaXService, type StellarXService, useXAccount, useXService } from '..';
16
+ import type { SuiXService } from '../xchains/sui/SuiXService';
17
+ import { CHAIN_INFO, SupportedChainId } from '../xchains/icon/IconXService';
18
+ import type { InjectiveXService } from '../xchains/injective/InjectiveXService';
19
+
20
+ /**
21
+ * Hook to get the appropriate wallet provider based on the chain type.
22
+ * Supports EVM, SUI, ICON and INJECTIVE chains.
23
+ *
24
+ * @param {ChainId | undefined} spokeChainId - The chain ID to get the wallet provider for. Can be any valid ChainId value.
25
+ * @returns {EvmWalletProvider | SuiWalletProvider | IconWalletProvider | InjectiveWalletProvider | undefined}
26
+ * The appropriate wallet provider instance for the given chain ID, or undefined if:
27
+ * - No chain ID is provided
28
+ * - Chain type is not supported
29
+ * - Required wallet provider options are not available
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * // Get wallet provider for a specific chain
34
+ * const walletProvider = useWalletProvider('sui');
35
+ * ```
36
+ */
37
+ export function useWalletProvider(
38
+ spokeChainId: ChainId | undefined,
39
+ ):
40
+ | EvmWalletProvider
41
+ | SuiWalletProvider
42
+ | IconWalletProvider
43
+ | InjectiveWalletProvider
44
+ | StellarWalletProvider
45
+ | SolanaWalletProvider
46
+ | undefined {
47
+ const xChainType = getXChainType(spokeChainId);
48
+
49
+ // EVM-specific hooks
50
+ const evmPublicClient = usePublicClient({
51
+ chainId: spokeChainId ? getWagmiChainId(spokeChainId) : undefined,
52
+ });
53
+ const { data: evmWalletClient } = useWalletClient({
54
+ chainId: spokeChainId ? getWagmiChainId(spokeChainId) : undefined,
55
+ });
56
+
57
+ // Cross-chain hooks
58
+ const xService = useXService(getXChainType(spokeChainId));
59
+ const xAccount = useXAccount(spokeChainId);
60
+
61
+ return useMemo(() => {
62
+ switch (xChainType) {
63
+ case 'EVM': {
64
+ if (!evmWalletClient) {
65
+ return undefined;
66
+ }
67
+ if (!evmPublicClient) {
68
+ return undefined;
69
+ }
70
+
71
+ return new EvmWalletProvider({
72
+ walletClient: evmWalletClient,
73
+ publicClient: evmPublicClient,
74
+ });
75
+ }
76
+
77
+ case 'SUI': {
78
+ const suiXService = xService as SuiXService;
79
+ const { client, wallet, account } = {
80
+ client: suiXService.suiClient,
81
+ wallet: suiXService.suiWallet,
82
+ account: suiXService.suiAccount,
83
+ };
84
+
85
+ return new SuiWalletProvider({ client, wallet, account });
86
+ }
87
+
88
+ case 'ICON': {
89
+ const { walletAddress, rpcUrl } = {
90
+ walletAddress: xAccount.address,
91
+ rpcUrl: CHAIN_INFO[SupportedChainId.MAINNET].APIEndpoint,
92
+ };
93
+
94
+ return new IconWalletProvider({
95
+ walletAddress: walletAddress as `hx${string}` | undefined,
96
+ rpcUrl: rpcUrl as `http${string}`,
97
+ });
98
+ }
99
+
100
+ case 'INJECTIVE': {
101
+ const injectiveXService = xService as InjectiveXService;
102
+ if (!injectiveXService) {
103
+ return undefined;
104
+ // throw new Error('InjectiveXService is not initialized');
105
+ }
106
+
107
+ const { walletAddress, msgBroadcaster } = {
108
+ walletAddress: xAccount.address,
109
+ msgBroadcaster: injectiveXService.msgBroadcaster,
110
+ };
111
+
112
+ return new InjectiveWalletProvider({
113
+ walletAddress: walletAddress as InjectiveEoaAddress | undefined,
114
+ msgBroadcaster: msgBroadcaster,
115
+ });
116
+ }
117
+
118
+ case 'STELLAR': {
119
+ const stellarXService = xService as StellarXService;
120
+
121
+ return new StellarWalletProvider({
122
+ type: 'BROWSER_EXTENSION',
123
+ walletsKit: stellarXService.walletsKit,
124
+ network: 'PUBLIC',
125
+ });
126
+ }
127
+
128
+ case 'SOLANA': {
129
+ const solanaXService = xService as SolanaXService;
130
+
131
+ if (!solanaXService.wallet) {
132
+ throw new Error('Wallet is not initialized');
133
+ }
134
+
135
+ if (!solanaXService.connection) {
136
+ throw new Error('Connection is not initialized');
137
+ }
138
+
139
+ return new SolanaWalletProvider({
140
+ wallet: solanaXService.wallet,
141
+ connection: solanaXService.connection,
142
+ });
143
+ }
144
+
145
+ default:
146
+ return undefined;
147
+ }
148
+ }, [xChainType, evmPublicClient, evmWalletClient, xService, xAccount]);
149
+ }
@@ -0,0 +1,51 @@
1
+ import { getXChainType } from '@/actions';
2
+ import { useMemo } from 'react';
3
+ import { usePublicClient, useWalletClient } from 'wagmi';
4
+ import type { ChainId } from '@sodax/types';
5
+ import { getWagmiChainId } from '../utils';
6
+ import { useXAccount, useXService } from '..';
7
+ import type { SuiXService } from '../xchains/sui/SuiXService';
8
+ import { CHAIN_INFO, SupportedChainId } from '../xchains/icon/IconXService';
9
+ import type { InjectiveXService } from '../xchains/injective/InjectiveXService';
10
+ import { getNetworkEndpoints, Network } from '@injectivelabs/networks';
11
+
12
+ export function useWalletProviderOptions(xChainId: ChainId | undefined) {
13
+ const xChainType = getXChainType(xChainId);
14
+
15
+ const evmPublicClient = usePublicClient({
16
+ chainId: xChainId ? getWagmiChainId(xChainId) : undefined,
17
+ });
18
+ const { data: evmWalletClient } = useWalletClient({
19
+ chainId: xChainId ? getWagmiChainId(xChainId) : undefined,
20
+ });
21
+
22
+ const xService = useXService(getXChainType(xChainId));
23
+ const xAccount = useXAccount(xChainId);
24
+
25
+ return useMemo(() => {
26
+ switch (xChainType) {
27
+ case 'EVM': {
28
+ return { walletClient: evmWalletClient, publicClient: evmPublicClient };
29
+ }
30
+ case 'SUI': {
31
+ const suiXService = xService as SuiXService;
32
+ return { client: suiXService.suiClient, wallet: suiXService.suiWallet, account: suiXService.suiAccount };
33
+ }
34
+ case 'ICON': {
35
+ return { walletAddress: xAccount.address, rpcUrl: CHAIN_INFO[SupportedChainId.MAINNET].APIEndpoint };
36
+ }
37
+ case 'INJECTIVE': {
38
+ const injectiveXService = xService as InjectiveXService;
39
+ const endpoints = getNetworkEndpoints(Network.Mainnet);
40
+
41
+ return {
42
+ walletAddress: xAccount.address,
43
+ msgBroadcaster: injectiveXService.msgBroadcaster,
44
+ rpcUrl: endpoints.rpc,
45
+ };
46
+ }
47
+ default:
48
+ return undefined;
49
+ }
50
+ }, [xChainType, evmPublicClient, evmWalletClient, xService, xAccount]);
51
+ }
@@ -0,0 +1,51 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import type { ChainId, ChainType } from '@sodax/types';
4
+
5
+ import type { XAccount } from '../types';
6
+ import { useXConnection } from './useXConnection';
7
+ import { getXChainType } from '../actions';
8
+
9
+ /**
10
+ * Hook to get the current connected account for a specific blockchain
11
+ *
12
+ * @param chainIdentifier - The blockchain identifier (either chain type like 'EVM' or chain ID like '0xa86a.avax')
13
+ * @returns {XAccount} The current connected account, or undefined if no account is connected
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * // Using ChainType (preferred)
18
+ * const { address } = useXAccount('EVM');
19
+ *
20
+ * // Using ChainId
21
+ * const { address } = useXAccount('0xa86a.avax');
22
+ *
23
+ * // Returns: { address: string | undefined, xChainType: ChainType | undefined }
24
+ * ```
25
+ */
26
+ function isChainType(chainIdentifier: ChainType | ChainId): chainIdentifier is ChainType {
27
+ return ['ICON', 'EVM', 'INJECTIVE', 'SUI', 'STELLAR', 'SOLANA'].includes(chainIdentifier);
28
+ }
29
+
30
+ export function useXAccount(chainIdentifier?: ChainType | ChainId): XAccount {
31
+ const resolvedChainType: ChainType | undefined = chainIdentifier
32
+ ? isChainType(chainIdentifier)
33
+ ? chainIdentifier
34
+ : getXChainType(chainIdentifier as ChainId)
35
+ : undefined;
36
+
37
+ const xConnection = useXConnection(resolvedChainType);
38
+
39
+ const xAccount = useMemo((): XAccount => {
40
+ if (!resolvedChainType) {
41
+ return {
42
+ address: undefined,
43
+ xChainType: undefined,
44
+ };
45
+ }
46
+
47
+ return xConnection?.xAccount || { address: undefined, xChainType: resolvedChainType };
48
+ }, [resolvedChainType, xConnection]);
49
+
50
+ return xAccount;
51
+ }
@@ -0,0 +1,56 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import type { ChainType } from '@sodax/types';
4
+ import { useCurrentAccount } from '@mysten/dapp-kit';
5
+ import { useWallet } from '@solana/wallet-adapter-react';
6
+ import { useAccount } from 'wagmi';
7
+
8
+ import type { XAccount } from '../types';
9
+ import { useXWagmiStore } from '../useXWagmiStore';
10
+
11
+ export function useXAccounts() {
12
+ const xChainTypes = useXWagmiStore(state => Object.keys(state.xServices));
13
+ const xConnections = useXWagmiStore(state => state.xConnections);
14
+ const { address: evmAddress } = useAccount();
15
+ const suiAccount = useCurrentAccount();
16
+ const solanaWallet = useWallet();
17
+
18
+ const xAccounts = useMemo(() => {
19
+ const result: Partial<Record<ChainType, XAccount>> = {};
20
+ for (const xChainType of xChainTypes) {
21
+ const xConnection = xConnections[xChainType];
22
+
23
+ if (xConnection?.xAccount) {
24
+ result[xChainType] = xConnection.xAccount;
25
+ } else {
26
+ result[xChainType] = {
27
+ address: undefined,
28
+ xChainType,
29
+ };
30
+ }
31
+ }
32
+
33
+ if (evmAddress) {
34
+ result['EVM'] = {
35
+ address: evmAddress,
36
+ xChainType: 'EVM',
37
+ };
38
+ }
39
+ if (suiAccount) {
40
+ result['SUI'] = {
41
+ address: suiAccount.address,
42
+ xChainType: 'SUI',
43
+ };
44
+ }
45
+ if (solanaWallet.publicKey) {
46
+ result['SOLANA'] = {
47
+ address: solanaWallet.publicKey.toString(),
48
+ xChainType: 'SOLANA',
49
+ };
50
+ }
51
+
52
+ return result;
53
+ }, [xChainTypes, xConnections, evmAddress, suiAccount, solanaWallet]);
54
+
55
+ return xAccounts;
56
+ }
@@ -0,0 +1,66 @@
1
+ import { getXChainType } from '@/actions';
2
+ import { type UseQueryResult, keepPreviousData, useQuery } from '@tanstack/react-query';
3
+ import type { ChainId, XToken } from '@sodax/types';
4
+ import { useXService } from './useXService';
5
+
6
+ /**
7
+ * Hook to fetch token balances for multiple tokens on a specific chain
8
+ *
9
+ * @param params - Query parameters object
10
+ * @param params.xChainId - Chain identifier (e.g. '0xa86a.avax', '0x1.base', '0x2.bsc', '0x89.polygon', '0x1.optimism')
11
+ * @param params.xTokens - Array of token objects to fetch balances for.
12
+ * @param params.address - Wallet address to fetch balances for. If undefined, returns empty object
13
+ *
14
+ * @returns UseQueryResult containing an object mapping token addresses to their balances as bigints.
15
+ * The balances are denominated in the token's smallest unit (e.g. wei for ETH).
16
+ * Returns empty object if wallet is not connected or service is unavailable.
17
+ */
18
+ /**
19
+ * @example
20
+ * ```tsx
21
+ * // Example usage in a component
22
+ * function TokenBalances({ tokens }: { tokens: XToken[] }) {
23
+ * const { address } = useXAccount('EVM');
24
+ * const { data: balances } = useXBalances({
25
+ * xChainId: '0xa86a.avax',
26
+ * xTokens: tokens,
27
+ * address,
28
+ * });
29
+ *
30
+ * return (
31
+ * <div>
32
+ * {tokens.map(token => (
33
+ * <div key={token.address}>
34
+ * {token.symbol}: {formatUnits(balances?.[token.address] || 0n, token.decimals)}
35
+ * </div>
36
+ * ))}
37
+ * </div>
38
+ * );
39
+ * }
40
+ * ```
41
+ */
42
+
43
+ export function useXBalances({
44
+ xChainId,
45
+ xTokens,
46
+ address,
47
+ }: { xChainId: ChainId; xTokens: XToken[]; address: string | undefined }): UseQueryResult<{
48
+ [key: string]: bigint;
49
+ }> {
50
+ const xService = useXService(getXChainType(xChainId));
51
+ return useQuery({
52
+ queryKey: ['xBalances', xChainId, xTokens.map(x => x.symbol), address],
53
+ queryFn: async () => {
54
+ if (!xService) {
55
+ return {};
56
+ }
57
+
58
+ const balances = await xService.getBalances(address, xTokens, xChainId);
59
+
60
+ return balances;
61
+ },
62
+ enabled: !!xService,
63
+ placeholderData: keepPreviousData,
64
+ refetchInterval: 5_000,
65
+ });
66
+ }
@@ -0,0 +1,119 @@
1
+ import type { XAccount } from '@/types';
2
+ import { useConnectWallet } from '@mysten/dapp-kit';
3
+ import { useWallet } from '@solana/wallet-adapter-react';
4
+ import { useMutation, type UseMutationResult } from '@tanstack/react-query';
5
+ import { useConnect } from 'wagmi';
6
+ import type { XConnector } from '../core/XConnector';
7
+ import { useXWagmiStore } from '../useXWagmiStore';
8
+ import type { EvmXConnector } from '../xchains/evm';
9
+ import type { SolanaXConnector } from '../xchains/solana';
10
+ import type { SuiXConnector } from '../xchains/sui';
11
+
12
+ /**
13
+ * Hook for connecting to various blockchain wallets across different chains
14
+ *
15
+ * Handles connection logic for EVM, SUI, Solana and other supported chains.
16
+ * Sets up wallet connections and stores connection state in XWagmiStore.
17
+ *
18
+ * @param {void} - No parameters required
19
+ * @returns {UseMutationResult<XAccount | undefined, Error, XConnector>} Mutation result containing:
20
+ * - mutateAsync: Function to connect a wallet
21
+ * - isPending: Boolean indicating if connection is in progress
22
+ * - error: Any error that occurred
23
+ * - data: Connected account data if successful
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * const { mutateAsync: connect, isPending } = useXConnect();
28
+ *
29
+ * const handleConnect = async (connector: XConnector) => {
30
+ * try {
31
+ * await connect(connector);
32
+ * } catch (err) {
33
+ * console.error(err);
34
+ * }
35
+ * };
36
+ * ```
37
+ */
38
+ export function useXConnect(): UseMutationResult<XAccount | undefined, Error, XConnector> {
39
+ const setXConnection = useXWagmiStore(state => state.setXConnection);
40
+
41
+ const { connectAsync: evmConnectAsync } = useConnect();
42
+ const { mutateAsync: suiConnectAsync } = useConnectWallet();
43
+
44
+ // const solanaWallet = useWallet();
45
+ const { select, connect, connected } = useWallet();
46
+
47
+ return useMutation({
48
+ mutationFn: async (xConnector: XConnector) => {
49
+ const xChainType = xConnector.xChainType;
50
+ let xAccount: XAccount | undefined;
51
+
52
+ switch (xChainType) {
53
+ case 'EVM':
54
+ await evmConnectAsync({ connector: (xConnector as EvmXConnector).connector });
55
+ break;
56
+ case 'SUI':
57
+ await suiConnectAsync({ wallet: (xConnector as SuiXConnector).wallet });
58
+ break;
59
+ case 'SOLANA': {
60
+ const walletName = (xConnector as SolanaXConnector).wallet.adapter.name;
61
+
62
+ await select(walletName);
63
+
64
+ const adapter = (xConnector as SolanaXConnector).wallet.adapter;
65
+
66
+ if (!adapter) throw new Error('No adapter found for Solana wallet');
67
+
68
+ if (walletName === 'MetaMask') {
69
+ await new Promise<void>((resolve, reject) => {
70
+ const timeout = setTimeout(() => {
71
+ cleanup();
72
+ reject(new Error('Wallet connection timeout'));
73
+ }, 30000);
74
+
75
+ const handleConnect = () => {
76
+ cleanup();
77
+ resolve();
78
+ };
79
+
80
+ const handleError = (error: Error) => {
81
+ cleanup();
82
+ reject(error);
83
+ };
84
+
85
+ const cleanup = () => {
86
+ clearTimeout(timeout);
87
+ adapter.off('connect', handleConnect);
88
+ adapter.off('error', handleError);
89
+ };
90
+
91
+ adapter.on('connect', handleConnect);
92
+ adapter.on('error', handleError);
93
+
94
+ connect().catch(err => {
95
+ cleanup();
96
+ reject(err);
97
+ });
98
+ });
99
+ }
100
+
101
+ break;
102
+ }
103
+
104
+ default:
105
+ xAccount = await xConnector.connect();
106
+ break;
107
+ }
108
+
109
+ if (xAccount) {
110
+ setXConnection(xConnector.xChainType, {
111
+ xAccount,
112
+ xConnectorId: xConnector.id,
113
+ });
114
+ }
115
+
116
+ return xAccount;
117
+ },
118
+ });
119
+ }
@@ -0,0 +1,72 @@
1
+ import type { ChainType } from '@sodax/types';
2
+ import { useCurrentAccount, useCurrentWallet } from '@mysten/dapp-kit';
3
+ import { useWallet } from '@solana/wallet-adapter-react';
4
+ import { useMemo } from 'react';
5
+ import { useAccount, useConnections } from 'wagmi';
6
+ import type { XConnection } from '../types';
7
+ import { useXWagmiStore } from '../useXWagmiStore';
8
+
9
+ /**
10
+ * Hook for accessing connection details for a specific blockchain
11
+ *
12
+ * Retrieves the current connection state for the specified chain type,
13
+ * including the connected account and connector ID.
14
+ *
15
+ * @param {ChainType} xChainType - The type of blockchain to get connection details for
16
+ * @returns {XConnection | undefined} Connection details including account and connector ID, or undefined if not connected
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * const connection = useXConnection('EVM');
21
+ *
22
+ * if (connection) {
23
+ * console.log('Connected account:', connection.xAccount.address);
24
+ * console.log('Using connector:', connection.xConnectorId);
25
+ * }
26
+ * ```
27
+ */
28
+ export function useXConnection(xChainType: ChainType | undefined): XConnection | undefined {
29
+ const xConnection = useXWagmiStore(state => (xChainType ? state.xConnections?.[xChainType] : undefined));
30
+
31
+ const evmConnections = useConnections();
32
+ const { address: evmAddress } = useAccount();
33
+ const suiAccount = useCurrentAccount();
34
+ const suiCurrentWallet = useCurrentWallet();
35
+ const solanaWallet = useWallet();
36
+
37
+ const xConnection2 = useMemo(() => {
38
+ if (!xChainType) {
39
+ return undefined;
40
+ }
41
+
42
+ switch (xChainType) {
43
+ case 'EVM':
44
+ return {
45
+ xAccount: { address: evmAddress as string, xChainType },
46
+ xConnectorId: evmConnections?.[0]?.connector.id,
47
+ };
48
+
49
+ case 'SUI':
50
+ if (suiCurrentWallet.currentWallet && suiCurrentWallet.connectionStatus === 'connected') {
51
+ return {
52
+ xAccount: { address: suiAccount?.address, xChainType },
53
+ xConnectorId: suiCurrentWallet.currentWallet.name,
54
+ };
55
+ }
56
+ return undefined;
57
+
58
+ case 'SOLANA':
59
+ if (solanaWallet.connected) {
60
+ return {
61
+ xAccount: { address: solanaWallet.publicKey?.toString(), xChainType },
62
+ xConnectorId: `${solanaWallet.wallet?.adapter.name}`,
63
+ };
64
+ }
65
+ return undefined;
66
+ default:
67
+ return xConnection;
68
+ }
69
+ }, [xChainType, xConnection, evmAddress, suiAccount, evmConnections, suiCurrentWallet, solanaWallet]);
70
+
71
+ return xConnection2;
72
+ }