From a6b08783d31db6dc67666f055155a70c304b09f9 Mon Sep 17 00:00:00 2001 From: jesse snyder Date: Wed, 23 Oct 2024 14:56:32 -0600 Subject: [PATCH 1/3] Feature/ensure correct receiver token (#30) * receiver currency selection is now controlled by sender currency selection * receiver token defined by sender token selection * disable submission if coins dont match --- web/src/components/DepositCard/DepositCard.tsx | 6 ++++-- web/src/components/WithdrawCard/WithdrawCard.tsx | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/web/src/components/DepositCard/DepositCard.tsx b/web/src/components/DepositCard/DepositCard.tsx index 353525c..d7dcfd5 100644 --- a/web/src/components/DepositCard/DepositCard.tsx +++ b/web/src/components/DepositCard/DepositCard.tsx @@ -351,7 +351,7 @@ export default function DepositCard(): React.ReactElement { {/* NOTE - the placeholder happens to only be shown when there isn't a matching */} {/* evm currency. It's also always disabled because it's controlled by sender currency selection. */} {isLoading ? "Processing..." : "Deposit"} diff --git a/web/src/components/WithdrawCard/WithdrawCard.tsx b/web/src/components/WithdrawCard/WithdrawCard.tsx index f322208..62c8518 100644 --- a/web/src/components/WithdrawCard/WithdrawCard.tsx +++ b/web/src/components/WithdrawCard/WithdrawCard.tsx @@ -374,7 +374,7 @@ export default function WithdrawCard(): React.ReactElement { {selectedIbcChain && ibcCurrencyOptions && (
{isLoading ? "Processing..." : "Withdraw"} From 99bfcdb16330d676098ef8fe809d3ddf2561840c Mon Sep 17 00:00:00 2001 From: jesse snyder Date: Wed, 23 Oct 2024 21:07:20 -0600 Subject: [PATCH 2/3] Feature/erc20 balances (#32) * show erc20 balances * use label for native token balance * refactor deposit card to use new evm wallet hook features --- .../components/DepositCard/DepositCard.tsx | 92 +++----- .../components/WithdrawCard/WithdrawCard.tsx | 92 ++------ web/src/config/chainConfigs/index.ts | 9 + .../EthWallet/hooks/useEvmChainSelection.ts | 59 ----- .../EthWallet/hooks/useEvmChainSelection.tsx | 208 ++++++++++++++++++ .../EthWallet/hooks/useSyncWalletProviders.ts | 3 + .../hooks/useIbcChainSelection.tsx | 2 +- 7 files changed, 262 insertions(+), 203 deletions(-) delete mode 100644 web/src/features/EthWallet/hooks/useEvmChainSelection.ts create mode 100644 web/src/features/EthWallet/hooks/useEvmChainSelection.tsx diff --git a/web/src/components/DepositCard/DepositCard.tsx b/web/src/components/DepositCard/DepositCard.tsx index d7dcfd5..880cc14 100644 --- a/web/src/components/DepositCard/DepositCard.tsx +++ b/web/src/components/DepositCard/DepositCard.tsx @@ -1,5 +1,5 @@ import type React from "react"; -import { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { useContext, useEffect, useMemo, useState } from "react"; import { Dec, DecUtils } from "@keplr-wallet/unit"; import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer"; @@ -8,27 +8,25 @@ import type { EvmChainInfo, IbcChainInfo } from "config/chainConfigs"; import { useConfig } from "config/hooks/useConfig"; import { NotificationType } from "features/Notifications/components/Notification/types"; import { NotificationsContext } from "features/Notifications/contexts/NotificationsContext"; -import EthWalletConnector from "features/EthWallet/components/EthWalletConnector/EthWalletConnector"; -import { useEthWallet } from "features/EthWallet/hooks/useEthWallet"; import { useEvmChainSelection } from "features/EthWallet/hooks/useEvmChainSelection"; import { useIbcChainSelection } from "features/IbcChainSelector/hooks/useIbcChainSelection"; import { sendIbcTransfer } from "services/ibc"; export default function DepositCard(): React.ReactElement { const { addNotification } = useContext(NotificationsContext); - const { userAccount: evmUserAccount, selectedWallet } = useEthWallet(); const { evmChains, ibcChains } = useConfig(); const { + evmAccountAddress: recipientAddress, selectEvmChain, evmChainsOptions, selectedEvmChain, selectEvmCurrency, evmCurrencyOptions, + evmBalance, + isLoadingEvmBalance, + connectEVMWallet, } = useEvmChainSelection(evmChains); - const defaultEvmChainOption = useMemo(() => { - return evmChainsOptions[0] || null; - }, [evmChainsOptions]); const defaultEvmCurrencyOption = useMemo(() => { return evmCurrencyOptions[0] || null; }, [evmCurrencyOptions]); @@ -94,7 +92,6 @@ export default function DepositCard(): React.ReactElement { const [amount, setAmount] = useState(""); const [isAmountValid, setIsAmountValid] = useState(false); - const [recipientAddress, setRecipientAddress] = useState(""); const [isRecipientAddressValid, setIsRecipientAddressValid] = useState(false); const [hasTouchedForm, setHasTouchedForm] = useState(false); @@ -114,7 +111,15 @@ export default function DepositCard(): React.ReactElement { setAmount(event.target.value); }; - const checkIsFormValid = (addressInput: string, amountInput: string) => { + const checkIsFormValid = ( + addressInput: string | null, + amountInput: string, + ) => { + if (addressInput === null) { + setIsRecipientAddressValid(false); + return; + } + const amount = Number.parseFloat(amountInput); const amountValid = amount > 0; setIsAmountValid(amountValid); @@ -123,26 +128,8 @@ export default function DepositCard(): React.ReactElement { setIsRecipientAddressValid(addressValid); }; - // NOTE - this was required to ensure the latest state was used in a callback - // used in the modal that connects to the evm wallet. - // create refs to hold the latest state values - const latestState = useRef({ - evmUserAccount, - selectedWallet, - recipientAddress, - selectedEvmChain, - }); - // update the ref whenever the state changes - useEffect(() => { - latestState.current = { - evmUserAccount, - selectedWallet, - recipientAddress, - selectedEvmChain, - }; - }, [evmUserAccount, selectedWallet, recipientAddress, selectedEvmChain]); - // ensure evm wallet connection when selected EVM chain changes + /* biome-ignore lint/correctness/useExhaustiveDependencies: */ useEffect(() => { if (!selectedEvmChain) { return; @@ -150,41 +137,6 @@ export default function DepositCard(): React.ReactElement { connectEVMWallet().then((_) => {}); }, [selectedEvmChain]); - const connectEVMWallet = async () => { - if (!selectedEvmChain) { - // select default chain if none selected, then return. effect handles retriggering. - selectEvmChain(defaultEvmChainOption.value); - return; - } - - addNotification({ - modalOpts: { - modalType: NotificationType.INFO, - title: "Connect EVM Wallet", - component: , - onCancel: () => { - const currentState = latestState.current; - setRecipientAddress(""); - selectEvmChain(null); - if (currentState.selectedWallet) { - currentState.selectedWallet = undefined; - } - }, - onConfirm: () => { - const currentState = latestState.current; - if (!currentState.evmUserAccount) { - setRecipientAddress(""); - selectEvmChain(null); - } else { - setRecipientAddress(currentState.evmUserAccount.address); - } - }, - }, - }); - }; - - // TODO - also set evm balance - const sendBalance = async () => { if (!selectedIbcChain || !selectedIbcCurrency) { addNotification({ @@ -196,11 +148,11 @@ export default function DepositCard(): React.ReactElement { }); return; } - if (!fromAddress) { + if (!fromAddress || !recipientAddress) { addNotification({ toastOpts: { toastType: NotificationType.WARNING, - message: "Please connect your Keplr wallet first.", + message: "Please connect your Keplr and EVM wallet first.", onAcknowledge: () => {}, }, }); @@ -368,6 +320,16 @@ export default function DepositCard(): React.ReactElement { Address: {recipientAddress}

)} + {recipientAddress && !isLoadingEvmBalance && ( +

+ Balance: {evmBalance} +

+ )} + {recipientAddress && isLoadingEvmBalance && ( +

+ Balance: +

+ )}
)} diff --git a/web/src/components/WithdrawCard/WithdrawCard.tsx b/web/src/components/WithdrawCard/WithdrawCard.tsx index 62c8518..f3abc53 100644 --- a/web/src/components/WithdrawCard/WithdrawCard.tsx +++ b/web/src/components/WithdrawCard/WithdrawCard.tsx @@ -1,15 +1,12 @@ import type React from "react"; -import { useContext, useEffect, useMemo, useRef, useState } from "react"; +import { useContext, useEffect, useMemo, useState } from "react"; import AnimatedArrowSpacer from "components/AnimatedDownArrowSpacer/AnimatedDownArrowSpacer"; import Dropdown, { type DropdownOption } from "components/Dropdown/Dropdown"; import type { EvmChainInfo, IbcChainInfo } from "config/chainConfigs"; import { useConfig } from "config/hooks/useConfig"; import { useIbcChainSelection } from "features/IbcChainSelector"; -import { - EthWalletConnector, - getAstriaWithdrawerService, -} from "features/EthWallet"; +import { getAstriaWithdrawerService } from "features/EthWallet"; import { useEthWallet } from "features/EthWallet/hooks/useEthWallet"; import { useEvmChainSelection } from "features/EthWallet/hooks/useEvmChainSelection"; import { NotificationType } from "features/Notifications/components/Notification/types"; @@ -17,20 +14,21 @@ import { NotificationsContext } from "features/Notifications/contexts/Notificati export default function WithdrawCard(): React.ReactElement { const { addNotification } = useContext(NotificationsContext); - const { userAccount: evmUserAccount, selectedWallet } = useEthWallet(); + const { selectedWallet } = useEthWallet(); const { evmChains, ibcChains } = useConfig(); const { + evmAccountAddress: fromAddress, selectEvmChain, evmChainsOptions, selectedEvmChain, selectEvmCurrency, evmCurrencyOptions, selectedEvmCurrency, + evmBalance, + isLoadingEvmBalance, + connectEVMWallet, } = useEvmChainSelection(evmChains); - const defaultEvmChainOption = useMemo(() => { - return evmChainsOptions[0] || null; - }, [evmChainsOptions]); const defaultEvmCurrencyOption = useMemo(() => { return evmCurrencyOptions[0] || null; }, [evmCurrencyOptions]); @@ -92,9 +90,6 @@ export default function WithdrawCard(): React.ReactElement { }; }, [selectedEvmCurrency, selectedIbcChain, defaultIbcCurrencyOption]); - const [fromAddress, setFromAddress] = useState(""); - const [balance, setBalance] = useState("0"); - const [isLoadingBalance, setIsLoadingBalance] = useState(false); const [amount, setAmount] = useState(""); const [isAmountValid, setIsAmountValid] = useState(false); @@ -104,34 +99,6 @@ export default function WithdrawCard(): React.ReactElement { const [isLoading, setIsLoading] = useState(false); const [isAnimating, setIsAnimating] = useState(false); - // create refs to hold the latest state values - const latestState = useRef({ - evmUserAccount, - selectedWallet, - recipientAddress, - selectedEvmChain, - }); - - // update the ref whenever the state changes - useEffect(() => { - latestState.current = { - evmUserAccount, - selectedWallet, - recipientAddress, - selectedEvmChain, - }; - }, [evmUserAccount, selectedWallet, recipientAddress, selectedEvmChain]); - - useEffect(() => { - if (evmUserAccount?.address) { - setFromAddress(evmUserAccount.address); - } - // TODO - get balance for selected currency - if (evmUserAccount?.balance) { - setBalance(`${evmUserAccount.balance} ${selectedEvmCurrency?.coinDenom}`); - } - }, [evmUserAccount, selectedEvmCurrency]); - useEffect(() => { if (amount || recipientAddress) { setHasTouchedForm(true); @@ -139,10 +106,12 @@ export default function WithdrawCard(): React.ReactElement { checkIsFormValid(amount, recipientAddress); }, [amount, recipientAddress]); + /* biome-ignore lint/correctness/useExhaustiveDependencies: */ useEffect(() => { if (!selectedEvmChain) { return; } + console.log("connecting EVM wallet"); connectEVMWallet().then((_) => {}); }, [selectedEvmChain]); @@ -165,45 +134,13 @@ export default function WithdrawCard(): React.ReactElement { setIsRecipientAddressValid(isRecipientAddressValid); }; - const connectEVMWallet = async () => { - if (!selectedEvmChain) { - // select default chain if none selected, then return. effect handles retriggering. - selectEvmChain(defaultEvmChainOption.value); - return; - } - - addNotification({ - modalOpts: { - modalType: NotificationType.INFO, - title: "Connect EVM Wallet", - component: , - onCancel: () => { - const currentState = latestState.current; - setFromAddress(""); - selectEvmChain(null); - if (currentState.selectedWallet) { - currentState.selectedWallet = undefined; - } - }, - onConfirm: () => { - const currentState = latestState.current; - if (!currentState.evmUserAccount) { - setFromAddress(""); - selectEvmChain(null); - } else { - setFromAddress(currentState.evmUserAccount.address); - } - }, - }, - }); - }; - const handleWithdraw = async () => { if ( !selectedWallet || !selectedEvmCurrency || !isAmountValid || - !recipientAddress + !recipientAddress || + !fromAddress ) { console.error( "Withdrawal cannot proceed: missing required fields or fields are invalid", @@ -321,7 +258,6 @@ export default function WithdrawCard(): React.ReactElement { )} - {/* TODO - show balance of whatever coin selected */} {fromAddress && (
{fromAddress && ( @@ -329,12 +265,12 @@ export default function WithdrawCard(): React.ReactElement { Address: {fromAddress}

)} - {fromAddress && !isLoadingBalance && ( + {fromAddress && !isLoadingEvmBalance && (

- Balance: {balance} + Balance: {evmBalance}

)} - {fromAddress && isLoadingBalance && ( + {fromAddress && isLoadingEvmBalance && (

Balance:

diff --git a/web/src/config/chainConfigs/index.ts b/web/src/config/chainConfigs/index.ts index b57071b..e19d60a 100644 --- a/web/src/config/chainConfigs/index.ts +++ b/web/src/config/chainConfigs/index.ts @@ -141,6 +141,15 @@ export type EvmCurrency = { iconClass?: string; }; +export function evmCurrencyBelongsToChain( + currency: EvmCurrency, + chain: EvmChainInfo, +): boolean { + // FIXME - what if two chains have currencies with the same coinDenom? + // e.g. USDC on Noble and USDC on Celestia + return chain.currencies?.includes(currency); +} + // EvmChains type maps labels to EvmChainInfo objects export type EvmChains = { [label: string]: EvmChainInfo; diff --git a/web/src/features/EthWallet/hooks/useEvmChainSelection.ts b/web/src/features/EthWallet/hooks/useEvmChainSelection.ts deleted file mode 100644 index 0d999dc..0000000 --- a/web/src/features/EthWallet/hooks/useEvmChainSelection.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useMemo, useState, useCallback } from "react"; -import type { DropdownOption } from "components/Dropdown/Dropdown"; -import type { EvmChainInfo, EvmChains, EvmCurrency } from "config/chainConfigs"; - -export function useEvmChainSelection(evmChains: EvmChains) { - const [selectedEvmChain, setSelectedEvmChain] = useState( - null, - ); - const [selectedEvmCurrency, setSelectedEvmCurrency] = - useState(null); - - const evmChainsOptions = useMemo(() => { - return Object.entries(evmChains).map( - ([chainLabel, chain]): DropdownOption => ({ - label: chainLabel, - value: chain, - leftIconClass: chain.iconClass, - }), - ); - }, [evmChains]); - - const selectEvmChain = useCallback((chain: EvmChainInfo | null) => { - setSelectedEvmChain(chain); - }, []); - - const evmCurrencyOptions = useMemo(() => { - if (!selectedEvmChain) { - return []; - } - - // can only withdraw the currency if it has a withdraw contract address defined - const withdrawableTokens = selectedEvmChain.currencies?.filter( - (currency) => - currency.erc20ContractAddress || - currency.nativeTokenWithdrawerContractAddress, - ); - - return withdrawableTokens.map( - (currency): DropdownOption => ({ - label: currency.coinDenom, - value: currency, - leftIconClass: currency.iconClass, - }), - ); - }, [selectedEvmChain]); - - const selectEvmCurrency = useCallback((currency: EvmCurrency) => { - setSelectedEvmCurrency(currency); - }, []); - - return { - selectedEvmChain, - selectEvmChain, - evmChainsOptions, - selectEvmCurrency, - selectedEvmCurrency, - evmCurrencyOptions, - }; -} diff --git a/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx b/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx new file mode 100644 index 0000000..2cee561 --- /dev/null +++ b/web/src/features/EthWallet/hooks/useEvmChainSelection.tsx @@ -0,0 +1,208 @@ +import React, { + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import type { DropdownOption } from "components/Dropdown/Dropdown"; +import { + type EvmChainInfo, + type EvmChains, + type EvmCurrency, + evmCurrencyBelongsToChain, +} from "config/chainConfigs"; +import { useEthWallet } from "./useEthWallet"; +import { + EthWalletConnector, + getAstriaWithdrawerService, +} from "features/EthWallet"; +import { NotificationType } from "features/Notifications/components/Notification/types"; +import { NotificationsContext } from "features/Notifications/contexts/NotificationsContext"; +import { AstriaErc20WithdrawerService } from "../services/AstriaWithdrawerService/AstriaWithdrawerService"; +import { formatBalance } from "utils"; + +export function useEvmChainSelection(evmChains: EvmChains) { + const { addNotification } = useContext(NotificationsContext); + + const [selectedEvmChain, setSelectedEvmChain] = useState( + null, + ); + const [selectedEvmCurrency, setSelectedEvmCurrency] = + useState(null); + const [evmAccountAddress, setEvmAccountAddress] = useState( + null, + ); + + const [evmBalance, setEvmBalance] = useState(null); + const [isLoadingEvmBalance, setIsLoadingEvmBalance] = + useState(false); + + const { selectedWallet, provider, userAccount } = useEthWallet(); + + useEffect(() => { + async function getAndSetBalance() { + if ( + !selectedWallet || + !userAccount || + !selectedEvmChain || + !selectedEvmCurrency || + !evmAccountAddress + ) { + console.debug("no selected wallet, user account, chain, or currency", { + selectedWallet, + userAccount, + selectedEvmChain, + selectedEvmCurrency, + evmAccountAddress, + }); + return; + } + if (!evmCurrencyBelongsToChain(selectedEvmCurrency, selectedEvmChain)) { + console.debug("currency doesn't belong to chain"); + return; + } + setIsLoadingEvmBalance(true); + try { + const contractAddress = + selectedEvmCurrency.erc20ContractAddress || + selectedEvmCurrency.nativeTokenWithdrawerContractAddress || + ""; + const withdrawerSvc = getAstriaWithdrawerService( + selectedWallet.provider, + contractAddress, + Boolean(selectedEvmCurrency.erc20ContractAddress), + ); + if (withdrawerSvc instanceof AstriaErc20WithdrawerService) { + const balanceRes = await withdrawerSvc.getBalance(evmAccountAddress); + const balanceStr = formatBalance(balanceRes.toString()); + const balance = `${balanceStr} ${selectedEvmCurrency.coinDenom}`; + setEvmBalance(balance); + } else { + const balance = `${userAccount.balance} ${selectedEvmCurrency.coinDenom}`; + setEvmBalance(balance); + } + setIsLoadingEvmBalance(false); + } catch (error) { + console.error("Failed to get balance from EVM", error); + setIsLoadingEvmBalance(false); + } + } + + getAndSetBalance().then((_) => {}); + }, [ + selectedEvmChain, + selectedEvmCurrency, + selectedWallet, + userAccount, + evmAccountAddress, + ]); + + const evmChainsOptions = useMemo(() => { + return Object.entries(evmChains).map( + ([chainLabel, chain]): DropdownOption => ({ + label: chainLabel, + value: chain, + leftIconClass: chain.iconClass, + }), + ); + }, [evmChains]); + + const selectEvmChain = useCallback((chain: EvmChainInfo | null) => { + setSelectedEvmChain(chain); + }, []); + + const evmCurrencyOptions = useMemo(() => { + if (!selectedEvmChain) { + return []; + } + + // can only withdraw the currency if it has a withdraw contract address defined + const withdrawableTokens = selectedEvmChain.currencies?.filter( + (currency) => + currency.erc20ContractAddress || + currency.nativeTokenWithdrawerContractAddress, + ); + + return withdrawableTokens.map( + (currency): DropdownOption => ({ + label: currency.coinDenom, + value: currency, + leftIconClass: currency.iconClass, + }), + ); + }, [selectedEvmChain]); + + const selectEvmCurrency = useCallback((currency: EvmCurrency) => { + setSelectedEvmCurrency(currency); + }, []); + + // create refs to hold the latest state values + const latestState = useRef({ + userAccount, + selectedWallet, + evmAccountAddress, + selectedEvmChain, + }); + + // update the ref whenever the state changes + useEffect(() => { + latestState.current = { + userAccount, + selectedWallet, + evmAccountAddress, + selectedEvmChain, + }; + }, [userAccount, selectedWallet, evmAccountAddress, selectedEvmChain]); + + const connectEVMWallet = async () => { + if (!selectedEvmChain) { + // select default chain if none selected, then return. effect handles retriggering. + setSelectedEvmChain(evmChainsOptions[0]?.value); + return; + } + + addNotification({ + modalOpts: { + modalType: NotificationType.INFO, + title: "Connect EVM Wallet", + component: , + onCancel: () => { + const currentState = latestState.current; + setEvmAccountAddress(""); + setSelectedEvmChain(null); + if (currentState.selectedWallet) { + currentState.selectedWallet = undefined; + } + }, + onConfirm: () => { + const currentState = latestState.current; + if (!currentState.userAccount) { + setEvmAccountAddress(""); + setSelectedEvmChain(null); + } else { + setEvmAccountAddress(currentState.userAccount.address); + } + }, + }, + }); + }; + + return { + evmChainsOptions, + evmCurrencyOptions, + + selectEvmChain, + selectEvmCurrency, + + selectedEvmChain, + selectedEvmCurrency, + + evmAccountAddress, + evmBalance, + isLoadingEvmBalance, + + connectEVMWallet, + }; +} diff --git a/web/src/features/EthWallet/hooks/useSyncWalletProviders.ts b/web/src/features/EthWallet/hooks/useSyncWalletProviders.ts index be5f7e6..3a467f4 100644 --- a/web/src/features/EthWallet/hooks/useSyncWalletProviders.ts +++ b/web/src/features/EthWallet/hooks/useSyncWalletProviders.ts @@ -32,6 +32,9 @@ const ethWalletProviderStore = { }, }; +/** + * A hook that listens for eip6963:announceProvider events and returns the detected wallet providers. + */ export const useSyncWalletProviders = () => useSyncExternalStore( ethWalletProviderStore.subscribe, diff --git a/web/src/features/IbcChainSelector/hooks/useIbcChainSelection.tsx b/web/src/features/IbcChainSelector/hooks/useIbcChainSelection.tsx index 71067f4..803fcb0 100644 --- a/web/src/features/IbcChainSelector/hooks/useIbcChainSelection.tsx +++ b/web/src/features/IbcChainSelector/hooks/useIbcChainSelection.tsx @@ -14,7 +14,7 @@ import { getKeplrFromWindow, } from "services/ibc"; import { NotificationsContext } from "features/Notifications/contexts/NotificationsContext"; -import { NotificationType } from "../../Notifications/components/Notification/types"; +import { NotificationType } from "features/Notifications/components/Notification/types"; /** * Custom hook to manage the selection of an IBC chain and currency. From eaab0f4eccf76de8852272610b18c349159150f3 Mon Sep 17 00:00:00 2001 From: Jesse Snyder Date: Wed, 23 Oct 2024 21:08:54 -0600 Subject: [PATCH 3/3] remove log --- web/src/components/WithdrawCard/WithdrawCard.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/web/src/components/WithdrawCard/WithdrawCard.tsx b/web/src/components/WithdrawCard/WithdrawCard.tsx index f3abc53..5f2b0a6 100644 --- a/web/src/components/WithdrawCard/WithdrawCard.tsx +++ b/web/src/components/WithdrawCard/WithdrawCard.tsx @@ -111,7 +111,6 @@ export default function WithdrawCard(): React.ReactElement { if (!selectedEvmChain) { return; } - console.log("connecting EVM wallet"); connectEVMWallet().then((_) => {}); }, [selectedEvmChain]);