Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
4 changes: 3 additions & 1 deletion src/app/account/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ import UnlockIcon from 'src/images/icons/unlock.svg';
import WithdrawIcon from 'src/images/icons/withdraw.svg';
import { shortenAddress } from 'src/utils/addresses';
import { usePageInvariant } from 'src/utils/navigation';
import { useIsMiniPay } from 'src/utils/useIsMiniPay';
import { StakingMode, useStakingMode } from 'src/utils/useStakingMode';
import useTabs from 'src/utils/useTabs';
import { Address } from 'viem';
Expand All @@ -52,7 +53,8 @@ import { useAccount } from 'wagmi';
export default function Page() {
const account = useAccount();
const address = account?.address;
usePageInvariant(!!address, '/');
const isMiniPay = useIsMiniPay();
usePageInvariant(!!address || isMiniPay, '/');

const { signingFor, isVoteSigner } = useVoteSignerToAccount(address);
const { balance: walletBalance } = useBalance(signingFor);
Expand Down
14 changes: 13 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';
import { useMemo } from 'react';
import { useRouter } from 'next/navigation';
import { useEffect, useMemo } from 'react';
import { Fade } from 'src/components/animation/Fade';
import { SkeletonBlock } from 'src/components/animation/Skeleton';
import { SolidButton } from 'src/components/buttons/SolidButton';
Expand All @@ -17,8 +18,19 @@ import { useIsMiniPay } from 'src/utils/useIsMiniPay';
import { useStakingMode } from 'src/utils/useStakingMode';

export default function Page() {
const isMiniPay = useIsMiniPay();
const router = useRouter();

useEffect(() => {
if (isMiniPay) {
router.replace('/account');
Comment on lines +25 to +26
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate MiniPay home redirect on wallet availability

This redirect is unconditional for MiniPay, so it also runs when no wallet address is connected (or before wagmi finishes hydrating). In that state, /account immediately redirects back to / via usePageInvariant(!!address, '/') in src/app/account/page.tsx, creating a //account navigation loop for MiniPay users and leaving no stable landing page. Redirecting only after account readiness (or when address is present) avoids that loop.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve minipay override when redirecting from home

The home redirect drops the current query string, so a session opened at /?minipay is immediately sent to /account without minipay, and useIsMiniPay() on the destination page falls back to window.ethereum?.isMiniPay. In a normal browser this disables the override after one navigation (and can bounce users back to / when no wallet is connected), which breaks the new query-param MiniPay emulation flow.

Useful? React with 👍 / 👎.

}
}, [isMiniPay, router]);

const { groups, totalVotes } = useValidatorGroups();

if (isMiniPay) return null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid running validator query before MiniPay early return

MiniPay now redirects away from / and returns null, but this happens only after useValidatorGroups() runs, so each MiniPay home visit still triggers the full validator-group fetch path and its error-toast side effects (useToastError in src/features/validators/useValidatorGroups.ts) for data that is never rendered. This adds avoidable RPC load and can show irrelevant errors during redirect.

Useful? React with 👍 / 👎.


return (
<Section className="mt-4">
<div className="space-y-6">
Expand Down
9 changes: 8 additions & 1 deletion src/components/nav/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useCallback } from 'react';
import { ChevronIcon } from 'src/components/icons/Chevron';
import { CeloGlyph } from 'src/components/logos/Celo';
import { DropdownMenu } from 'src/components/menus/Dropdown';
import { useStCELOBalance } from 'src/features/account/hooks';
import Bridge from 'src/images/icons/bridge.svg';
import Dashboard from 'src/images/icons/dashboard.svg';
import Delegate from 'src/images/icons/delegate.svg';
Expand All @@ -18,7 +19,7 @@ import { useAccount } from 'wagmi';

const LINKS = (isWalletConnected?: boolean) => [
{ label: 'Staking', to: '/', icon: Staking },
{ label: 'Governance', to: '/governance', icon: Governance },
{ label: 'Governance', to: '/governance', icon: Governance, hideInMiniPayUntilStaked: true },
{ label: 'Delegate', to: '/delegate', icon: Delegate, hideInMiniPay: true },
{ label: 'Bridge', to: '/bridge', icon: Bridge, hideInMiniPay: true },
{ label: 'Names', to: 'https://names.celo.org', icon: ENS, hideInMiniPay: true },
Expand All @@ -30,6 +31,8 @@ export function NavBar({ collapsed }: { collapsed?: boolean }) {
const { address } = useAccount();
const trackEvent = useTrackEvent();
const isMiniPay = useIsMiniPay();
const { stCELOBalances } = useStCELOBalance(address);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid stCELO polling for non-MiniPay navigation

The new useStCELOBalance(address) call executes for every connected user, even outside MiniPay, because the hook is not gated and its internal queries are enabled by !!address with periodic refetching. Since hideInMiniPayUntilStaked is only checked later in the filters, regular web users now do recurring stCELO RPC reads (and can see related error toasts) despite this feature being MiniPay-only.

Useful? React with 👍 / 👎.

const hasStaked = stCELOBalances.total > 0n;

const handleNavClick = useCallback(
(item: string) => {
Expand All @@ -43,6 +46,7 @@ export function NavBar({ collapsed }: { collapsed?: boolean }) {
<ul className="flex list-none items-center justify-center space-x-6 overflow-hidden">
{LINKS(!!address)
.filter((l) => !(isMiniPay && l.hideInMiniPay))
.filter((l) => !(isMiniPay && l.hideInMiniPayUntilStaked && !hasStaked))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate MiniPay governance on CELO stake, not stCELO balance

The new MiniPay Governance filter depends on hasStaked, but hasStaked is derived only from useStCELOBalance; MiniPay users are forced into CELO mode (src/utils/useStakingMode.tsx:23) and stake through the CELO flow, so they can complete staking while stCELOBalances.total remains 0n. In that path, Governance stays hidden indefinitely instead of reappearing after the user has staked.

Useful? React with 👍 / 👎.

.map((l) => {
const isSelected = l.to === pathname || (l.to !== '/' && pathname?.startsWith(l.to));

Expand Down Expand Up @@ -77,6 +81,8 @@ export function MobileNavDropdown({ className }: { className?: string }) {
const { address } = useAccount();
const trackEvent = useTrackEvent();
const isMiniPay = useIsMiniPay();
const { stCELOBalances } = useStCELOBalance(address);
const hasStaked = stCELOBalances.total > 0n;

const handleNavClick = useCallback(
(item: string) => {
Expand All @@ -97,6 +103,7 @@ export function MobileNavDropdown({ className }: { className?: string }) {
menuClasses="space-y-8 py-6 px-8"
menuItems={LINKS(!!address)
.filter((l) => !(isMiniPay && l.hideInMiniPay))
.filter((l) => !(isMiniPay && l.hideInMiniPayUntilStaked && !hasStaked))
.map((l) => {
return (
<Link
Expand Down
Loading