@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.
- package/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/index.cjs +1589 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +15970 -0
- package/dist/index.d.ts +15970 -0
- package/dist/index.mjs +1513 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +74 -0
- package/src/SodaxWalletProvider.tsx +55 -0
- package/src/actions/getXChainType.ts +10 -0
- package/src/actions/getXService.ts +25 -0
- package/src/actions/index.ts +2 -0
- package/src/assets/wallets/hana.svg +6 -0
- package/src/assets/wallets/havah.svg +76 -0
- package/src/assets/wallets/keplr.svg +30 -0
- package/src/assets/wallets/metamask.svg +60 -0
- package/src/assets/wallets/phantom.svg +4 -0
- package/src/assets/wallets/sui.svg +20 -0
- package/src/constants/index.ts +1 -0
- package/src/constants/xChains.ts +164 -0
- package/src/core/XConnector.ts +54 -0
- package/src/core/XService.ts +91 -0
- package/src/core/index.ts +2 -0
- package/src/hooks/index.ts +11 -0
- package/src/hooks/useEthereumChainId.ts +36 -0
- package/src/hooks/useEvmSwitchChain.ts +92 -0
- package/src/hooks/useWalletProvider.ts +149 -0
- package/src/hooks/useWalletProviderOptions.ts +51 -0
- package/src/hooks/useXAccount.ts +51 -0
- package/src/hooks/useXAccounts.ts +56 -0
- package/src/hooks/useXBalances.ts +66 -0
- package/src/hooks/useXConnect.ts +119 -0
- package/src/hooks/useXConnection.ts +72 -0
- package/src/hooks/useXConnectors.ts +56 -0
- package/src/hooks/useXDisconnect.ts +65 -0
- package/src/hooks/useXService.ts +8 -0
- package/src/index.ts +18 -0
- package/src/types/index.ts +44 -0
- package/src/useXWagmiStore.tsx +164 -0
- package/src/utils/index.ts +33 -0
- package/src/xchains/evm/EvmXConnector.ts +27 -0
- package/src/xchains/evm/EvmXService.ts +189 -0
- package/src/xchains/evm/index.ts +2 -0
- package/src/xchains/icon/IconHanaXConnector.ts +39 -0
- package/src/xchains/icon/IconXService.ts +115 -0
- package/src/xchains/icon/iconex/index.tsx +46 -0
- package/src/xchains/icon/index.ts +2 -0
- package/src/xchains/injective/InjectiveKelprXConnector.ts +37 -0
- package/src/xchains/injective/InjectiveMetamaskXConnector.ts +40 -0
- package/src/xchains/injective/InjectiveXService.ts +71 -0
- package/src/xchains/injective/index.ts +4 -0
- package/src/xchains/injective/utils.ts +17 -0
- package/src/xchains/solana/SolanaXConnector.ts +26 -0
- package/src/xchains/solana/SolanaXService.ts +50 -0
- package/src/xchains/solana/hooks/useAnchorProvider.tsx +9 -0
- package/src/xchains/solana/index.ts +2 -0
- package/src/xchains/stellar/CustomSorobanServer.ts +93 -0
- package/src/xchains/stellar/StellarWalletsKitXConnector.ts +45 -0
- package/src/xchains/stellar/StellarXService.ts +61 -0
- package/src/xchains/stellar/index.tsx +2 -0
- package/src/xchains/stellar/useStellarXConnectors.ts +30 -0
- package/src/xchains/stellar/utils.ts +49 -0
- package/src/xchains/sui/SuiXConnector.ts +28 -0
- package/src/xchains/sui/SuiXService.ts +56 -0
- 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
|
+
}
|