Skip to content

poc: decode Drift protocol hack transaction#241

Draft
prasanna-anchorage wants to merge 5 commits intomainfrom
drift-hack-poc
Draft

poc: decode Drift protocol hack transaction#241
prasanna-anchorage wants to merge 5 commits intomainfrom
drift-hack-poc

Conversation

@prasanna-anchorage
Copy link
Copy Markdown
Contributor

@prasanna-anchorage prasanna-anchorage commented Apr 8, 2026

Summary

Stacks Squads Multisig (#240) + Drift (#239) presets to fully decode the April 1 2026 Drift Protocol exploit transaction.

Depends on:

What the parser reveals

The $285M Drift exploit used two pre-signed durable nonce transactions. The parser fully decodes both:

Transaction 1 — Create + Approve (signer 1)
https://solscan.io/tx/2HvMSgDEfKhNryYZKhjowrBY55rUx5MWtcWkG9hqxZCFBaTiahPwfynP1dxBSRk9s5UTVc8LFeS4Btvkm9pc2C4H

1. AdvanceNonceAccount          — activates durable nonce
2. vaultTransactionCreate       — Squads multisig wrapping inner Drift: updateAdmin
   └─ Inner: Drift updateAdmin  — Current admin: AiLGd...  →  New admin: H7PiG...
3. proposalCreate               — creates governance proposal (transactionIndex: 7)
4. proposalApprove              — first signer approves

Transaction 2 — Approve + Execute (signer 2)
https://solscan.io/tx/4BKBmAJn6TdsENij7CsVbyMVLJU1tX27nfrMM1zgKv1bs2KJy6Am2NqdA3nJm4g9C6eC64UAf5sNs974ygB9RsN1

1. AdvanceNonceAccount          — activates durable nonce
2. proposalApprove              — second signer approves (reaches 2-of-5 threshold)
3. vaultTransactionExecute      — executes the stored vault transaction

Defense point

The critical signing moment is Transaction 1. The parser decodes the nested vaultTransactionCreate and reveals Drift: updateAdmin with the new admin address — a signer reviewing this in a wallet would see the admin transfer before approving.

By Transaction 2, the inner instructions are already stored on-chain (not in the tx bytes), so vaultTransactionExecute only shows a reference to the vault transaction account. The defense must happen at Tx 1.

Key fixes discovered during this POC

  • VaultTransactionMessage::deserialize was reading instruction data_len as u8 instead of u16 LE, causing a 1-byte offset that made inner instruction discriminators unmatchable
  • Disambiguated "Current admin" vs "New admin" labels when IDL account and arg names collide

Test plan

  • All workspace tests pass
  • parser_cli correctly decodes both exploit transactions
  • Inner updateAdmin shows current and new admin addresses

🤖 Generated with Claude Code

prasanna-anchorage and others added 5 commits April 7, 2026 22:58
Embed the Squads v4 Anchor IDL and decode instructions using
parse_instruction_with_idl so transactions show named instructions
(vaultTransactionCreate, proposalCreate, proposalApprove, etc.),
named accounts, and decoded args instead of raw hex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Deserialize the VaultTransactionMessage from the transactionMessage
bytes field using Solana's compact wire format, reconstruct full
Instructions, and pass them through the existing visualizer framework
so inner program calls (System, Drift, Token, etc.) are displayed
with their decoded names and accounts instead of raw hex.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Fix instruction data_len field in VaultTransactionMessage::deserialize
to read as u16 LE instead of u8, matching the Squads v4 on-chain wire
format. The previous u8 read consumed one too few bytes, shifting
instruction data by one byte and causing inner instruction discriminator
mismatches (e.g. Drift updateAdmin showed as "Unknown Instruction").

Also cache the parsed Squads IDL with LazyLock to avoid re-parsing 86KB
JSON on every instruction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add IDL-based visualizer for Drift Protocol v2
(dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH) with 249 instructions.
Uses LazyLock to cache parsed IDL. Includes unit tests for IDL loading,
discriminator validation, and error handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an IDL instruction has both an account and an argument with the
same name (e.g. "admin" in Drift's updateAdmin), label them as
"Current admin" (account) and "New admin" (argument) in the expanded
view to avoid confusion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.

1 participant