Skip to content

[TOOL-3047] Dashboard: Fix TransactionButton opening "No funds" Modal when quickly clicking it after switching chain #5941

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 14, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 35 additions & 21 deletions apps/dashboard/src/components/buttons/MismatchButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,41 +63,42 @@ const GAS_FREE_CHAINS = [
247253, // Saakuru Testnet
];

function useNetworkMismatchAdapter(desiredChainId: number) {
function useIsNetworkMismatch(txChainId: number) {
const walletChainId = useActiveWalletChain()?.id;
if (!walletChainId) {
// simply not ready yet, assume false
return false;
}
// otherwise, compare the chain ids
return walletChainId !== desiredChainId;
return walletChainId !== txChainId;
}

type MistmatchButtonProps = React.ComponentProps<typeof Button> & {
desiredChainId: number;
txChainId: number;
twAccount: Account | undefined;
};

export const MismatchButton = forwardRef<
HTMLButtonElement,
MistmatchButtonProps
>((props, ref) => {
const { desiredChainId, twAccount, ...buttonProps } = props;
const { txChainId, twAccount, ...buttonProps } = props;
const account = useActiveAccount();
const wallet = useActiveWallet();
const activeWalletChain = useActiveWalletChain();
const [dialog, setDialog] = useState<undefined | "no-funds" | "pay">();
const { theme } = useTheme();
const client = useThirdwebClient();
const pathname = usePathname();
const txChain = useV5DashboardChain(txChainId);

const evmBalance = useWalletBalance({
const txChainBalance = useWalletBalance({
address: account?.address,
chain: activeWalletChain,
chain: txChain,
client,
});

const networksMismatch = useNetworkMismatchAdapter(desiredChainId);
const networksMismatch = useIsNetworkMismatch(txChainId);
const [isMismatchPopoverOpen, setIsMismatchPopoverOpen] = useState(false);
const trackEvent = useTrack();

Expand Down Expand Up @@ -130,8 +131,15 @@ export const MismatchButton = forwardRef<
);
}

const isBalanceRequired = !GAS_FREE_CHAINS.includes(txChainId);

const notEnoughBalance =
(evmBalance.data?.value || 0n) === 0n && !GAS_FREE_CHAINS.includes(chainId);
(txChainBalance.data?.value || 0n) === 0n && isBalanceRequired;

const disabled =
buttonProps.disabled ||
// if user is about to trigger a transaction on txChain, but txChainBalance is not yet loaded and is required before proceeding
(!networksMismatch && txChainBalance.isPending && isBalanceRequired);

return (
<>
Expand All @@ -150,6 +158,7 @@ export const MismatchButton = forwardRef<
<PopoverTrigger asChild>
<Button
{...buttonProps}
disabled={disabled}
type={
networksMismatch || notEnoughBalance ? "button" : buttonProps.type
}
Expand Down Expand Up @@ -182,7 +191,7 @@ export const MismatchButton = forwardRef<
</PopoverTrigger>
<PopoverContent className="min-w-[350px]" side="top" sideOffset={10}>
<MismatchNotice
desiredChainId={desiredChainId}
txChainId={txChainId}
onClose={(hasSwitched) => {
if (hasSwitched) {
setIsMismatchPopoverOpen(false);
Expand Down Expand Up @@ -416,8 +425,8 @@ function GetFundsFromFaucet(props: {

const MismatchNotice: React.FC<{
onClose: (hasSwitched: boolean) => void;
desiredChainId: number;
}> = ({ onClose, desiredChainId }) => {
txChainId: number;
}> = ({ onClose, txChainId }) => {
const connectedChainId = useActiveWalletChain()?.id;
const switchNetwork = useSwitchActiveWalletChain();
const activeWallet = useActiveWallet();
Expand All @@ -427,14 +436,15 @@ const MismatchNotice: React.FC<{
const walletConnectedNetworkInfo = connectedChainId
? idToChain.get(connectedChainId)
: undefined;
const chain = desiredChainId ? idToChain.get(desiredChainId) : undefined;
const chainV5 = useV5DashboardChain(desiredChainId);

const txChain = txChainId ? idToChain.get(txChainId) : undefined;
const chainV5 = useV5DashboardChain(txChainId);
const switchNetworkMutation = useMutation({
mutationFn: switchNetwork,
});

const onSwitchWallet = useCallback(async () => {
if (actuallyCanAttemptSwitch && desiredChainId && chainV5) {
if (actuallyCanAttemptSwitch && txChainId && chainV5) {
try {
await switchNetworkMutation.mutateAsync(chainV5);
onClose(true);
Expand All @@ -446,7 +456,7 @@ const MismatchNotice: React.FC<{
}, [
chainV5,
actuallyCanAttemptSwitch,
desiredChainId,
txChainId,
onClose,
switchNetworkMutation,
]);
Expand All @@ -458,15 +468,19 @@ const MismatchNotice: React.FC<{
Network Mismatch
</h3>

<p className="text-muted-foreground">
<p className="mb-1 text-muted-foreground">
Your wallet is connected to the{" "}
<span className="font-medium capitalize">
<span className="font-semibold capitalize">
{walletConnectedNetworkInfo?.name ||
`Chain ID #${connectedChainId}`}
</span>{" "}
network but this action requires you to connect to the{" "}
<span className="font-medium capitalize">
{chain?.name || `Chain ID #${desiredChainId}`}
network
</p>

<p className="text-muted-foreground">
This action requires you to connect to the{" "}
<span className="font-semibold capitalize">
{txChain?.name || `Chain ID #${txChainId}`}
</span>{" "}
network.
</p>
Expand All @@ -485,7 +499,7 @@ const MismatchNotice: React.FC<{
<UnplugIcon className="size-4 shrink-0" />
)}
<span className="line-clamp-1 block truncate">
Switch {chain ? `to ${chain.name}` : "chain"}
Switch {txChain ? `to ${txChain.name}` : "chain"}
</span>
</Button>

Expand Down
4 changes: 2 additions & 2 deletions apps/dashboard/src/components/buttons/TransactionButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export const TransactionButton: React.FC<TransactionButtonProps> = ({
<PopoverTrigger asChild>
<ButtonComponent
variant={variant || "primary"}
desiredChainId={txChainID}
txChainId={txChainID}
twAccount={twAccount}
{...restButtonProps}
disabled={disabled}
Expand Down Expand Up @@ -189,7 +189,7 @@ const ExternalApprovalNotice: React.FC<ExternalApprovalNoticeProps> = ({
<h4 className="text-foreground">Approve Transaction</h4>

<p className="text-muted-foreground text-sm">
You will need to approve this transaction in your connected wallet.
Your connected wallet will prompt you to approve this transaction
</p>

{showHint && (
Expand Down
Loading