Skip to content

refactor: Decompose simulation, add tests#928

Open
askov wants to merge 7 commits intosolana-foundation:masterfrom
hoodieshq:refactor/decompose-simulation-extract-cu-entity
Open

refactor: Decompose simulation, add tests#928
askov wants to merge 7 commits intosolana-foundation:masterfrom
hoodieshq:refactor/decompose-simulation-extract-cu-entity

Conversation

@askov
Copy link
Copy Markdown
Contributor

@askov askov commented Apr 8, 2026

Description

  • Extract compute-unit FSD entity — moves CU schedule logic, default CU values, instruction log formatting, and the CUProfilingCard component out of utils/ and features/cu-profiling/ into
    entities/compute-unit/, with a clean public API via barrel export
  • Decompose useSimulation hook — breaks the monolithic 300+ line hook into focused, pure library functions (simulateTransaction, buildTokenBalances, computeSolBalanceChanges, getMintDecimals,
    resolveAddressLookupTables) that are independently testable and easier to reason about
  • Add comprehensive test coverage — new test suites for all extracted lib functions plus the useSimulation hook and SolBalanceChangesCard Storybook stories

Type of change

  • Other (please describe): Refactoring + tests

Testing

Regression targets

Related Issues

Closes HOO-410

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

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 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
Copy Markdown
Contributor

greptile-apps bot commented Apr 8, 2026

Greptile Summary

This PR decomposes the monolithic useSimulation hook (300+ lines) into focused, independently-testable library functions (simulateTransaction, buildTokenBalances, computeSolBalanceChanges, getMintDecimals, resolveAddressLookupTables), migrates CU-profiling logic into a new entities/compute-unit/ FSD entity, and adds comprehensive test coverage across all extracted units. Both previously-flagged issues (ALT accounts silently dropped from balance tracking, fingerprint cache key not including ALT configuration) are correctly resolved.

Confidence Score: 5/5

This PR is safe to merge — all previously-flagged correctness issues are resolved and no new P0/P1 issues were found.

Both prior blocking concerns (ALT-resolved accounts silently dropped, cache key missing ALT configuration) are definitively fixed. The new resolveAddressLookupTables also silently fixed a key-index alignment bug from the old filter-then-map pattern, and the test suite is comprehensive across all extracted units. No remaining findings rise above P2.

No files require special attention.

Vulnerabilities

No security concerns identified. The refactoring does not alter authentication boundaries, and all external data (RPC responses, base64-encoded account buffers) is validated before use (size checks, program-owner checks). Token amounts are handled via bigint to avoid precision loss.

Important Files Changed

Filename Overview
app/features/instruction-simulation/lib/simulate-transaction.ts New pure async function orchestrating RPC calls and result interpretation; now correctly uses keySegments().flat() to include ALT-resolved accounts, fixing the previously-flagged silent data loss.
app/features/instruction-simulation/model/use-simulation.ts Refactored to use useSWRMutation; fingerprint now serializes the full message (fixing the ALT cache-key collision); clean discriminated-union state type replaces scattered useState calls.
app/features/instruction-simulation/lib/build-token-balances.ts Extracted token balance builder; uses readU64LE returning bigint instead of Number(...), fixing precision loss for amounts > Number.MAX_SAFE_INTEGER. Token-2022 discriminator byte correctly handled.
app/features/instruction-simulation/lib/resolve-address-lookup-tables.ts New helper; also fixes a subtle index-alignment bug from the old code where filtering null accounts before mapping caused key mismatches for missing ALTs.
app/features/instruction-simulation/ui/SimulationCard.tsx Cleanly decomposed into distinct status branches using SimulationCardShell; introduces generateTokenBalanceRows at the UI layer where it belongs. Token balance and SOL balance cards are correctly gated on succeeded.
app/features/instruction-simulation/lib/tests/simulate-transaction.spec.ts 456-line test suite covering happy path, error branches, ALT resolution, token balance parsing, and large-amount precision.
app/entities/compute-unit/index.ts New barrel export for the compute-unit FSD entity; clean public API surface.
app/features/instruction-simulation/lib/get-mint-decimals.ts Correctly distinguishes mint vs token-account buffers for both base spl-token and Token-2022 extended accounts using the type discriminator at offset 165.

Reviews (3): Last reviewed commit: "fix: show simulation error when RPC retu..." | Re-trigger Greptile

Comment thread app/features/instruction-simulation/lib/simulate-transaction.ts Outdated
Comment thread app/features/instruction-simulation/model/use-simulation.ts Outdated
@askov
Copy link
Copy Markdown
Contributor Author

askov commented Apr 8, 2026

@greptile-apps please review

@vercel
Copy link
Copy Markdown

vercel bot commented Apr 8, 2026

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

Project Deployment Actions Updated (UTC)
explorer Ready Ready Preview, Comment Apr 13, 2026 9:11am

Request Review

@askov
Copy link
Copy Markdown
Contributor Author

askov commented Apr 8, 2026

@greptile-apps please review


const KEY_A = new PublicKey('5ZiE3vAkrdXBgyFL7KqG3RoEGBws8CjY8AsGq1MuR5My');
const KEY_B = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
const KEY_C = new PublicKey('So11111111111111111111111111111111111111112');
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.

nit: I'd prefer using Keypair.generate().pubkey here to create a key if we just need a key. Otherwise, it seems that we need a specific key

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced all the hardcoded keys where possible

const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';

/** Real ALT account data from mainnet. Addresses: ADDR_3ZK, ADDR_SQDS, ADDR_BGUMA */
const ALT_FIXTURE_3_ADDRS =
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.

I'd move these into app/fixtures directory as they are for real ALTs

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done


import { MINT_SIZE, TOKEN_ACCOUNT_SIZE } from '../lib/token-layout';

export const TOKEN_PROGRAM = 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA';
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.

For token programs, it is better to use solana-program/token & token2022

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced

export const SYSTEM_PROGRAM = '11111111111111111111111111111111';

export const USDC_MINT = new PublicKey('EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v');
export const WSOL_MINT = new PublicKey('So11111111111111111111111111111111111111112');
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.

WSOL is present at the project as NATIVE_MINT. USDC_MINT might be moved to app/__fixtures or __mocks

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Replaced NATIVE_MINT and moved USDC_MINT to shared, as it's not a fixture, and we have several of them hardcoded in different files.


/** String comparison for RPC responses where owner is a base58 string, not a PublicKey */
export function isTokenProgramBase58(owner: string): boolean {
return owner === TOKEN_PROGRAM_BASE58 || owner === TOKEN_2022_PROGRAM_BASE58;
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.

We have a similar helper at app/shared/model/token-program.ts

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Moved the helper to the shared model

export const MIN_MINT_ACCOUNT_BUFFER_LENGTH = MINT_SIZE;

/** Base SPL token account size (bytes) */
export const TOKEN_ACCOUNT_SIZE = 165;
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.

@solana-program/token client has methods to get sizes for mint size and TA size

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks, replaced

* Token-2022 account type discriminator values.
* The discriminator byte sits at offset `TOKEN_ACCOUNT_SIZE` (165).
*/
export const ACCOUNT_TYPE_MINT = 1;
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.

solana-program/token also has account types in it

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.

I mean that we could reuse already existing logic within the client to make better refactoring

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Imported everything I could find

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.

Except these ^ LGTM

import type { SimulationResult } from '../../lib/simulate-transaction';
import type { SimulationState } from '../use-simulation';

const MOCK_URL = 'https://api.devnet.solana.com';
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.

Let's use 'https://devnet.rpc.address', maybe, to distinguish it from a real address unless the types require it to look like a devnet address

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

expect(typeof getSimulate(result.current)).toBe('function');
});

it('should transition to done with result on success', async () => {
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.

I may be missing something, but it looks like we need to construct some real transaction data (Message), which would have tolen and sol changes to test that buffer layouts work

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is already covered by unit tests

@askov askov force-pushed the refactor/decompose-simulation-extract-cu-entity branch from eab0de7 to c86a616 Compare April 13, 2026 07:12
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