From f1864ac88a3acad2cc4772626f54c0a1d75f13e7 Mon Sep 17 00:00:00 2001 From: J M Rossy Date: Sat, 27 Apr 2024 12:46:04 -0400 Subject: [PATCH] Fix issues with Celo Terminal signing and disconnection --- src/app/account/page.tsx | 2 +- src/config/consts.ts | 3 ++ src/config/wagmi.tsx | 2 - src/features/account/hooks.ts | 2 +- src/features/locking/useLockedStatus.ts | 7 ++- src/features/staking/ActiveStakesTable.tsx | 2 +- .../useWriteContractWithReceipt.ts | 52 +++++++++++++++++-- src/features/wallet/WalletDropdown.tsx | 17 ++++-- src/utils/navigation.ts | 4 +- 9 files changed, 75 insertions(+), 16 deletions(-) diff --git a/src/app/account/page.tsx b/src/app/account/page.tsx index 911590a..280f454 100644 --- a/src/app/account/page.tsx +++ b/src/app/account/page.tsx @@ -37,7 +37,7 @@ import { useAccount } from 'wagmi'; export default function Page() { const account = useAccount(); const address = account?.address; - usePageInvariant(!!address, '/', 'No account connected'); + usePageInvariant(!!address, '/'); const { balance: walletBalance } = useBalance(address); const { lockedBalances } = useLockedStatus(address); diff --git a/src/config/consts.ts b/src/config/consts.ts index 527d40b..599304b 100644 --- a/src/config/consts.ts +++ b/src/config/consts.ts @@ -24,3 +24,6 @@ export const EXECUTION_STAGE_EXPIRY_TIME = 259_200_000; // 3 days // Delegation export const MAX_NUM_DELEGATEES = 10; + +// Wallets +export const WALLET_CONNECT_CONNECTOR_ID = 'walletConnect'; diff --git a/src/config/wagmi.tsx b/src/config/wagmi.tsx index be13d38..382e441 100644 --- a/src/config/wagmi.tsx +++ b/src/config/wagmi.tsx @@ -1,7 +1,6 @@ import { RainbowKitProvider, connectorsForWallets, lightTheme } from '@rainbow-me/rainbowkit'; import '@rainbow-me/rainbowkit/styles.css'; import { - ledgerWallet, metaMaskWallet, omniWallet, rainbowWallet, @@ -29,7 +28,6 @@ const connectors = connectorsForWallets( rainbowWallet, omniWallet, trustWallet, - ledgerWallet, ], }, ], diff --git a/src/features/account/hooks.ts b/src/features/account/hooks.ts index d82331b..e17450f 100644 --- a/src/features/account/hooks.ts +++ b/src/features/account/hooks.ts @@ -43,7 +43,7 @@ export function useLockedBalance(address?: Address) { }; } -// Note, this retrieves the address's info from the Accounts contract +// Note, this retrieves the address' info from the Accounts contract // It has nothing to do with wallets or backend services export function useAccountDetails(address?: Address) { const { diff --git a/src/features/locking/useLockedStatus.ts b/src/features/locking/useLockedStatus.ts index b67ee63..a331928 100644 --- a/src/features/locking/useLockedStatus.ts +++ b/src/features/locking/useLockedStatus.ts @@ -2,6 +2,7 @@ import { lockedGoldABI } from '@celo/abis'; import { useQuery } from '@tanstack/react-query'; import { useToastError } from 'src/components/notifications/useToastError'; import { Addresses } from 'src/config/contracts'; +import { useAccountDetails } from 'src/features/account/hooks'; import { LockedStatus, PendingWithdrawal } from 'src/features/locking/types'; import { logger } from 'src/utils/logger'; import { PublicClient } from 'viem'; @@ -10,10 +11,12 @@ import { usePublicClient } from 'wagmi'; export function useLockedStatus(address?: Address) { const publicClient = usePublicClient(); + const { isRegistered } = useAccountDetails(address); + const { isLoading, isError, error, data, refetch } = useQuery({ - queryKey: ['useLockedStatus', publicClient, address], + queryKey: ['useLockedStatus', publicClient, address, isRegistered], queryFn: async () => { - if (!address || !publicClient) return null; + if (!address || !isRegistered || !publicClient) return null; logger.debug('Fetching locked status balance and withdrawals'); return fetchLockedStatus(publicClient, address); }, diff --git a/src/features/staking/ActiveStakesTable.tsx b/src/features/staking/ActiveStakesTable.tsx index 80cc960..b9e1190 100644 --- a/src/features/staking/ActiveStakesTable.tsx +++ b/src/features/staking/ActiveStakesTable.tsx @@ -68,7 +68,7 @@ export function ActiveStakesTable({ return Loading staking data; } - if (!objLength(groupToStake)) { + if (!tableData.length) { return ( any, showTxSuccessToast = false, ) { + const account = useAccount(); + const publicClient = usePublicClient(); + const { data: hash, error: writeError, @@ -21,6 +33,38 @@ export function useWriteContractWithReceipt( writeContract, } = useWriteContract(); + const writeContractWithTxPrep = useCallback( + async (args: Parameters>[0]) => { + // Some WalletConnect-ed wallets like Celo Terminal require that the tx be fully populated + if (account?.connector?.id === WALLET_CONNECT_CONNECTOR_ID) { + if (!publicClient) { + logger.error('Public client not ready for WalletConnect wallet tx'); + return; + } + + logger.debug('Preparing transaction request for WalletConnect wallet'); + const encodedData = encodeFunctionData(args); + const request = await publicClient.prepareTransactionRequest({ + to: args.address, + chainId: args.chainId, + value: args.value, + data: encodedData, + account: account.address, + }); + logger.debug('Transaction request prepared, triggering write'); + writeContract({ + ...args, + gas: request.gas, + gasPrice: request.gasPrice, + } as any); + } else { + logger.debug('Trigger transaction write'); + writeContract(args); + } + }, + [writeContract, publicClient, account], + ); + const { isLoading: isConfirming, isSuccess: isConfirmed, @@ -68,6 +112,6 @@ export function useWriteContractWithReceipt( isError: isWriteError || isWaitError, isLoading: isPending || isConfirming, isConfirmed, - writeContract, + writeContract: writeContractWithTxPrep, }; } diff --git a/src/features/wallet/WalletDropdown.tsx b/src/features/wallet/WalletDropdown.tsx index 9b9372c..44eb44d 100644 --- a/src/features/wallet/WalletDropdown.tsx +++ b/src/features/wallet/WalletDropdown.tsx @@ -9,13 +9,24 @@ import { useStakingRewards } from 'src/features/staking/rewards/useStakingReward import { useStakingBalances } from 'src/features/staking/useStakingBalances'; import { shortenAddress } from 'src/utils/addresses'; import { useCopyHandler } from 'src/utils/clipboard'; +import { logger } from 'src/utils/logger'; import { useAccount, useDisconnect } from 'wagmi'; import { useBalance, useLockedBalance } from '../account/hooks'; export function WalletDropdown() { const { address, isConnected } = useAccount(); const { openConnectModal } = useConnectModal(); - const { disconnect } = useDisconnect(); + const { disconnectAsync } = useDisconnect(); + + const onDisconnect = async () => { + try { + await disconnectAsync(); + } catch (err) { + logger.error('Error disconnecting wallet', err); + // Sometimes a page reload helps handle disconnection issues + window.location.reload(); + } + }; return (
@@ -29,7 +40,7 @@ export function WalletDropdown() { )} buttonClasses={`${OutlineButtonClassName} pl-1.5 pr-3 all:py-1`} modal={({ close }) => ( - + )} modalClasses="p-4" /> @@ -46,7 +57,7 @@ function DropdownContent({ close, }: { address: Address; - disconnect: () => void; + disconnect: () => any; close: () => void; }) { const { balance: walletBalance } = useBalance(address); diff --git a/src/utils/navigation.ts b/src/utils/navigation.ts index f672de0..3794e70 100644 --- a/src/utils/navigation.ts +++ b/src/utils/navigation.ts @@ -2,13 +2,13 @@ import { useRouter } from 'next/navigation'; import { useEffect, useState } from 'react'; import { toast } from 'react-toastify'; -export function usePageInvariant(predicate: any, fallbackPath: string, errorMessage: string) { +export function usePageInvariant(predicate: any, fallbackPath: string, errorMessage?: string) { const [isMessageShown, setIsMessageShown] = useState(false); const router = useRouter(); useEffect(() => { if (!predicate && !isMessageShown) { setIsMessageShown(true); - toast.error(errorMessage); + if (errorMessage) toast.error(errorMessage); router.replace(fallbackPath); } }, [router, predicate, fallbackPath, errorMessage, isMessageShown]);