Skip to content

feat: add download button to account pages#883

Open
askov wants to merge 16 commits intosolana-foundation:masterfrom
hoodieshq:feat/account-download-button
Open

feat: add download button to account pages#883
askov wants to merge 16 commits intosolana-foundation:masterfrom
hoodieshq:feat/account-download-button

Conversation

@askov
Copy link
Contributor

@askov askov commented Mar 18, 2026

Description

  • Add a download button to account page headers (token accounts, unknown accounts, upgradeable loader accounts) that lets users download raw account data in hex, base58, or base64 encoding
  • Replace the legacy Bootstrap dropdown with a new Radix UI (shadcn/ui) DropdownMenu component and migrate all existing download dropdowns to use it
  • Reorganize account data-fetching hooks into FSD layers (@entities/account, @features/account, app/shared/)

Type of change

  • New feature

Screenshots

localhost_3000_address_UNCaXzXkR3vp8mbCJyxWUvwuRk5uHgzrwe6jcWPfiUR

Testing

  • Verify the download button appears on token mint, token account, unknown account, and upgradeable program account pages (example 1, example 2, example 3)
  • Verify the button does not appear for accounts with 0 data bytes (example)
  • Verify existing download dropdowns on transaction detail and inspector pages still work (example)
  • Verify clicking the dropdown fetches data and offers hex/base58/base64 download options
  • Run pnpm test:ci — new unit tests cover useAccountsInfo, useRawAccountData, and AccountDownloadDropdown
  • pnpm sb — new Storybook stories for DropdownMenu and DownloadDropdown render correctly

Related Issues

A part of HOO-356

Checklist

  • My code follows the project's style guidelines
  • I have added tests that prove my fix/feature works
  • All tests pass locally and in CI
  • I have run build:info script to update build information
  • CI/CD checks pass
  • I have included screenshots for protocol screens (if applicable)

@vercel
Copy link

vercel bot commented Mar 18, 2026

@askov is attempting to deploy a commit to the Solana Foundation Team on Vercel.

A member of the Team first needs to authorize it.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 18, 2026

Greptile Summary

This PR adds a raw-data download button to token mint, token account, unknown account, and upgradeable program account pages. It replaces the legacy Bootstrap DropdownMenu/DownloadableDropdown with a new Radix UI (shadcn/ui) DropdownMenu component, reorganizes account-related hooks into FSD layers (@entities/account, @features/account), and introduces useRefreshAccount as a drop-in replacement for useFetchAccountInfo that additionally invalidates the lazily-fetched raw-data SWR cache on refresh.

Key changes:

  • New DownloadDropdown (shared) and AccountDownloadDropdown (feature) components replace DownloadableDropdown across transaction history, inspector, and account pages.
  • useRawAccountData lazily fetches raw account bytes via SWR with all auto-revalidation disabled; data is only requested when the download dropdown first opens.
  • Previous review concerns (error state not surfaced, null type mismatch on page-client.tsx, AccountDownloadDropdown not forwarding error) have all been addressed.
  • Two minor follow-up items remain: mutate() fires on every dropdown open rather than only when data is absent, and a useMemo in TransactionHistoryCard is effectively a no-op because serialize() runs outside the memo.

Confidence Score: 4/5

  • Safe to merge; all previously flagged blocking issues are resolved and only two minor non-breaking style items remain.
  • All P0/P1 concerns from prior rounds are addressed: error state is propagated, null type mismatch is fixed, and AccountDownloadDropdown correctly forwards error to DownloadDropdown. The two remaining items — unconditional mutate() on every dropdown open and an ineffective useMemo — are P2 style issues that don't break functionality or cause data loss. Feature is well-covered by new unit tests.
  • app/features/account/ui/AccountDownloadDropdown.tsx (mutate guard) and app/components/account/history/TransactionHistoryCard.tsx (useMemo dependency)

Important Files Changed

Filename Overview
app/features/account/ui/AccountDownloadDropdown.tsx New component that gates download on space !== 0 and lazily fetches raw account data on dropdown open — mutate() is called unconditionally, firing a new RPC call on every open instead of just the first.
app/shared/components/DownloadDropdown.tsx New shared Radix-UI-based download dropdown replacing the Bootstrap-based DownloadableDropdown; correctly handles loading, error, and data states with a static DefaultTrigger constant and encodeBytes helpers.
app/entities/account/model/use-raw-account-data.ts New SWR hook for lazy-fetching raw account bytes; correct use of revalidateOnMount: false to prevent auto-fetch on mount; returns the full SWR object so callers can access data, error, isLoading, and mutate.
app/entities/account/model/use-refresh-account.ts Drop-in replacement for useFetchAccountInfo that also invalidates the SWR raw-data cache key; clean, well-commented, correctly memoized with useCallback.
app/components/account/history/TransactionHistoryCard.tsx Migrated from DownloadableDropdown to DownloadDropdown; useMemo wrapping new Uint8Array(serialized) is ineffective because serialize() is called outside the memo, producing a new reference every render.
app/components/shared/ui/dropdown-menu.tsx Standard shadcn/ui Radix DropdownMenu wrapper; well-structured with proper forwardRef, display names, and project-scoped Tailwind classes.
app/components/account/TokenAccountSection.tsx Migrated useFetchAccountInfouseRefreshAccount and added AccountDownloadDropdown to three token card headers (FungibleTokenMintAccountCard, NonFungibleTokenMintAccountCard, TokenAccountCard) with correct e-gap-2 spacing.
app/components/account/UpgradeableLoaderAccountSection.tsx Added AccountDownloadDropdown to UpgradeableProgramSection header; migrated all useFetchAccountInfouseRefreshAccount; consistent with other card updates.
app/components/account/UnknownAccountCard.tsx Adds AccountDownloadDropdown to the unknown account card header; previous spacing concern (missing e-gap-2) has been resolved in this PR.
app/entities/account/index.ts Clean FSD barrel export for the account entity layer; exports useAccountsInfo, useRawAccountData, and useRefreshAccount.

Sequence Diagram

sequenceDiagram
    participant User
    participant AccountDownloadDropdown
    participant useRawAccountData (SWR)
    participant DownloadDropdown
    participant Solana RPC

    User->>AccountDownloadDropdown: mounts (space != 0)
    Note over useRawAccountData (SWR): revalidateOnMount: false — no fetch yet

    User->>DownloadDropdown: opens dropdown
    DownloadDropdown->>AccountDownloadDropdown: onOpenChange(true)
    AccountDownloadDropdown->>useRawAccountData (SWR): mutate()
    useRawAccountData (SWR)->>Solana RPC: getAccountInfo(address)
    Note over DownloadDropdown: shows "Loading hex/base58/base64…" (disabled)
    Solana RPC-->>useRawAccountData (SWR): AccountInfo { data: Uint8Array }
    useRawAccountData (SWR)-->>AccountDownloadDropdown: rawData = Uint8Array
    AccountDownloadDropdown-->>DownloadDropdown: data=rawData, loading=false
    Note over DownloadDropdown: shows "Download hex/base58/base64" (enabled)

    User->>DownloadDropdown: clicks "Download hex"
    DownloadDropdown->>DownloadDropdown: encodeBytes(data, "hex")
    DownloadDropdown->>User: triggerDownloadText → .txt file

    User->>AccountDownloadDropdown: clicks Refresh button
    AccountDownloadDropdown->>useRawAccountData (SWR): mutate(rawAccountDataKey)
    Note over useRawAccountData (SWR): invalidates cached raw data
Loading

Reviews (7): Last reviewed commit: "fix: overflowing card header buttons" | Re-trigger Greptile

@askov askov marked this pull request as draft March 18, 2026 14:09
@askov askov marked this pull request as ready for review March 19, 2026 06:04
@askov
Copy link
Contributor Author

askov commented Mar 19, 2026

@greptile-apps review please

@askov
Copy link
Contributor Author

askov commented Mar 19, 2026

@greptile-apps please review

@askov
Copy link
Contributor Author

askov commented Mar 19, 2026

@greptile-apps review and update summary

@askov
Copy link
Contributor Author

askov commented Mar 19, 2026

@greptile-apps review please


function handleDownload(data: ByteArray, encoding: EncodingFormat, filename: string) {
const encoded = encodeBytes(data, encoding);
triggerDownloadText(encoded, `${filename}_${encoding}.txt`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Any of these might cause an unhandled exception. I think having a tooltip here is a good one

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a try-catch block, but honestly, I don’t think it makes much sense. This isn't an error we expect, it could be due to malformed data or browser quirks, I guess. There’s not much we can do about it-it's non-actionable.

Using a tooltip for an error is an even more over-engineered approach. A toast notification might make more sense, though. I don't know. Why do you believe there should be some UI feedback?

@askov askov force-pushed the feat/account-download-button branch from 6307b5b to 38b1732 Compare March 20, 2026 10:39
@askov
Copy link
Contributor Author

askov commented Mar 20, 2026

@greptile-apps review please

)}
<span className="me-2"></span>
<DownloadableDropdown filename={signature} data={rawDetails?.data?.raw?.message.serialize() || null} />
<DownloadDropdown filename={signature} data={rawDetails?.data?.raw?.message.serialize()} />
Copy link
Contributor

Choose a reason for hiding this comment

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

please add loading state

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added (it wasn't there in the original implementation)

import { AccountDownloadDropdown } from '../AccountDownloadDropdown';

const mockMutate = vi.fn();
const mockRawData: Buffer | null = null;
Copy link
Contributor

Choose a reason for hiding this comment

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

type should be Uint8Array | undefined

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed

@askov askov force-pushed the feat/account-download-button branch from 24cb619 to f5f279b Compare March 24, 2026 07:38
@vercel
Copy link

vercel bot commented Mar 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
explorer Ready Ready Preview, Comment Mar 24, 2026 10:23am

Request Review

@askov
Copy link
Contributor Author

askov commented Mar 24, 2026

@greptile-apps review please

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants