Rust + Anchor + Quasar: the standard Solana program stack in 2026
Solana programs are Rust. Anchor handles boilerplate. Quasar is the in-test cluster harness. Here's the canonical workflow from cargo new to mainnet deploy.
New Solana program authors invariably converge on the same stack within a week: Rust for the program, Anchor for the ergonomic layer, and a fast test harness for the integration tests that would otherwise be unbearable against solana-test-validator. The harness used to be Mocha + solana-test-validator; increasingly it's LiteSVM or Quasar.
This article is the opinionated path through the standard stack — cargo new through mainnet deploy.
The shape of a project
my-program/
├── Anchor.toml # programs, cluster, wallet, scripts
├── Cargo.toml # workspace
├── programs/
│ └── my-program/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs # entrypoint + #[program] module
│ ├── instructions/
│ │ ├── mod.rs
│ │ ├── initialize.rs
│ │ └── transfer.rs
│ ├── state/
│ │ ├── mod.rs
│ │ └── vault.rs
│ └── errors.rs
├── tests/
│ └── my-program.ts # Mocha/Anchor TS tests
├── target/
│ ├── deploy/
│ │ ├── my_program.so # compiled BPF binary
│ │ └── my_program-keypair.json
│ └── idl/
│ └── my_program.json # generated IDLThe minimum Anchor program
use anchor_lang::prelude::*;
declare_id!("My111111111111111111111111111111111111111");
#[program]
pub mod my_program {
use super::*;
pub fn initialize(ctx: Context<Initialize>, name: String) -> Result<()> {
let vault = &mut ctx.accounts.vault;
vault.owner = ctx.accounts.payer.key();
vault.name = name;
vault.balance = 0;
Ok(())
}
pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()> {
ctx.accounts.vault.balance = ctx
.accounts.vault.balance
.checked_add(amount)
.ok_or(MyError::Overflow)?;
Ok(())
}
}
#[derive(Accounts)]
#[instruction(name: String)]
pub struct Initialize<'info> {
#[account(init, payer = payer, space = 8 + Vault::INIT_SPACE,
seeds = [b"vault", payer.key().as_ref(), name.as_bytes()], bump)]
pub vault: Account<'info, Vault>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct Deposit<'info> {
#[account(mut, has_one = owner)]
pub vault: Account<'info, Vault>,
pub owner: Signer<'info>,
}
#[account]
#[derive(InitSpace)]
pub struct Vault {
pub owner: Pubkey,
#[max_len(32)]
pub name: String,
pub balance: u64,
}
#[error_code]
pub enum MyError {
#[msg("arithmetic overflow")]
Overflow,
}Anchor generates: the discriminator-based dispatcher, account validation (has_one, seeds, init rent payment), serialization, the IDL JSON, and the TypeScript client.
Building
anchor build
# Compiles programs/* to target/deploy/*.so + emits target/idl/*.json
anchor idl init -f target/idl/my_program.json <program_id>
# (Optional) writes the IDL to the on-chain IDL account so explorers can decode txs
anchor test
# Spins up solana-test-validator, deploys the program, runs tests/*.ts against itTests — the slow vs fast options
Default (Anchor): Mocha + solana-test-validator. Each anchor test takes ~10-30s to spin up the validator. Fine for a couple of tests, painful at 100+.
Fast (LiteSVM): in-process SVM, no validator. Same instruction dispatch, microsecond-fast. The default for unit and integration tests in 2026:
import { LiteSVM } from "litesvm"
import { PublicKey } from "@solana/web3.js"
const svm = new LiteSVM()
svm.addProgramFromFile(programId, "target/deploy/my_program.so")
svm.airdrop(payer.publicKey, 1_000_000_000n)
const ix = /* build your instruction */
const result = svm.sendTransaction(new Transaction().add(ix).sign(payer))
expect(result.err).toBeNull()
const vault = svm.getAccount(vaultPda)
// assertions on vault.data ...Mainnet-realistic (Surfpool): when you need real Mainnet state in the test (real USDC mint, real oracle accounts), use Surfpool with a fork. Slower than LiteSVM, far more realistic than vanilla solana-test-validator. See the Surfpool article.
Deploying to devnet → mainnet
# Switch CLI to devnet, deploy
solana config set --url https://api.devnet.solana.com
anchor deploy --provider.cluster devnet
# Output: program deployed at <program_id>
# Run your tests against the devnet deploy
anchor test --skip-deploy --provider.cluster devnet
# Once happy, switch to mainnet
solana config set --url https://api.mainnet-beta.solana.com
# Pre-fund the deploy wallet (~3 SOL for a moderate program)
solana airdrop 3 # only works on devnet; transfer SOL in for mainnet
anchor deploy --provider.cluster mainnet
# Or for explicit upgrade authority:
solana program deploy target/deploy/my_program.so \
--program-id target/deploy/my_program-keypair.json \
--upgrade-authority <your-multisig-or-key>Upgrade authority — set it carefully
First-time deploys default to making the deploy wallet the upgrade authority. Production programs should hand this off immediately:
# Transfer upgrade authority to a Squads multisig
solana program set-upgrade-authority <program_id> \
--new-upgrade-authority <squads_vault_pda> \
--skip-new-upgrade-authority-signer-check
# Or freeze the program (irrevocable!)
solana program set-upgrade-authority <program_id> --finalSee the Squads article for the recommended pattern.
Where Quasar fits
quasar in this stack name historically refers to rapid in-memory cluster harnesses for testing — the lineage runs through bankrun → LiteSVM → newer derivatives. The constant is: skip solana-test-validator for anything past basic smoke tests; use an in-process SVM for speed.
The opinionated stack, summarised
Language: Rust (stable channel, edition 2021)
Framework: Anchor 0.31+ (or Pinocchio for hot paths)
Local tests: LiteSVM for fast, Surfpool for Mainnet-realistic
Devnet tests: anchor test --provider.cluster devnet
TS client: codama-generated against @solana/kit
Frontend: gill (Next.js / React) or kit-squared (SvelteKit)
Deploy auth: Squads V4 multisig
CI: GitHub Actions → cargo test + anchor test (LiteSVM)References
Rust + Anchor + a fast SVM harness is the path 90% of Solana program authors land on. Layer in Pinocchio for hot paths, Squads for upgrade authority, and the stack scales to production.