SolDiff.Try Playground
Security infrastructure

Meet! SolDiff.
Built for a secure Solana future.

Decompile BPF bytecode and surface risk-graded diffs in seconds.

Get startedLearn more
Playground · Live demo

Run a real mainnet upgrade diff.

Pick a program below to load a pre-computed report for a genuine on-chain upgrade. Every finding here was produced by the rule engine, not hand-written.

soldiff playground
mainnet · demo mode5 findings loaded
97Risk
Drift ProtocolCritical
dRiftyHA39MWEi33UH·292,000,000294,500,000
C2
H1
L1
I3
Instructions changed3
Accounts affected7
New CPI targets1
How it works

From upgrade signatures to
evidence-backed bytecode diffs.

Built around trustworthy historical reconstruction first. Advanced semantic claims stay out of scope until they can be proven.

01

Parse upgrade transactions

SolDiff reads two BPF Upgradeable Loader Upgrade transactions, extracts buffer / Program / ProgramData addresses, and validates Version A is older than Version B.

parseUpgradeTransaction(sigA|sigB)
02

Reconstruct historical ELFs

Buffer Write instructions are collected, ordered deterministically, isolated to a deployment cycle, coverage-checked, and assembled into validated ELF64 artifacts with full SHA-256 identity.

Write replay → coverage map → validateElf()
03

Raw byte + SBF instruction diff

`.text` and `.rodata` are compared at the byte-chunk layer. An SBF instruction-level decoder adds sequence-aligned instruction evidence. This is not a semantic decompiler.

raw-byte diff + soldiff-ebpf-isa / optional sbpf|llvm-objdump
04

Optional Anchor IDL diff

When an IDL can be historically matched, instruction/account/discriminator changes are reported with evidence. Otherwise historical IDL is marked unavailable — never silently unchanged.

normalizeIdl → compareNormalizedIdls
05

Evidence-backed findings

Heuristic findings attach analyzer, confidence, and evidence. Overstated claims (e.g. treating sampled pubkeys as CPI targets) are avoided.

Finding{ analyzer, code, severity, confidence, evidence }
06

Reproducible case study

CLI/scripts emit manifest.json + report.md with hashes and provenance. Reports are ephemeral in the UI today — persistence/shareable URLs are not shipped yet.

bun run case-study --program … --from … --to …
Rule catalog (mixed live + aspirational)

Ten rules that catch
real exploits.

Every rule is derived from a real Solana incident. The engine targets a zero false-negative rate on CRITICAL findings — if it fires, treat the upgrade as dangerous until proven otherwise.

REMOVED_SIGNER_CHECKCRITICAL

A required signer constraint was removed from an instruction. Any caller can now invoke it without authorization.

e.g. liquidate_perp: #[account(signer)] → #[account]
NEW_INVOKE_SIGNED_TARGETCRITICAL

A new CPI target was added inside invoke_signed. A program PDA authority is now used to CPI to an unverified program.

e.g. place_order: new CPI → xYz3...kP9m
CHANGED_AUTHORITY_FIELDCRITICAL

An account marked as authority or admin has changed its ownership or address constraint.

e.g. config.upgrade_authority: old_key → new_key
DISCRIMINATOR_CHANGEHIGH

An instruction's 8-byte discriminator was modified — a breaking change that can silently break all existing clients.

e.g. withdraw: discriminator [0x4a..] → [0x9f..]
NEW_MUTABLE_ACCOUNTHIGH

An account that was previously read-only is now marked as writable. Enables unexpected balance mutations.

e.g. settle_pnl: insurance_fund_vault → #[account(mut)]
REMOVED_OWNER_CHECKHIGH

An owner validation (constraint on account.owner) was removed, enabling account substitution attacks.

e.g. deposit: owner = token_program removed
ADDED_CLOSE_ACCOUNTMEDIUM

A new close_account CPI was added. Could be used to drain PDA lamports to an attacker-controlled destination.

e.g. admin_withdraw: new close_account(escrow)
CHANGED_SEEDSMEDIUM

PDA derivation seeds changed. The PDA address changes — existing records become inaccessible, potential account substitution.

e.g. deposit: seeds ["v1", user] → ["v2", user]
NEW_32_BYTE_PUBLIC_KEY_CANDIDATELOW

Live pipeline: a sampled 32-byte window decodes as a public key. Hypothesis only — not a proven CPI target.

e.g. sampled bytes → candidate pubkey (confidence=low)
LARGE_TEXT_REGION_CHANGEDINFO

Live pipeline: many aligned .text chunks differ. Measures raw churn, not proven semantic logic change.

e.g. .text: 48 chunks differ
Community rule contributions
New rule patterns can be submitted as a PR. Rules are versioned, tested, and reviewed before merge.
Integrations

Fits into your existing workflow.

Web UI, CLI, TypeScript SDK, or GitHub Actions. One engine, every entry point.

CLInpx soldiff

Run in any terminal. Diff by slot, upgrade transaction, or program ID. Outputs JSON for CI pipelines.

$ npx soldiff JUP4...GuJB \
    --from-slot 280000000 \
    --to-slot 284500000 \
    --output report.html
Squads v4 SDK@soldiff/sdk

Generate a diff report before submitting an upgrade transaction. Attach the shareable URL to the multisig proposal.

import { generateDiffReport } from "@soldiff/sdk";

const report = await generateDiffReport({
  programId: new PublicKey("JUP4...GuJB"),
  fromSlot: 280_000_000,
  toSlot: 284_500_000,
});
console.log(report.shareableUrl);
GitHub Actionssoldiff-action

Monitor programs automatically. Get Slack or webhook alerts the moment any watched program is upgraded.

- uses: MisbahAnsar/soldiff-action@v1
  with:
    program-ids: |
      JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB
    rpc-url: ${{ secrets.HELIUS_RPC_URL }}
    alert-on-severity: HIGH
    notify-slack: ${{ secrets.SLACK_WEBHOOK }}
Governance ready

Immutable, content-addressed reports.

SolDiff reports are hashed by SHA-256 and permanently hosted. Multisig members and DAO voters can attach a SolDiff URL to any upgrade proposal on Realms or Squads — if the URL still loads, it is the same report that was reviewed.

Realms / SPL GovernanceSquads v4Serum MultisigMean Finance DAO
SHA-256
Content-addressed