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)
descriptionStringGitHub URL 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 start with https://github.com

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 be a https://github.com link
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