Skip to content

Instantly share code, notes, and snippets.

@thlorenz
Created August 28, 2025 08:52
Show Gist options
  • Save thlorenz/811a613eb63b646b6c135d6d2b59b62f to your computer and use it in GitHub Desktop.
Save thlorenz/811a613eb63b646b6c135d6d2b59b62f to your computer and use it in GitHub Desktop.
Analyzing Solana Feepayer enforcement on Transaction Sanitization
[package]
name = "feepayer"
version = "0.1.0"
edition = "2024"
[dependencies]
solana-instruction = "3.0.0"
solana-pubkey = "3.0.0"
solana-sanitize = "3.0.1"
solana-transaction = { version = "3.0.1", features = ["blake3"] }
use std::collections::HashSet;
use solana_sanitize::Sanitize;
use solana_instruction::{AccountMeta, Instruction};
use solana_pubkey::Pubkey;
use solana_transaction::{sanitized::SanitizedTransaction, Transaction};
fn sanitize_ixs(ixs: &[Instruction], payer: Option<Pubkey>, label: &str) -> Transaction {
let tx = Transaction::new_with_payer(ixs, payer.as_ref());
let res = tx.sanitize();
eprintln!("{label:18}: {res:?}");
tx
}
fn main() {
let program = Pubkey::default();
let payer = Pubkey::new_unique();
// No accounts
let ix = Instruction::new_with_bytes(program, &[], vec![]);
sanitize_ixs(&[ix], None, "Empty Accounts");
// Readonly Payer
let payer_meta = AccountMeta::new_readonly(payer, false);
let ix = Instruction::new_with_bytes(program, &[], vec![payer_meta]);
sanitize_ixs(&[ix], None, "Readonly");
// Readonly signing payer
let payer_meta = AccountMeta::new_readonly(payer, true);
let ix = Instruction::new_with_bytes(program, &[], vec![payer_meta]);
sanitize_ixs(&[ix], None, "Readonly signing");
// Writable Payer
let payer_meta = AccountMeta::new(payer, false);
let ix = Instruction::new_with_bytes(program, &[], vec![payer_meta]);
sanitize_ixs(&[ix], None, "Writable");
// Writable signing payer
let payer_meta = AccountMeta::new(payer, true);
let ix = Instruction::new_with_bytes(program, &[], vec![payer_meta]);
let tx = sanitize_ixs(&[ix], None, "Writable signing");
let sanitized = SanitizedTransaction::try_from_legacy_transaction(tx.clone(), &HashSet::new());
eprintln!(" : {sanitized:#?}");
// Explicit payer
let ix = Instruction::new_with_bytes(program, &[], vec![]);
let tx = sanitize_ixs(&[ix], Some(payer), "Explicit payer");
let sanitized = SanitizedTransaction::try_from_legacy_transaction(tx.clone(), &HashSet::new());
eprintln!(" : {sanitized:#?}");
// Explicit payer used as readonly in ix
let payer_meta = AccountMeta::new_readonly(payer, false);
let ix = Instruction::new_with_bytes(program, &[], vec![payer_meta]);
let tx = sanitize_ixs(
&[ix],
Some(payer),
"Explicit payer included as readonly not signing",
);
let sanitized = SanitizedTransaction::try_from_legacy_transaction(tx.clone(), &HashSet::new());
eprintln!(" : {sanitized:#?}");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment