Skip to content

fix(sdk-provider-solana): preserve Solana RPC error details on TransactionError cause#387

Open
chybisov wants to merge 3 commits into
mainfrom
feature/emb-390-preserve-solana-rpc-error-details
Open

fix(sdk-provider-solana): preserve Solana RPC error details on TransactionError cause#387
chybisov wants to merge 3 commits into
mainfrom
feature/emb-390-preserve-solana-rpc-error-details

Conversation

@chybisov
Copy link
Copy Markdown
Member

@chybisov chybisov commented May 25, 2026

Summary

  • Introduce SolanaTransactionDetailsError, a minimal Error subclass exposing err and logs as own readonly properties — exported from the package root so consumers can instanceof and type-narrow.
  • Apply it uniformly at all three Solana throw sites that previously stringified the structured RPC result into a message and discarded the original payload.
  • Consumers can now read error.cause.err and error.cause.logs directly, one level deep — without re-simulating the transaction.

Credit

Big thanks to @samsamtrum for surfacing the issue in #385 and shipping the first-pass fix in #386 — this PR builds directly on that diagnosis. It supersedes #386 by flattening the cause shape and extending the same fix to two sibling sites.

Why

Closes #385.
Supersedes #386 — please close it after this lands.

The Solana provider has three RPC-driven failure paths. Each one used to stringify the structured Solana result into a message and lose the rest:

Site Before After
SolanaStandardWaitForTransactionTask preflight simulation cause was new Error(msg, { cause: { err, logs } }) (nested two levels) cause is SolanaTransactionDetailsError with .err and .logs
SolanaStandardWaitForTransactionTask post-send signatureResult.err no cause at all cause is SolanaTransactionDetailsError with .err
SolanaJitoWaitForTransactionTask bundle failedResult.err no cause, no bigint-safe JSON.stringify (latent bug if err contains bigints) cause is SolanaTransactionDetailsError with .err; bigint payloads now serialize safely

Bigint-safe JSON.stringify is now a single helper (safeStringifyBigInt), replacing three near-duplicate inline copies.

Public surface

  • TransactionError.code and message prefixes (Transaction simulation failed: …, Transaction failed: …) are preserved verbatim.
  • error.cause is strictly more informative; was a wrapper Error or undefined before.
  • SolanaTransactionDetailsError is exported from @lifi/sdk-provider-solana for typed consumer access:
import { SolanaTransactionDetailsError } from '@lifi/sdk-provider-solana'

try {
  await sdk.executeRoute(route)
} catch (error) {
  if (error.cause instanceof SolanaTransactionDetailsError) {
    console.log(error.cause.err)   // structured Solana error
    console.log(error.cause.logs)  // program execution logs (preflight only)
  }
}

Test plan

  • pnpm check (biome) — clean
  • pnpm check:types — clean (all 6 packages)
  • pnpm check:circular-deps — clean
  • pnpm knip:check — clean
  • pnpm --filter @lifi/sdk-provider-solana test:unit — 49 passed (16 new)
  • pnpm test:unit (monorepo) — all packages green
  • pnpm build (monorepo) — all packages built; SolanaTransactionDetailsError appears in dist/esm/index.d.ts

New unit tests:

  • solanaErrorCause.unit.spec.tssafeStringifyBigInt + SolanaTransactionDetailsError behaviour
  • SolanaStandardWaitForTransactionTask.unit.spec.ts — simulation failure, post-send failure, bigint payload safety
  • SolanaJitoWaitForTransactionTask.unit.spec.ts — bundle failure, bigint regression coverage

Linear

EMB-390

chybisov and others added 3 commits May 25, 2026 16:32
…ctionError cause

Three throw sites in the Solana provider stringified the structured Solana
result into a message and discarded the original payload — notably the
`logs` array, the most actionable field for diagnosing program failures.

Introduces SolanaTransactionDetailsError, a minimal Error subclass that
exposes `err` and `logs` as own readonly properties, and applies it
uniformly at:

- SolanaStandardWaitForTransactionTask preflight simulation (was nesting
  details two levels deep via Error(msg, { cause: { err, logs } }))
- SolanaStandardWaitForTransactionTask post-send signatureResult.err
  (previously dropped on the floor)
- SolanaJitoWaitForTransactionTask bundle failedResult.err (previously
  dropped on the floor and missing bigint-safe JSON.stringify)

Consumers can now read `error.cause.err` and `error.cause.logs` directly,
one level deep. Public surface (TransactionError code and message
prefixes) is unchanged.

Closes #385
Supersedes #386
…ackage root

Let consumers narrow `error.cause` with `instanceof
SolanaTransactionDetailsError` for typed access to `err` and `logs`,
without reaching into a deep import path.
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.

SolanaStepExecutor discards simulation details on TransactionSimulationFailed (code 1019)

2 participants