Skip to Content
svmgovProgram Reference

svmgov Program Reference

Program ID: govYkyQ3ePtGULAtY6V75qjWE8UH4vCUVQ1W4HdCAZU

Complete reference for all on-chain accounts, instructions, and error codes in the svmgov Anchor program.

If any term is unfamiliar, use the svmgov Terminology page as a glossary while reading this reference.


Account Structures

GlobalConfig

PDA seeds: ["global_config"]

The global governance configuration singleton. Initialized by the admin once.

FieldTypeDescription
adminPubkeyAdmin who can update config. Set to the program upgrade authority at init; transferable via the two-step nominate_admin / accept_admin flow
pending_adminOption<Pubkey>Nominated next admin awaiting acceptance; None when no transfer is in progress
max_title_lengthu16Maximum bytes in a proposal title (≤ 200)
max_description_lengthu16Maximum bytes in a proposal description (≤ 500)
max_support_epochsu64Max epochs a proposal can remain in the support phase
min_proposal_stake_lamportsu64Minimum stake (lamports) to create a proposal
cluster_support_pct_min_bpsu64% of cluster stake required to activate voting (basis points)
discussion_epochsu64Epochs reserved for discussion after voting is activated
voting_epochsu64Number of epochs in the active voting window
snapshot_epoch_extensionu64Epochs of extension before the snapshot slot is set
snapshot_slot_offseti64Slot offset from the target epoch start slot; may be negative if the computed slot remains in the future
bumpu8PDA bump

ProposalIndex

PDA seeds: ["index"]

Tracks the global proposal counter. Incremented on each new proposal.

FieldTypeDescription
current_indexu32Sequential proposal counter
bumpu8PDA bump

Proposal

PDA seeds: ["proposal", seed.to_le_bytes(), spl_vote_account_pubkey]

The main proposal account. One per governance proposal.

FieldTypeDescription
authorPubkeyValidator who created the proposal
titleStringShort title (length in bytes ≤ max_title_length, ≤ 200)
descriptionStringURL in https://github.com/solana-foundation/solana-governance-proposals for full proposal text (length in bytes ≤ max_description_length, ≤ 500)
creation_epochu64Epoch when created
start_epochu64Epoch when voting begins
end_epochu64Epoch when voting ends
proposer_stake_weight_bpu64Proposer’s stake weight (basis points) at creation
cluster_support_lamportsu64Running total of supporting stake (lamports)
for_votes_lamportsu64Accumulated lamports voted For
against_votes_lamportsu64Accumulated lamports voted Against
abstain_votes_lamportsu64Accumulated lamports voted Abstain
votingbooltrue once support threshold is reached
finalizedbooltrue after finalize_proposal
proposal_bumpu8PDA bump (used as seed for CPI signing)
creation_timestampi64Unix timestamp at creation
vote_countu32Number of votes cast
indexu32Sequential proposal index from ProposalIndex
consensus_resultOption<Pubkey>The NCN ConsensusResult PDA for this proposal
snapshot_slotu64Slot of stake snapshot (set when voting activates)
proposal_seedu64Seed used to derive proposal PDA (needed for CPI)
vote_account_pubkeyPubkeyProposer’s SPL vote account

Support

PDA seeds: ["support", proposal_pubkey, spl_vote_account_pubkey]

A receipt proving that a validator has already supported a proposal. Prevents double-supporting.

FieldTypeDescription
proposalPubkeyThe supported proposal
validatorPubkeyThe supporting validator
bumpu8PDA bump

Vote

PDA seeds: ["vote", proposal_pubkey, spl_vote_account_pubkey]

Records a validator’s vote on a proposal.

FieldTypeDescription
validatorPubkeyValidator who voted
proposalPubkeyThe voted-on proposal
for_votes_bpu64Basis points allocated to For
against_votes_bpu64Basis points allocated to Against
abstain_votes_bpu64Basis points allocated to Abstain
for_votes_lamportsu64Actual lamports for For (stake × bp / 10,000)
against_votes_lamportsu64Actual lamports for Against
abstain_votes_lamportsu64Actual lamports for Abstain
stakeu64Validator’s total stake at vote time
override_lamportsu64Stake already overridden by delegators
vote_timestampi64Unix timestamp
bumpu8PDA bump

VoteOverride

PDA seeds: ["vote_override", proposal_pubkey, spl_stake_account_pubkey, validator_vote_pubkey]

Records a delegator’s override of their validator’s vote.

FieldTypeDescription
delegatorPubkeyDelegator who overrode
stake_accountPubkeyStake account used for the override
validatorPubkeyValidator’s vote account being overridden
proposalPubkeyThe proposal
vote_account_validatorPubkeyThe validator’s Vote PDA address
for_votes_bpu64Delegator’s For allocation (basis points)
against_votes_bpu64Delegator’s Against allocation
abstain_votes_bpu64Delegator’s Abstain allocation
for_votes_lamportsu64Delegator’s For (stake × bp / 10,000)
against_votes_lamportsu64Delegator’s Against
abstain_votes_lamportsu64Delegator’s Abstain
stake_amountu64Delegator’s stake amount in this account
vote_override_timestampi64Unix timestamp
bumpu8PDA bump

VoteOverrideCache

PDA seeds: ["vote_override_cache", proposal_pubkey, validator_vote_pubkey]

Aggregates all delegator overrides for a validator on a proposal. Used when a delegator overrides before the validator has voted.

FieldTypeDescription
validatorPubkeyValidator vote account
proposalPubkeyProposal
vote_account_validatorPubkeyThe validator’s Vote PDA address
for_votes_bpu64Accumulated For (basis points) from all delegators
against_votes_bpu64Accumulated Against
abstain_votes_bpu64Accumulated Abstain
for_votes_lamportsu64Accumulated For (lamports)
against_votes_lamportsu64Accumulated Against
abstain_votes_lamportsu64Accumulated Abstain
total_stakeu64Total delegator stake in this cache
bumpu8PDA bump

VoteOverrideCache is created with init_if_needed to prevent prefunding DoS attacks. The validator’s cast_vote checks for an existing cache and applies it to reduce their effective stake.


Instructions

initialize_config

One-time initialization. Sets all governance parameters and stores the signer as admin.

Signer: the program’s upgrade authority. Init is gated by passing the program’s program and program_data accounts and requiring program_data.upgrade_authority_address == admin. The signer becomes the stored admin.

Required accounts: admin (signer), global_config (PDA, init), system_program, program (this program), program_data (this program’s ProgramData account).

ParameterTypeDescriptionValidation
max_title_lengthu16Max title bytes1–200
max_description_lengthu16Max description bytes1–500
max_support_epochsu64Max epochs in support phase
min_proposal_stake_lamportsu64Min stake to create a proposal
cluster_support_pct_min_bpsu64Cluster support threshold0–10,000
discussion_epochsu64Epochs of discussion
voting_epochsu64Epochs for voting
snapshot_epoch_extensionu64Extension epochs before snapshot
snapshot_slot_offseti64Slot offset from epoch start for the snapshot

update_config

Updates any subset of config fields. All params are Option<T>; only the Some fields are written.

Signer: the stored admin (GlobalConfig.admin).

The same bounds as initialize_config apply to max_title_length (1–200), max_description_length (1–500), and cluster_support_pct_min_bps (0–10,000).


nominate_admin

Step 1 of the two-step admin transfer. Records proposed_admin in GlobalConfig.pending_admin. Overwrites any prior nomination; rejects the all-zero pubkey. Emits AdminNominated.

Signer: the current admin.

ParameterTypeDescription
proposed_adminPubkeyThe nominee who must call accept_admin to take over

accept_admin

Step 2 of the two-step admin transfer. Promotes the signer to admin and clears pending_admin. Emits AdminTransferred.

Signer: the pending admin (must equal GlobalConfig.pending_admin).

Because the transfer only completes when the nominee signs, authority can never be handed to a key that cannot sign — and each side signs its own, separate transaction, which makes multisig-to-multisig handoffs (e.g. Squads) feasible.


initialize_index

Creates the global ProposalIndex PDA. Can be called by anyone. Must be called once before any proposals.


create_proposal

Creates a new governance proposal.

Signer: validator (must match spl_vote_account.node_pubkey)

ParameterTypeValidation
seed: u64u64Unique seed for PDA derivation
title: StringStringNon-empty, ≤ max_title_length
description: StringStringNon-empty, ≤ max_description_length, must be in https://github.com/solana-foundation/solana-governance-proposals and contain no .. segment

Additional checks: proposer stake ≥ min_proposal_stake_lamports


support_proposal

A validator signals support for a proposal.

Signer: validator

Adds the validator’s stake to proposal.cluster_support_lamports. When the total crosses the cluster support threshold:

  • Sets proposal.voting = true
  • Calculates snapshot_slot from the target epoch start slot plus GlobalConfig.snapshot_slot_offset
  • Sets start_epoch and end_epoch
  • Derives consensus_result PDA
  • CPIs to ncn-snapshot::init_ballot_box

cast_vote

A validator votes on an active proposal.

Signer: validator (must match meta_merkle_leaf.voting_wallet)

ParameterTypeValidation
for_votes_bpu64
against_votes_bpu64
abstain_votes_bpu64Sum of all three must equal 10,000

Flow:

  1. Verifies proposal is in voting phase and epoch range
  2. Reads MetaMerkleProof PDA
  3. CPIs to ncn-snapshot::verify_merkle_proof
  4. If VoteOverrideCache exists: applies cached delegator votes, reduces validator’s effective stake
  5. Records Vote PDA with lamport amounts

modify_vote

Modifies an existing validator vote during the voting phase.

Signer: validator

Subtracts old vote lamports from proposal totals, adds new values, re-verifies Merkle proof.


cast_vote_override

A delegator overrides their validator’s vote using a stake account proof.

Signer: delegator (must match stake_merkle_leaf.voting_wallet)

ParameterTypeDescription
for_votes_bpu64
against_votes_bpu64
abstain_votes_bpu64Must sum to 10,000
stake_merkle_proofVec<[u8;32]>Proof path for the stake account
stake_merkle_leafStakeMerkleLeafStake account leaf data

Two paths:

  • Path A (validator has voted): Immediately adjusts the validator’s Vote — subtracts delegator’s portion from old vote, adds delegator’s preferred allocation, updates override_lamports
  • Path B (validator hasn’t voted yet): Stores in VoteOverrideCache to be applied when the validator votes

modify_vote_override

Modifies an existing delegator override. Reverses the old override’s effect and applies new values.

Signer: delegator


finalize_proposal

Permissionless. Marks a proposal as finalized after its voting period ends.

Conditions: proposal.voting == true, !proposal.finalized, clock.epoch >= proposal.end_epoch


flush_merkle_root

Admin-only recovery instruction. Re-anchors a supported proposal’s snapshot/voting window forward off the current epoch (current_epoch + snapshot_epoch_extension), recomputing the snapshot_slot and consensus_result and creating a new BallotBox. Used when an NCN snapshot fails to reach consensus and the proposal must be rescheduled to allow a fresh snapshot. Only callable before voting starts (clock.epoch < start_epoch); gating it to the admin prevents an individual proposer from postponing their own vote.

Signer: program admin only (global_config.admin)


Error Codes

ErrorDescription
NotEnoughStakeInsufficient stake to create or vote
TitleEmptyProposal title is empty
TitleTooLongTitle exceeds max length (bytes)
DescriptionEmptyDescription is empty
DescriptionTooLongDescription exceeds max length (bytes)
DescriptionInvalidDescription must link to https://github.com/solana-foundation/solana-governance-proposals without .. path segments
InvalidProposalIdInvalid proposal ID
VotingNotStartedProposal has not yet entered voting phase
ProposalClosedVoting period has ended
ProposalFinalizedProposal already finalized
InvalidVoteDistributionFor + Against + Abstain ≠ 10,000 bps
VotingPeriodNotEndedTried to finalize before end_epoch
InvalidVoteAccountVote account mismatch
FailedDeserializeNodePubkeyCould not read node_pubkey from vote account
VoteNodePubkeyMismatchSigner doesn’t match vote account’s node_pubkey
InvalidClusterStakeCluster stake is zero
InvalidStartEpochStart epoch in the past
InvalidVotingLengthVoting length must be > 0
InvalidVoteAccountVersionUnsupported vote account version
InvalidVoteAccountSizeVote account has unexpected size
InvalidStakeAccountStake account invalid
InvalidStakeStateStake account in unexpected state
InvalidStakeAccountSizeStake account has unexpected size
InvalidSnapshotProgramProvided program ID is not the ncn-snapshot program
UnauthorizedAdminOnly the program admin (global_config.admin) can call flush_merkle_root
MerkleRootAlreadySetCannot set Merkle root that is already set
InvalidMerkleRootMerkle root cannot be all zeros
InvalidSnapshotSlotSnapshot slot must be past or current
MustBeOwnedBySnapshotProgramAccount must be owned by ncn-snapshot
InvalidConsensusResultPDAConsensusResult PDA address mismatch
CannotDeserializeMetaMerkleProofPDAMetaMerkleProof deserialization failed
CannotDeserializeConsensusResultConsensusResult deserialization failed
CannotModifyAfterStartCannot modify proposal after voting starts
VotingLengthTooLongVoting length exceeds max_support_epochs
ArithmeticOverflowOverflow in stake calculations
SnapshotProgramUpgradedSnapshot program upgrade protection triggered
MerkleRootNotSetMerkle root not yet set for this proposal
SupportPeriodExpiredSupport period has expired
UnauthorizedAdminSigner is not the configured GlobalConfig.admin
InvalidProgramprogram/program_data do not belong to this program (init upgrade-authority gate)
InvalidClusterSupportPctMincluster_support_pct_min_bps must be 0–10,000
InvalidMaxTitleLengthmax_title_length must be 1–200 (bytes)
InvalidMaxDescriptionLengthmax_description_length must be 1–500 (bytes)
InvalidAdminNominated admin cannot be the default (all-zero) pubkey
NoPendingAdminaccept_admin called with no pending nomination
NotPendingAdminSigner is not the pending admin nominee
Last updated on