Skip to content

Native deploy, upgrade, and Squads multisig integration#68

Open
L0STE wants to merge 38 commits intomasterfrom
feat/multisig-deploy
Open

Native deploy, upgrade, and Squads multisig integration#68
L0STE wants to merge 38 commits intomasterfrom
feat/multisig-deploy

Conversation

@L0STE
Copy link
Contributor

@L0STE L0STE commented Mar 22, 2026

Native deploy pipeline

Previously quasar deploy shelled out to solana program deploy. We had no control over transaction construction, couldn't set priority fees, and got the Solana CLI's error messages instead of our own. This branch rebuilds the entire deploy path in native Rust — we encode BPF Loader Upgradeable instructions directly, manage buffer uploads ourselves, and talk to the RPC via ureq.

Every transaction in the pipeline now carries a SetComputeUnitPrice instruction. The CLI queries getRecentPrioritizationFees for a median and applies it automatically, or you can override with --priority-fee.

Buffer uploads chunk at 950 bytes per transaction with a progress bar, retry failed chunks up to 3 times with fresh blockhashes, and confirm every transaction via getSignatureStatuses polling. No fire-and-forget.

The deploy command validates state before doing work:

  • Won't deploy if the program already exists on-chain (tells you to use --upgrade)
  • Won't upgrade if the program doesn't exist (tells you to drop --upgrade)
  • Verifies your keypair matches the on-chain upgrade authority before uploading a buffer

No solana CLI subprocess calls remain in the deploy or multisig code paths.

Squads v4 multisig integration

New --multisig <address> flag on quasar deploy for teams that manage program authority through a Squads multisig.

quasar deploy --multisig <addr> — Fresh deploy, then transfers upgrade authority to the multisig vault.

quasar deploy --upgrade --multisig <addr> — Uploads a buffer, transfers buffer authority to the vault, then submits a Squads proposal (VaultTransactionCreate + ProposalCreate + ProposalApprove) in a single transaction.

quasar deploy --upgrade --multisig <addr> --status — Fetches the latest proposal, shows each member's vote with colored indicators, and prompts to execute if threshold is met.

The Squads integration parses on-chain account layouts directly (multisig state, proposals, vault transactions) rather than depending on an SDK. Anchor discriminators are computed at runtime. The inner upgrade instruction is encoded as a Squads TransactionMessage matching their SmallVec wire format.

CLI reference

quasar deploy

Build and deploy a fresh program. Fails if the program keypair already exists on-chain.

quasar deploy [OPTIONS]

quasar deploy --upgrade

Upgrade an existing program. Verifies the on-chain upgrade authority matches your keypair before uploading the buffer.

quasar deploy --upgrade [OPTIONS]

quasar deploy --upgrade --multisig <ADDRESS>

Propose a program upgrade through a Squads v4 multisig. Uploads the buffer, transfers its authority to the multisig vault, then creates a vault transaction + proposal and casts the first approval vote.

quasar deploy --upgrade --multisig <ADDRESS> [OPTIONS]

quasar deploy --upgrade --multisig <ADDRESS> --status

Show the approval status of the latest multisig proposal. Displays each voting member with a green checkmark or dim dot, the threshold progress, and prompts to execute if enough signatures have been collected.

quasar deploy --upgrade --multisig <ADDRESS> --status [OPTIONS]

Shared options

Flag Description
--program-keypair <PATH> Path to the program keypair. Defaults to target/deploy/<name>-keypair.json
--upgrade-authority <PATH> Upgrade authority keypair. Defaults to the payer keypair
-k, --keypair <PATH> Payer keypair. Defaults to the Solana CLI configured keypair
-u, --url <URL> RPC URL or cluster name (devnet, testnet, localnet, mainnet-beta). Defaults to Solana CLI config
--skip-build Skip the build step, use the existing .so
--priority-fee <MICRO_LAMPORTS> Override the auto-calculated priority fee

New modules

Module Responsibility
rpc.rs JSON-RPC client, Solana CLI config resolution, Keypair wrapper over ed25519-dalek, transaction confirmation
bpf_loader.rs BPF Loader instruction encoding (all 5 variants), ComputeBudget encoding, deploy/upgrade/buffer orchestrators
multisig.rs Squads v4 PDAs, instruction builders, account parsers, proposal + execute orchestrators, status display

L0STE added 30 commits March 21, 2026 17:16
agave-install needs a full version (e.g. 2.1.21), not just a
major.minor prefix. Comparison still uses major.minor matching.
- `quasar keys list` prints program ID from keypair file
- `quasar keys sync` updates declare_id!() to match keypair
- `quasar keys new` generates keypair + syncs (--force to overwrite)
- `quasar clean` now preserves keypair files in target/deploy/

Uses the IDL parser's syn-based extraction for declare_id!().
Closes #64
- Validate program keypair is exactly 64 bytes before slicing in deploy.rs
- Add RPC error field checks to get_latest_blockhash and get_account_data
- Make Keypair inner SigningKey private (was unnecessarily pub)
Critical:
- Transfer buffer authority to vault PDA after write-buffer so Squads
  can actually execute the upgrade

Important:
- Extract resolve_program_keypair() to deduplicate keypair path logic
- Move program ID extraction to multisig::read_program_id_from_keypair()
  making deploy.rs a thin dispatch for the multisig path
- Expand ~ in read_config_field() results via expand_tilde()
- read_program_id_from_keypair() checks file existence before reading

Minor:
- Hoist AccountMeta import to module level (was repeated in 3 functions)
- Use checked_add for transaction index increment
- Remove tautological bump <= 255 assertion (u8 is always <= 255)
- Add tilde_expansion test
quasar deploy --multisig <ADDR> --status now fetches and displays:
- Proposal status (Active/Approved/Executed/etc) with colored labels
- Each voting member's approval state (green check or dim dot)
- Threshold progress ("2/3 signed — awaiting 1 signature")
- If threshold met, interactive prompt to execute the transaction

Addresses shown in short format (1234...5678). Colored output
throughout using the existing style module.

Also adds: parse_multisig_account, parse_proposal_account,
vault_transaction_execute_ix, and 5 new tests.
…ct/multisig)

- quasar deploy: initial deploy, errors if program exists on-chain
- quasar deploy --multisig: deploy + transfer authority to vault
- quasar deploy --upgrade: direct upgrade
- quasar deploy --upgrade --multisig: propose through Squads

Extracts parse_multisig_address, build_and_find_so, solana_deploy
helpers. Makes short_addr public for cross-module use.
…ly, fix clippy

- Fix critical VaultTransaction deserialization: creator is Pubkey (32 bytes)
  not 8, vault_bump field was missing, ephemeral_signer_bumps is Vec<u8> not
  u8. vault_index now correctly read at offset 81, message offset accounts
  for variable-length ephemeral bumps vec.
- Resolve cluster URL (localnet→http://localhost:8899 etc.) before passing
  to solana CLI in all deploy paths, not just multisig paths.
- Refactor deploy::run() from 8 positional params to DeployOpts struct
  to satisfy clippy::too_many_arguments.
- Collapse nested if into single condition (clippy::collapsible_if).
- Add 2 tests for VaultTransaction execute IX parsing (with and without
  ephemeral signer bumps).
L0STE added 8 commits March 22, 2026 11:15
Move RPC helpers, Keypair struct, and Solana CLI config functions into a
dedicated rpc module shared by both multisig.rs and deploy.rs. Add
Keypair::generate() for programmatic keypair creation.
…r returns

- All ureq RPC calls go through a shared agent with 30s global timeout
- Buffer chunk writes retry up to 3 times with 1s backoff
- Multisig propose and execute paths now confirm transactions
- Replace process::exit(1) in deploy.rs with proper error propagation
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