@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,56 @@
|
|
|
1
|
+
import type { ChainType } from '@sodax/types';
|
|
2
|
+
import { useWallets } from '@mysten/dapp-kit';
|
|
3
|
+
import { useWallet } from '@solana/wallet-adapter-react';
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { useConnectors } from 'wagmi';
|
|
6
|
+
import type { XConnector } from '../core';
|
|
7
|
+
import { EvmXConnector } from '../xchains/evm';
|
|
8
|
+
import { SolanaXConnector } from '../xchains/solana';
|
|
9
|
+
import { useStellarXConnectors } from '../xchains/stellar/useStellarXConnectors';
|
|
10
|
+
import { SuiXConnector } from '../xchains/sui';
|
|
11
|
+
import { useXService } from './useXService';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Hook to retrieve available wallet connectors for a specific blockchain type.
|
|
15
|
+
*
|
|
16
|
+
* This hook aggregates wallet connectors from different blockchain ecosystems:
|
|
17
|
+
* - EVM: Uses wagmi connectors
|
|
18
|
+
* - Sui: Uses Sui wallet adapters
|
|
19
|
+
* - Stellar: Uses custom Stellar connectors
|
|
20
|
+
* - Solana: Uses Solana wallet adapters (filtered to installed wallets only)
|
|
21
|
+
*
|
|
22
|
+
* @param xChainType - The blockchain type to get connectors for ('EVM' | 'SUI' | 'STELLAR' | 'SOLANA')
|
|
23
|
+
* @returns An array of XConnector instances compatible with the specified chain type
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export function useXConnectors(xChainType: ChainType | undefined): XConnector[] {
|
|
27
|
+
const xService = useXService(xChainType);
|
|
28
|
+
const evmConnectors = useConnectors();
|
|
29
|
+
const suiWallets = useWallets();
|
|
30
|
+
const { data: stellarXConnectors } = useStellarXConnectors();
|
|
31
|
+
|
|
32
|
+
const { wallets: solanaWallets } = useWallet();
|
|
33
|
+
|
|
34
|
+
const xConnectors = useMemo((): XConnector[] => {
|
|
35
|
+
if (!xChainType || !xService) {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
switch (xChainType) {
|
|
40
|
+
case 'EVM':
|
|
41
|
+
return evmConnectors.map(connector => new EvmXConnector(connector));
|
|
42
|
+
case 'SUI':
|
|
43
|
+
return suiWallets.map(wallet => new SuiXConnector(wallet));
|
|
44
|
+
case 'STELLAR':
|
|
45
|
+
return stellarXConnectors || [];
|
|
46
|
+
case 'SOLANA':
|
|
47
|
+
return solanaWallets
|
|
48
|
+
.filter(wallet => wallet.readyState === 'Installed')
|
|
49
|
+
.map(wallet => new SolanaXConnector(wallet));
|
|
50
|
+
default:
|
|
51
|
+
return xService.getXConnectors();
|
|
52
|
+
}
|
|
53
|
+
}, [xService, xChainType, evmConnectors, suiWallets, stellarXConnectors, solanaWallets]);
|
|
54
|
+
|
|
55
|
+
return xConnectors;
|
|
56
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { ChainType } from '@sodax/types';
|
|
2
|
+
import { useDisconnectWallet } from '@mysten/dapp-kit';
|
|
3
|
+
import { useWallet } from '@solana/wallet-adapter-react';
|
|
4
|
+
import { useCallback } from 'react';
|
|
5
|
+
import { useDisconnect } from 'wagmi';
|
|
6
|
+
import { getXService } from '../actions';
|
|
7
|
+
import { useXWagmiStore } from '../useXWagmiStore';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook for disconnecting from a specific blockchain wallet
|
|
11
|
+
*
|
|
12
|
+
* Handles disconnection logic for EVM, SUI, Solana and other supported chains.
|
|
13
|
+
* Clears connection state from XWagmiStore.
|
|
14
|
+
*
|
|
15
|
+
* @param {void} - No parameters required
|
|
16
|
+
* @returns {(xChainType: ChainType) => Promise<void>} Async function that disconnects from the specified chain
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const disconnect = useXDisconnect();
|
|
21
|
+
*
|
|
22
|
+
* const handleDisconnect = async (xChainType: ChainType) => {
|
|
23
|
+
* await disconnect(xChainType);
|
|
24
|
+
* };
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function useXDisconnect(): (xChainType: ChainType) => Promise<void> {
|
|
28
|
+
// Get connection state and disconnect handler from store
|
|
29
|
+
const xConnections = useXWagmiStore(state => state.xConnections);
|
|
30
|
+
const unsetXConnection = useXWagmiStore(state => state.unsetXConnection);
|
|
31
|
+
|
|
32
|
+
// Get chain-specific disconnect handlers
|
|
33
|
+
const { disconnectAsync } = useDisconnect();
|
|
34
|
+
const { mutateAsync: suiDisconnectAsync } = useDisconnectWallet();
|
|
35
|
+
const solanaWallet = useWallet();
|
|
36
|
+
|
|
37
|
+
return useCallback(
|
|
38
|
+
async (xChainType: ChainType) => {
|
|
39
|
+
// Handle disconnection based on chain type
|
|
40
|
+
switch (xChainType) {
|
|
41
|
+
case 'EVM':
|
|
42
|
+
await disconnectAsync();
|
|
43
|
+
break;
|
|
44
|
+
case 'SUI':
|
|
45
|
+
await suiDisconnectAsync();
|
|
46
|
+
break;
|
|
47
|
+
case 'SOLANA':
|
|
48
|
+
await solanaWallet.disconnect();
|
|
49
|
+
break;
|
|
50
|
+
default: {
|
|
51
|
+
// Handle other chain types
|
|
52
|
+
const xService = getXService(xChainType);
|
|
53
|
+
const xConnectorId = xConnections[xChainType]?.xConnectorId;
|
|
54
|
+
const xConnector = xConnectorId ? xService.getXConnectorById(xConnectorId) : undefined;
|
|
55
|
+
await xConnector?.disconnect();
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Clear connection state from store
|
|
61
|
+
unsetXConnection(xChainType);
|
|
62
|
+
},
|
|
63
|
+
[xConnections, unsetXConnection, disconnectAsync, suiDisconnectAsync, solanaWallet],
|
|
64
|
+
);
|
|
65
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChainType } from '@sodax/types';
|
|
2
|
+
import type { XService } from '../core';
|
|
3
|
+
import { useXWagmiStore } from '../useXWagmiStore';
|
|
4
|
+
|
|
5
|
+
export function useXService(xChainType: ChainType | undefined): XService | undefined {
|
|
6
|
+
const xService = useXWagmiStore(state => (xChainType ? state.xServices[xChainType] : undefined));
|
|
7
|
+
return xService;
|
|
8
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './actions';
|
|
2
|
+
export * from './constants';
|
|
3
|
+
export * from './core';
|
|
4
|
+
|
|
5
|
+
export * from './utils';
|
|
6
|
+
|
|
7
|
+
export * from './xchains/evm';
|
|
8
|
+
export * from './xchains/icon';
|
|
9
|
+
export * from './xchains/injective';
|
|
10
|
+
export * from './xchains/solana';
|
|
11
|
+
export * from './xchains/stellar';
|
|
12
|
+
export * from './xchains/sui';
|
|
13
|
+
export * from './hooks';
|
|
14
|
+
export * from './useXWagmiStore';
|
|
15
|
+
export * from './SodaxWalletProvider';
|
|
16
|
+
|
|
17
|
+
export * from './types';
|
|
18
|
+
export type * from './types';
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ChainType } from '@sodax/types';
|
|
2
|
+
import type { EvmChainId } from '@/xchains/evm/EvmXService';
|
|
3
|
+
|
|
4
|
+
export type XAccount = {
|
|
5
|
+
address: string | undefined;
|
|
6
|
+
xChainType: ChainType | undefined;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export type XConnection = {
|
|
10
|
+
xAccount: XAccount;
|
|
11
|
+
xConnectorId: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CurrencyKey = string;
|
|
15
|
+
|
|
16
|
+
export enum WalletId {
|
|
17
|
+
METAMASK = 'metamask',
|
|
18
|
+
HANA = 'hana',
|
|
19
|
+
PHANTOM = 'phantom',
|
|
20
|
+
SUI = 'sui',
|
|
21
|
+
KEPLR = 'keplr',
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type EVMConfig = {
|
|
25
|
+
chains: EvmChainId[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type SuiConfig = {
|
|
29
|
+
isMainnet: boolean;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type SolanaConfig = {
|
|
33
|
+
endpoint: string;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type XConfig = {
|
|
37
|
+
[key in ChainType]: key extends 'EVM'
|
|
38
|
+
? EVMConfig
|
|
39
|
+
: key extends 'SUI'
|
|
40
|
+
? SuiConfig
|
|
41
|
+
: key extends 'SOLANA'
|
|
42
|
+
? SolanaConfig
|
|
43
|
+
: unknown;
|
|
44
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import type { ChainType } from '@sodax/types';
|
|
4
|
+
import type { XConfig } from './types';
|
|
5
|
+
import { useCurrentAccount, useCurrentWallet, useSuiClient } from '@mysten/dapp-kit';
|
|
6
|
+
import React, { useEffect } from 'react';
|
|
7
|
+
import { create } from 'zustand';
|
|
8
|
+
import { createJSONStorage, persist } from 'zustand/middleware';
|
|
9
|
+
import { immer } from 'zustand/middleware/immer';
|
|
10
|
+
import { getXService } from '.';
|
|
11
|
+
import type { XService } from './core';
|
|
12
|
+
import type { XConnection } from './types';
|
|
13
|
+
import { EvmXService } from './xchains/evm';
|
|
14
|
+
import { InjectiveMetamaskXConnector, InjectiveXService } from './xchains/injective';
|
|
15
|
+
import { SolanaXService } from './xchains/solana/SolanaXService';
|
|
16
|
+
import { StellarXService } from './xchains/stellar';
|
|
17
|
+
import { SuiXService } from './xchains/sui';
|
|
18
|
+
import { IconXService } from './xchains/icon';
|
|
19
|
+
import { IconHanaXConnector } from './xchains/icon/IconHanaXConnector';
|
|
20
|
+
import { useAnchorProvider } from './xchains/solana/hooks/useAnchorProvider';
|
|
21
|
+
import { useConnection, useWallet } from '@solana/wallet-adapter-react';
|
|
22
|
+
|
|
23
|
+
type XWagmiStore = {
|
|
24
|
+
xServices: Partial<Record<ChainType, XService>>;
|
|
25
|
+
xConnections: Partial<Record<ChainType, XConnection>>;
|
|
26
|
+
|
|
27
|
+
setXConnection: (xChainType: ChainType, xConnection: XConnection) => void;
|
|
28
|
+
unsetXConnection: (xChainType: ChainType) => void;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export const useXWagmiStore = create<XWagmiStore>()(
|
|
32
|
+
persist(
|
|
33
|
+
immer((set, get) => ({
|
|
34
|
+
xServices: {},
|
|
35
|
+
xConnections: {},
|
|
36
|
+
setXConnection: (xChainType: ChainType, xConnection: XConnection) => {
|
|
37
|
+
set(state => {
|
|
38
|
+
state.xConnections[xChainType] = xConnection;
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
unsetXConnection: (xChainType: ChainType) => {
|
|
42
|
+
set(state => {
|
|
43
|
+
delete state.xConnections[xChainType];
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
})),
|
|
47
|
+
{
|
|
48
|
+
name: 'xwagmi-store',
|
|
49
|
+
storage: createJSONStorage(() => localStorage),
|
|
50
|
+
partialize: state => ({ xConnections: state.xConnections }),
|
|
51
|
+
|
|
52
|
+
// TODO: better way to handle rehydration of xConnections?
|
|
53
|
+
onRehydrateStorage: state => {
|
|
54
|
+
console.log('hydration starts');
|
|
55
|
+
|
|
56
|
+
return (state, error) => {
|
|
57
|
+
if (state?.xConnections) {
|
|
58
|
+
console.log('rehydrating xConnections', state.xConnections);
|
|
59
|
+
Object.entries(state.xConnections).forEach(([xChainType, xConnection]) => {
|
|
60
|
+
const xConnector = getXService(xChainType as ChainType).getXConnectorById(xConnection.xConnectorId);
|
|
61
|
+
xConnector?.connect();
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (error) {
|
|
65
|
+
console.log('an error happened during hydration', error);
|
|
66
|
+
} else {
|
|
67
|
+
console.log('hydration finished');
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const initXServices = (config: XConfig) => {
|
|
76
|
+
const xServices = {};
|
|
77
|
+
Object.keys(config).forEach(key => {
|
|
78
|
+
const xChainType = key as ChainType;
|
|
79
|
+
|
|
80
|
+
switch (xChainType) {
|
|
81
|
+
case 'EVM':
|
|
82
|
+
if (config[xChainType]) {
|
|
83
|
+
xServices[xChainType] = EvmXService.getInstance();
|
|
84
|
+
xServices[xChainType].setXConnectors([]);
|
|
85
|
+
xServices[xChainType].setConfig(config[xChainType]);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
case 'INJECTIVE':
|
|
89
|
+
xServices[xChainType] = InjectiveXService.getInstance();
|
|
90
|
+
xServices[xChainType].setXConnectors([new InjectiveMetamaskXConnector()]);
|
|
91
|
+
break;
|
|
92
|
+
case 'STELLAR':
|
|
93
|
+
xServices[xChainType] = StellarXService.getInstance();
|
|
94
|
+
xServices[xChainType].setXConnectors([]);
|
|
95
|
+
break;
|
|
96
|
+
case 'SUI':
|
|
97
|
+
xServices[xChainType] = SuiXService.getInstance();
|
|
98
|
+
xServices[xChainType].setXConnectors([]);
|
|
99
|
+
break;
|
|
100
|
+
case 'SOLANA':
|
|
101
|
+
xServices[xChainType] = SolanaXService.getInstance();
|
|
102
|
+
xServices[xChainType].setXConnectors([]);
|
|
103
|
+
break;
|
|
104
|
+
case 'ICON':
|
|
105
|
+
xServices[xChainType] = IconXService.getInstance();
|
|
106
|
+
xServices[xChainType].setXConnectors([new IconHanaXConnector()]);
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
return xServices;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
export const initXWagmiStore = (config: XConfig) => {
|
|
117
|
+
useXWagmiStore.setState({
|
|
118
|
+
xServices: initXServices(config),
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
export const InitXWagmiStore = () => {
|
|
123
|
+
// sui
|
|
124
|
+
const suiClient = useSuiClient();
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
if (suiClient) {
|
|
127
|
+
SuiXService.getInstance().suiClient = suiClient;
|
|
128
|
+
}
|
|
129
|
+
}, [suiClient]);
|
|
130
|
+
const { currentWallet: suiWallet } = useCurrentWallet();
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
if (suiWallet) {
|
|
133
|
+
SuiXService.getInstance().suiWallet = suiWallet;
|
|
134
|
+
}
|
|
135
|
+
}, [suiWallet]);
|
|
136
|
+
const suiAccount = useCurrentAccount();
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
if (suiAccount) {
|
|
139
|
+
SuiXService.getInstance().suiAccount = suiAccount;
|
|
140
|
+
}
|
|
141
|
+
}, [suiAccount]);
|
|
142
|
+
|
|
143
|
+
// solana
|
|
144
|
+
const { connection: solanaConnection } = useConnection();
|
|
145
|
+
const solanaWallet = useWallet();
|
|
146
|
+
const solanaProvider = useAnchorProvider();
|
|
147
|
+
useEffect(() => {
|
|
148
|
+
if (solanaConnection) {
|
|
149
|
+
SolanaXService.getInstance().connection = solanaConnection;
|
|
150
|
+
}
|
|
151
|
+
}, [solanaConnection]);
|
|
152
|
+
useEffect(() => {
|
|
153
|
+
if (solanaWallet) {
|
|
154
|
+
SolanaXService.getInstance().wallet = solanaWallet;
|
|
155
|
+
}
|
|
156
|
+
}, [solanaWallet]);
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
if (solanaProvider) {
|
|
159
|
+
SolanaXService.getInstance().provider = solanaProvider;
|
|
160
|
+
}
|
|
161
|
+
}, [solanaProvider]);
|
|
162
|
+
|
|
163
|
+
return <></>;
|
|
164
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { ChainId, XToken } from '@sodax/types';
|
|
2
|
+
|
|
3
|
+
export const isNativeToken = (xToken: XToken) => {
|
|
4
|
+
const nativeAddresses = [
|
|
5
|
+
'cx0000000000000000000000000000000000000000',
|
|
6
|
+
'0x0000000000000000000000000000000000000000',
|
|
7
|
+
'inj',
|
|
8
|
+
'0x0000000000000000000000000000000000000000000000000000000000000002::sui::SUI',
|
|
9
|
+
'hx0000000000000000000000000000000000000000',
|
|
10
|
+
'11111111111111111111111111111111', // solana
|
|
11
|
+
'CAS3J7GYLGXMF6TDJBBYYSE3HQ6BBSMLNUQ34T6TZMYMW2EVH34XOWMA', // stellar,
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
return nativeAddresses.includes(xToken.address);
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// TODO: remove this? move to dapp-kit?
|
|
18
|
+
export const getWagmiChainId = (xChainId: ChainId): number => {
|
|
19
|
+
const xChainMap = {
|
|
20
|
+
'0xa869.fuji': 43113,
|
|
21
|
+
'sonic-blaze': 57054,
|
|
22
|
+
sonic: 146,
|
|
23
|
+
'0xa86a.avax': 43114,
|
|
24
|
+
'0x38.bsc': 56,
|
|
25
|
+
'0xa4b1.arbitrum': 42161,
|
|
26
|
+
'0x2105.base': 8453,
|
|
27
|
+
'0xa.optimism': 10,
|
|
28
|
+
'0x89.polygon': 137,
|
|
29
|
+
hyper: 999,
|
|
30
|
+
lightlink: 1890,
|
|
31
|
+
};
|
|
32
|
+
return xChainMap[xChainId] ?? 0;
|
|
33
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { XConnector } from '@/core/XConnector';
|
|
2
|
+
import type { XAccount } from '@/types';
|
|
3
|
+
import type { Config, Connector } from 'wagmi';
|
|
4
|
+
|
|
5
|
+
export class EvmXConnector extends XConnector {
|
|
6
|
+
connector: Connector;
|
|
7
|
+
|
|
8
|
+
constructor(connector: Connector) {
|
|
9
|
+
super('EVM', connector.name, connector.id);
|
|
10
|
+
this.connector = connector;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async connect(): Promise<XAccount | undefined> {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async disconnect(): Promise<void> {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public get id() {
|
|
22
|
+
return this.connector.id;
|
|
23
|
+
}
|
|
24
|
+
public get icon() {
|
|
25
|
+
return this.connector.icon;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { XService } from '@/core/XService';
|
|
2
|
+
import type { ChainId, XToken } from '@sodax/types';
|
|
3
|
+
import type { EVMConfig } from '@/types';
|
|
4
|
+
import { getWagmiChainId, isNativeToken } from '@/utils';
|
|
5
|
+
|
|
6
|
+
import { type Address, type PublicClient, type WalletClient, defineChain, erc20Abi } from 'viem';
|
|
7
|
+
import { getPublicClient, getWalletClient } from 'wagmi/actions';
|
|
8
|
+
import { createConfig, http, type Transport } from 'wagmi';
|
|
9
|
+
import {
|
|
10
|
+
mainnet,
|
|
11
|
+
avalanche,
|
|
12
|
+
base,
|
|
13
|
+
optimism,
|
|
14
|
+
polygon,
|
|
15
|
+
arbitrum,
|
|
16
|
+
bsc,
|
|
17
|
+
sonic,
|
|
18
|
+
nibiru,
|
|
19
|
+
lightlinkPhoenix,
|
|
20
|
+
} from 'wagmi/chains';
|
|
21
|
+
|
|
22
|
+
import {
|
|
23
|
+
AVALANCHE_MAINNET_CHAIN_ID,
|
|
24
|
+
ARBITRUM_MAINNET_CHAIN_ID,
|
|
25
|
+
BASE_MAINNET_CHAIN_ID,
|
|
26
|
+
BSC_MAINNET_CHAIN_ID,
|
|
27
|
+
SONIC_MAINNET_CHAIN_ID,
|
|
28
|
+
OPTIMISM_MAINNET_CHAIN_ID,
|
|
29
|
+
POLYGON_MAINNET_CHAIN_ID,
|
|
30
|
+
NIBIRU_MAINNET_CHAIN_ID,
|
|
31
|
+
HYPEREVM_MAINNET_CHAIN_ID,
|
|
32
|
+
LIGHTLINK_MAINNET_CHAIN_ID,
|
|
33
|
+
} from '@sodax/types';
|
|
34
|
+
|
|
35
|
+
// HyperEVM chain is not supported by viem, so we need to define it manually
|
|
36
|
+
export const hyper = /*#__PURE__*/ defineChain({
|
|
37
|
+
id: 999,
|
|
38
|
+
name: 'HyperEVM',
|
|
39
|
+
nativeCurrency: {
|
|
40
|
+
decimals: 18,
|
|
41
|
+
name: 'HYPE',
|
|
42
|
+
symbol: 'HYPE',
|
|
43
|
+
},
|
|
44
|
+
rpcUrls: {
|
|
45
|
+
default: { http: ['https://rpc.hyperliquid.xyz/evm'] },
|
|
46
|
+
},
|
|
47
|
+
blockExplorers: {
|
|
48
|
+
default: {
|
|
49
|
+
name: 'HyperEVMScan',
|
|
50
|
+
url: 'https://hyperevmscan.io/',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
contracts: {
|
|
54
|
+
multicall3: {
|
|
55
|
+
address: '0xcA11bde05977b3631167028862bE2a173976CA11',
|
|
56
|
+
blockCreated: 13051,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const evmChainMap = {
|
|
62
|
+
[AVALANCHE_MAINNET_CHAIN_ID]: avalanche,
|
|
63
|
+
[ARBITRUM_MAINNET_CHAIN_ID]: arbitrum,
|
|
64
|
+
[BASE_MAINNET_CHAIN_ID]: base,
|
|
65
|
+
[BSC_MAINNET_CHAIN_ID]: bsc,
|
|
66
|
+
[SONIC_MAINNET_CHAIN_ID]: sonic,
|
|
67
|
+
[OPTIMISM_MAINNET_CHAIN_ID]: optimism,
|
|
68
|
+
[POLYGON_MAINNET_CHAIN_ID]: polygon,
|
|
69
|
+
[NIBIRU_MAINNET_CHAIN_ID]: nibiru,
|
|
70
|
+
[HYPEREVM_MAINNET_CHAIN_ID]: hyper,
|
|
71
|
+
[LIGHTLINK_MAINNET_CHAIN_ID]: lightlinkPhoenix,
|
|
72
|
+
} as const;
|
|
73
|
+
|
|
74
|
+
export type EvmChainId = keyof typeof evmChainMap;
|
|
75
|
+
|
|
76
|
+
export const getWagmiConfig = (chains: EvmChainId[]) => {
|
|
77
|
+
const mappedChains = chains.map(chain => evmChainMap[chain]);
|
|
78
|
+
const finalChains = mappedChains.length > 0 ? mappedChains : [mainnet];
|
|
79
|
+
|
|
80
|
+
const transports = finalChains.reduce(
|
|
81
|
+
(acc, chain) => {
|
|
82
|
+
acc[chain.id] = http();
|
|
83
|
+
return acc;
|
|
84
|
+
},
|
|
85
|
+
{} as Record<number, Transport>,
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return createConfig({
|
|
89
|
+
chains: finalChains as [typeof mainnet, ...(typeof mainnet)[]],
|
|
90
|
+
transports,
|
|
91
|
+
// ssr: true,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Service class for handling EVM chain interactions.
|
|
97
|
+
* Implements singleton pattern and provides methods for wallet/chain operations.
|
|
98
|
+
*/
|
|
99
|
+
|
|
100
|
+
export class EvmXService extends XService {
|
|
101
|
+
private static instance: EvmXService;
|
|
102
|
+
private config: EVMConfig | undefined;
|
|
103
|
+
private constructor() {
|
|
104
|
+
super('EVM');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
getXConnectors() {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
public static getInstance(): EvmXService {
|
|
112
|
+
if (!EvmXService.instance) {
|
|
113
|
+
EvmXService.instance = new EvmXService();
|
|
114
|
+
}
|
|
115
|
+
return EvmXService.instance;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
public setConfig(config: EVMConfig) {
|
|
119
|
+
this.config = config;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
getPublicClient(chainId: number): PublicClient | undefined {
|
|
123
|
+
if (!this.config) {
|
|
124
|
+
throw new Error('EvmXService: config is not initialized yet');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// @ts-ignore
|
|
128
|
+
return getPublicClient(getWagmiConfig(this.config.chains), { chainId });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
public async getWalletClient(chainId: number): Promise<WalletClient> {
|
|
132
|
+
if (!this.config) {
|
|
133
|
+
throw new Error('EvmXService: config is not initialized yet');
|
|
134
|
+
}
|
|
135
|
+
return await getWalletClient(getWagmiConfig(this.config.chains), { chainId });
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getBalance(address: string | undefined, xToken: XToken, xChainId: ChainId): Promise<bigint> {
|
|
139
|
+
if (!address) return 0n;
|
|
140
|
+
|
|
141
|
+
const chainId = getWagmiChainId(xChainId);
|
|
142
|
+
|
|
143
|
+
if (isNativeToken(xToken)) {
|
|
144
|
+
const balance = await this.getPublicClient(chainId)?.getBalance({ address: address as Address });
|
|
145
|
+
return balance || 0n;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
throw new Error(`Unsupported token: ${xToken.symbol}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async getBalances(address: string | undefined, xTokens: XToken[], xChainId: ChainId) {
|
|
152
|
+
if (!address) return {};
|
|
153
|
+
|
|
154
|
+
const balancePromises = xTokens
|
|
155
|
+
.filter(xToken => isNativeToken(xToken))
|
|
156
|
+
.map(async xToken => {
|
|
157
|
+
const balance = await this.getBalance(address, xToken, xChainId);
|
|
158
|
+
return { symbol: xToken.symbol, address: xToken.address, balance };
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const balances = await Promise.all(balancePromises);
|
|
162
|
+
const tokenMap = balances.reduce((map, { address, balance }) => {
|
|
163
|
+
if (balance) map[address] = balance;
|
|
164
|
+
return map;
|
|
165
|
+
}, {});
|
|
166
|
+
|
|
167
|
+
const nonNativeXTokens = xTokens.filter(xToken => !isNativeToken(xToken));
|
|
168
|
+
const result = await this.getPublicClient(getWagmiChainId(xChainId))?.multicall({
|
|
169
|
+
contracts: nonNativeXTokens.map(token => ({
|
|
170
|
+
abi: erc20Abi,
|
|
171
|
+
address: token.address as `0x${string}`,
|
|
172
|
+
functionName: 'balanceOf',
|
|
173
|
+
args: [address],
|
|
174
|
+
chainId: getWagmiChainId(xChainId),
|
|
175
|
+
})),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
return nonNativeXTokens
|
|
179
|
+
.map((token, index) => ({
|
|
180
|
+
symbol: token.symbol,
|
|
181
|
+
address: token.address,
|
|
182
|
+
balance: result?.[index]?.result?.toString() || '0',
|
|
183
|
+
}))
|
|
184
|
+
.reduce((acc, balance) => {
|
|
185
|
+
acc[balance.address] = balance.balance;
|
|
186
|
+
return acc;
|
|
187
|
+
}, tokenMap);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { XAccount } from '@/types';
|
|
2
|
+
import { ICONexRequestEventType, ICONexResponseEventType, request } from './iconex';
|
|
3
|
+
|
|
4
|
+
import { XConnector } from '@/core/XConnector';
|
|
5
|
+
|
|
6
|
+
export class IconHanaXConnector extends XConnector {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('ICON', 'Hana Wallet', 'hana');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async connect(): Promise<XAccount | undefined> {
|
|
12
|
+
const { hanaWallet } = window as any;
|
|
13
|
+
if (window && !hanaWallet && !hanaWallet?.isAvailable) {
|
|
14
|
+
window.open('https://chromewebstore.google.com/detail/hana-wallet/jfdlamikmbghhapbgfoogdffldioobgl', '_blank');
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const detail = await request({
|
|
19
|
+
type: ICONexRequestEventType.REQUEST_ADDRESS,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (detail?.type === ICONexResponseEventType.RESPONSE_ADDRESS) {
|
|
23
|
+
return {
|
|
24
|
+
address: detail?.payload,
|
|
25
|
+
xChainType: this.xChainType,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async disconnect(): Promise<void> {
|
|
33
|
+
console.log('HanaIconXConnector disconnected');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
public get icon() {
|
|
37
|
+
return 'https://raw.githubusercontent.com/balancednetwork/icons/master/wallets/hana.svg';
|
|
38
|
+
}
|
|
39
|
+
}
|