Skip to content

feat: enable raw data for all account headers#884

Draft
askov wants to merge 12 commits intosolana-foundation:masterfrom
hoodieshq:refactor/extract-account-card-component
Draft

feat: enable raw data for all account headers#884
askov wants to merge 12 commits intosolana-foundation:masterfrom
hoodieshq:refactor/extract-account-card-component

Conversation

@askov
Copy link
Copy Markdown
Contributor

@askov askov commented Mar 18, 2026

Description

  • Extracts a reusable AccountCard component (@features/account) that encapsulates the card shell (header, Raw/Refresh buttons, raw account data view), eliminating duplicated card markup across 13 account section files
  • Every account detail card now gets a "Raw" toggle showing address, balance, owner, data size, executable flag, and hex-encoded account data

Type of change

  • New feature

Screenshots

localhost_3000_address_EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

Testing

Related Issues

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
Copy Markdown

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.

@askov askov marked this pull request as draft March 18, 2026 13:44
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 18, 2026

Greptile Summary

This PR extracts a reusable AccountCard component that encapsulates the card shell (header, Raw/Refresh buttons, raw account data table) and migrates 13 account-section files to use it. Every card now gets a "Raw" toggle that shows address, balance, owner, data size, executable flag, and hex-encoded account data via a new useRawAccountDataOnMount hook backed by useSWRImmutable.

Key changes:

  • New AccountCard / BaseAccountCard (app/features/account/ui/): thin compositional wrapper; RawAccountRows is only mounted when showRaw is true, so the network fetch is lazy and correctly gated on user interaction.
  • New BaseRawAccountRows: properly distinguishes three data states — in-flight spinner (isLoading=true), not-found ("Account data unavailable", rawData=undefined), and empty/populated data — addressing the concern raised in a prior review cycle.
  • Refactored shared/HexData: the existing common/HexData is deprecated to a re-export; the new implementation adds truncate, inverted, and align props plus pure utility functions (splitHexPairs, truncateHexPairs, formatHexSpans, groupHexRows) that are covered by unit tests.

One P1 concern: AccountDownloadDropdown was previously rendered in UnknownAccountCard and FungibleTokenMintAccountCard and has been removed during the migration without any replacement. The Raw inline view and the file-download capability are distinct features, so this is a functional regression rather than an intentional trade-off (the PR description does not mention retiring the download button).

Confidence Score: 4/5

Safe to merge once the AccountDownloadDropdown removal is confirmed intentional or reinstated.

One P1 finding: the download-to-disk feature previously available on UnknownAccountCard and FungibleTokenMintAccountCard has been silently dropped. All other changes are clean refactors with good test coverage; the prior loading-vs-not-found concern is now correctly resolved.

app/components/account/UnknownAccountCard.tsx and app/components/account/TokenAccountSection.tsx — both lost AccountDownloadDropdown without replacement.

Important Files Changed

Filename Overview
app/components/account/UnknownAccountCard.tsx Migrated to AccountCard, but AccountDownloadDropdown was removed without replacement — download-to-disk functionality is silently lost (P1).
app/components/account/TokenAccountSection.tsx FungibleTokenMintAccountCard migrated to AccountCard, but AccountDownloadDropdown removed without replacement — same download regression as UnknownAccountCard.
app/features/account/ui/AccountCard.tsx New thin wrapper composing BaseAccountCard with RawAccountRows; hook fires only on mount of RawAccountRows (when Raw is clicked), so no eager fetch occurs.
app/features/account/ui/BaseAccountCard.tsx New reusable card shell with Raw/Refresh buttons; showRaw state controls which content is rendered, correctly preventing RawAccountRows from mounting until Raw is clicked.
app/features/account/ui/BaseRawAccountRows.tsx Correctly distinguishes loading, not-found, empty, and data-present states.
app/entities/account/model/use-raw-account-data.ts Adds useRawAccountDataOnMount using useSWRImmutable; new Connection per call flagged in prior thread; returns correct undefined/Uint8Array distinction.
app/components/shared/HexData.tsx Expanded HexData with truncation, alignment, and inverted colour options; utility functions are well-tested.
app/components/common/HexData.tsx Correctly deprecated to a re-export from @shared/HexData for backward compatibility.

Reviews (2): Last reviewed commit: "fix: memoize conn to not recreate it mul..." | Re-trigger Greptile

Comment on lines +29 to +33
async function fetchRawAccountData([, url, address]: [string, string, string]): Promise<Uint8Array | null> {
const connection = new Connection(url, 'confirmed');
const info = await connection.getAccountInfo(new PublicKey(address));
return info ? Uint8Array.from(info.data) : null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 New Connection instantiated on every fetch

A brand-new Connection object is created inside the SWR fetcher on every invocation. While useSWRImmutable prevents redundant calls for the same cache key, this still creates an extra connection object every time the cache key changes (different cluster or account). The existing cluster providers already manage a shared Connection instance.

Consider consuming the Connection from the cluster context, or at minimum memoizing it at module level keyed by URL, rather than constructing one inside the fetcher.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed. Allowed to reuse the connection and not to recreate it

)}
</div>

<TableCardBody {...tableProps}>{showRaw ? rawContent : children}</TableCardBody>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 rawContent is always mounted despite showRaw being false at start

AccountCardBase passes rawContent as a prop to AccountCard which immediately evaluates the JSX expression <RawAccountRows account={account} /> and creates the React element. However, since rawContent is only rendered in the tree when showRaw is true (via the ternary), RawAccountRows itself isn't mounted until the button is clicked — so the hook fires correctly on demand.

This is fine as-is, but it's worth noting that passing rawContent as a render prop () => <RawAccountRows /> instead of a pre-evaluated element would make the lazy-mounting intent more explicit and guard against future accidental eager rendering.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not an issue

Comment on lines +17 to +23
export function useRawAccountDataOnMount(pubkey: PublicKey): Uint8Array | null {
const { url } = useCluster();

const { data } = useSWRImmutable(getRawAccountDataKey(url, pubkey), fetchRawAccountData);

return data ?? null;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Indistinguishable loading vs. not-found state

useRawAccountDataOnMount returns null for both the initial loading state (SWR data is undefined, and undefined ?? null evaluates to null) and the case where fetchRawAccountData itself returns null (i.e., getAccountInfo found no account on chain).

BaseRawAccountRows treats any falsy rawData as "still loading" and shows a spinning indicator indefinitely. If a user visits a closed or non-existent account and clicks "Raw", the spinner will never resolve — there is no terminal state for "account not found".

The fix is to remove the ?? null coercion from the return value so that SWR's initial undefined (in-flight) remains distinct from the null that signals "account not found". The BaseRawAccountRowsProps type already allows rawData to be undefined, so the rendering logic just needs a third branch:

  • undefined → loading spinner
  • null → "Account data unavailable" message
  • Uint8Array → render HexData

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Fixed

@rogaldh rogaldh force-pushed the refactor/extract-account-card-component branch from b41693c to 6d15487 Compare March 27, 2026 17:59
@vercel
Copy link
Copy Markdown

vercel bot commented Mar 28, 2026

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

Project Deployment Actions Updated (UTC)
explorer Ready Ready Preview, Comment Mar 28, 2026 0:25am

Request Review

@rogaldh
Copy link
Copy Markdown
Contributor

rogaldh commented Mar 28, 2026

@greptile-apps check again

Copy link
Copy Markdown
Contributor

@rogaldh rogaldh left a comment

Choose a reason for hiding this comment

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

LGTM

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.

2 participants