mirror of
https://github.com/lux-tel/websites.git
synced 2026-10-30 20:31:36 +00:00
246 lines
6.7 KiB
TypeScript
246 lines
6.7 KiB
TypeScript
import { addresses, SENT_DECIMALS, SENT_SYMBOL } from '@session/contracts';
|
|
import { CHAIN, chains } from '@session/contracts/chains';
|
|
import { useSENTBalanceQuery } from '@session/contracts/hooks/SENT';
|
|
import { useEns } from '@session/contracts/hooks/ens';
|
|
import { useWeb3Modal } from '@web3modal/wagmi/react';
|
|
import { useMemo, useState } from 'react';
|
|
import { type Address, createWalletClient, custom, type SwitchChainErrorType } from 'viem';
|
|
import { useAccount, useBalance, useConfig, useDisconnect } from 'wagmi';
|
|
import { switchChain as switchChainWagmi } from 'wagmi/actions';
|
|
import { getEthereumWindowProvider } from '../lib/eth';
|
|
import { useArbName } from './arb';
|
|
|
|
export const useToggleWalletModal = (): ReturnType<typeof useWeb3Modal> => {
|
|
return useWeb3Modal();
|
|
};
|
|
|
|
/**
|
|
* The status of a wallet.
|
|
*/
|
|
export enum WALLET_STATUS {
|
|
/** The wallet is connecting. */
|
|
CONNECTING = 'connecting',
|
|
/** The wallet is reconnecting. */
|
|
RECONNECTING = 'reconnecting',
|
|
/** The wallet is connected. */
|
|
CONNECTED = 'connected',
|
|
/** The wallet is disconnected. */
|
|
DISCONNECTED = 'disconnected',
|
|
}
|
|
|
|
/**
|
|
* Parses the wallet connection status based on the provided flags.
|
|
*
|
|
* @returns The wallet connection status.
|
|
*/
|
|
const parseWalletStatus = ({
|
|
isConnecting,
|
|
isDisconnected,
|
|
isConnected,
|
|
isReconnecting,
|
|
}: {
|
|
isConnecting: boolean;
|
|
isDisconnected: boolean;
|
|
isConnected: boolean;
|
|
isReconnecting: boolean;
|
|
}): WALLET_STATUS => {
|
|
if (isConnecting) return WALLET_STATUS.CONNECTING;
|
|
if (isReconnecting) return WALLET_STATUS.RECONNECTING;
|
|
if (isConnected) return WALLET_STATUS.CONNECTED;
|
|
if (isDisconnected) return WALLET_STATUS.DISCONNECTED;
|
|
return WALLET_STATUS.DISCONNECTED;
|
|
};
|
|
|
|
type UseWalletType = {
|
|
/** The address of the wallet. */
|
|
address?: Address;
|
|
/** The ENS name of the wallet. */
|
|
ensName?: string | null;
|
|
/** The ENS avatar of the wallet. */
|
|
ensAvatar?: string | null;
|
|
/** The .arb name of the wallet. */
|
|
arbName?: string | null;
|
|
/** The token balance of the wallet. */
|
|
tokenBalance?: bigint | null;
|
|
/** The eth balance of the wallet. */
|
|
ethBalance?: bigint | null;
|
|
/** The status of the wallet. */
|
|
status: WALLET_STATUS;
|
|
/** Whether the wallet is connected. */
|
|
isConnected: boolean;
|
|
/** Disconnect the wallet. */
|
|
disconnect: () => void;
|
|
};
|
|
|
|
export function useWallet(): UseWalletType {
|
|
const { address, isConnected, isConnecting, isDisconnected, isReconnecting } = useAccount();
|
|
const { disconnect } = useDisconnect();
|
|
const { balance: tokenBalanceData } = useSENTBalanceQuery({
|
|
address,
|
|
});
|
|
|
|
const { data: ethBalanceData } = useBalance({
|
|
address,
|
|
query: { enabled: isConnected },
|
|
});
|
|
|
|
const { ensName, ensAvatar } = useEns({
|
|
address,
|
|
enabled: isConnected,
|
|
});
|
|
|
|
const { arbName } = useArbName({ address });
|
|
|
|
const tokenBalance = useMemo(
|
|
() => (tokenBalanceData ? tokenBalanceData : null),
|
|
[tokenBalanceData]
|
|
);
|
|
|
|
const ethBalance = useMemo(
|
|
() => (ethBalanceData ? ethBalanceData?.value : null),
|
|
[ethBalanceData]
|
|
);
|
|
|
|
const status = useMemo(
|
|
() => parseWalletStatus({ isConnected, isConnecting, isDisconnected, isReconnecting }),
|
|
[isConnected, isConnecting, isDisconnected, isReconnecting]
|
|
);
|
|
|
|
return {
|
|
address,
|
|
ensName,
|
|
ensAvatar,
|
|
arbName,
|
|
status,
|
|
isConnected,
|
|
tokenBalance,
|
|
ethBalance,
|
|
disconnect,
|
|
};
|
|
}
|
|
|
|
export function useWalletChain() {
|
|
const { chainId } = useAccount();
|
|
const config = useConfig();
|
|
|
|
const chain = useMemo(() => {
|
|
if (!chainId) return null;
|
|
|
|
// Finds the key (CHAIN) where value.id is equal to chainId
|
|
const validChain = Object.keys(chains).find((key) => chains[key as CHAIN].id === chainId) as
|
|
| CHAIN
|
|
| undefined;
|
|
if (!validChain) return null;
|
|
|
|
return validChain;
|
|
}, [chainId]);
|
|
|
|
const switchChain = async (
|
|
targetChain: CHAIN,
|
|
handleError?: (error: SwitchChainErrorType) => void
|
|
) => {
|
|
try {
|
|
await switchChainWagmi(config, {
|
|
chainId: chains[targetChain].id,
|
|
});
|
|
} catch (error) {
|
|
handleError?.(error as SwitchChainErrorType);
|
|
}
|
|
};
|
|
|
|
return { chain, switchChain };
|
|
}
|
|
|
|
export function useAddSessionTokenToWallet(tokenIcon: string) {
|
|
const [isPending, setIsPending] = useState<boolean>(false);
|
|
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const { chain: currentChain } = useWalletChain();
|
|
|
|
const addToken = async () => {
|
|
setIsPending(true);
|
|
setIsSuccess(false);
|
|
try {
|
|
if (!currentChain || (currentChain !== CHAIN.MAINNET && currentChain !== CHAIN.TESTNET)) {
|
|
throw new Error('Invalid chain');
|
|
}
|
|
|
|
const ethereumProvider = getEthereumWindowProvider();
|
|
if (!ethereumProvider) {
|
|
throw new Error('No ethereum provider detected in window object');
|
|
}
|
|
|
|
const walletClient = createWalletClient({
|
|
chain: chains[currentChain],
|
|
transport: custom(ethereumProvider),
|
|
});
|
|
|
|
const wasAdded = await walletClient.watchAsset({
|
|
type: 'ERC20',
|
|
options: {
|
|
// The address of the token.
|
|
address: addresses.SENT[currentChain],
|
|
// A ticker symbol or shorthand, up to 5 characters.
|
|
symbol: SENT_SYMBOL.replaceAll('$', ''),
|
|
// The number of decimals in the token.
|
|
decimals: SENT_DECIMALS,
|
|
// A string URL of the token logo.
|
|
image: tokenIcon,
|
|
},
|
|
});
|
|
|
|
if (!wasAdded) {
|
|
throw new Error('Failed to add token');
|
|
}
|
|
|
|
setIsSuccess(true);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
setError(error.message);
|
|
} else {
|
|
setError('Failed to add token');
|
|
}
|
|
} finally {
|
|
setIsPending(false);
|
|
}
|
|
};
|
|
return { addToken, error, isPending, isSuccess };
|
|
}
|
|
|
|
export function useAddChain() {
|
|
const [isPending, setIsPending] = useState<boolean>(false);
|
|
const [isSuccess, setIsSuccess] = useState<boolean>(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const addChain = async (chainToAdd: CHAIN) => {
|
|
setIsPending(true);
|
|
setIsSuccess(false);
|
|
try {
|
|
const ethereumProvider = getEthereumWindowProvider();
|
|
if (!ethereumProvider) {
|
|
throw new Error('No ethereum provider detected in window object');
|
|
}
|
|
|
|
const chain = chains[chainToAdd];
|
|
|
|
const walletClient = createWalletClient({
|
|
chain,
|
|
transport: custom(ethereumProvider),
|
|
});
|
|
|
|
await walletClient.addChain({ chain });
|
|
|
|
setIsSuccess(true);
|
|
} catch (error) {
|
|
if (error instanceof Error) {
|
|
setError(error.message);
|
|
} else {
|
|
setError('Failed to add chain');
|
|
}
|
|
} finally {
|
|
setIsPending(false);
|
|
}
|
|
};
|
|
return { addChain, error, isPending, isSuccess };
|
|
}
|