How to create a custom plugin and attach it to Realms
Create a Custom Plugin
SPL Governance uses an open/close plugin architecture that allows you to customize how voting power is determined. Instead of the default "deposit tokens = voting power" model, you can build plugins that implement any custom logic: NFT voting, token locking, staking, quadratic voting, prediction markets, and more.
How Plugins Work
Plugins (also called "addins") are standalone Solana programs that integrate with SPL Governance through two standardized interfaces:
VoterWeightRecord - Provides individual voter weight to the governance program
MaxVoterWeightRecord - Provides the maximum possible voter weight for quorum calculations
When a Realm is configured with a plugin, the governance program reads the VoterWeightRecord account instead of using deposited token amounts for voting power. This means your plugin has full control over how voting power is computed.
The VoterWeightRecord Interface
Your plugin must create and maintain VoterWeightRecord accounts that conform to this structure:
pubstructVoterWeightRecord{/// Discriminator: sha256("account:VoterWeightRecord")[..8]pubaccount_discriminator:[u8;8],/// The Realm this record belongs topubrealm:Pubkey,/// The governing token mint (community or council)pubgoverning_token_mint:Pubkey,/// The voter's wallet addresspubgoverning_token_owner:Pubkey,/// The computed voter weightpubvoter_weight:u64,/// Optional: slot when this weight expires (None = never expires)pubvoter_weight_expiry:Option<Slot>,/// Optional: the specific governance action this weight applies topubweight_action:Option<VoterWeightAction>,/// Optional: the target account for the action (e.g., a specific Proposal)pubweight_action_target:Option<Pubkey>,pubreserved:[u8;8],}
The VoterWeightAction enum defines which governance actions the weight applies to:
The MaxVoterWeightRecord Interface
If your plugin needs to define the maximum possible voting weight (used for quorum calculations), implement:
Building Your Plugin: Step by Step
1. Create the On-Chain Program
Your plugin is a standard Solana program (Anchor or native). It must:
Store voter weight data in VoterWeightRecord accounts with the correct discriminator
Provide an instruction to update/refresh the voter weight (commonly called UpdateVoterWeightRecord)
Optionally provide a MaxVoterWeightRecord if your model needs custom quorum logic
Recommended structure using Anchor:
A Registrar account is a common pattern: it stores the plugin configuration for a specific Realm (e.g., which token mints are accepted, locking parameters, etc.).
2. Key Instructions to Implement
CreateRegistrar - Initialize plugin config for a realm:
UpdateVoterWeightRecord - Refresh the voter's weight before governance actions:
Read your custom state (deposits, locks, NFTs, etc.)
Compute the weight
Write it to the VoterWeightRecord with the current slot as expiry
This instruction is called in the same transaction as the governance action
3. Register the Plugin with a Realm
Use SetRealmConfig to attach your plugin to a Realm:
4. Build the UI Client
If you want your plugin to work with the Realms UI, implement the VotePlugin TypeScript interface:
See the Custom UI Integration guide for details on integrating with the Realms UI.
Existing Plugin Examples
These plugins are live on mainnet and serve as reference implementations:
Plugin
Description
Program ID
VSR (Voter Stake Registry)
Token locking with time-weighted multipliers
vsr2nfGVNHmSY8uxoBGqq8AQbwz3JwaEaHqGbsTPXqQ
Bio VSR
Bio variant of Voter Stake Registry
bioU1oVcFscZa2KwxexVREBVBuRtMBUNdFoGPUSsPnf
Epicentral VSR
Epicentral Labs staking-based voting
epciLwfT8DePi1ch5FKXvLmwHBknXV9EL42yGqrZPEy
NFT Voter
Vote with NFTs from specific collections
GnftV5kLjd67tvHpNGyodwWveEKivz3ZWvvE3Z4xi2iw
Token Voter
Vote based on token holdings
HA99cuBQCCzZu1zuHN2qBxo2FBo1cxNLwKkdt6Prhy8v
Bonk
Bonk token voting
BonKNRsdWbRFkjPRNXfMqGNq5GShqdT2RGKZZ7Pn1LR
Token Haver
Vote based on token ownership (haver)
HavrKkk4L3mpEgPRD4GZ3RBvE3qLiKrK2xPam8ojDhPg
Gateway
Civic Pass identity verification gate
GgathUhdrCWRHowoRKACjgWhYHfxCEdBi5ViqYN6HVxk
Quadratic
Quadratic voting power distribution
quadCSapU8nTdLg73KHDnmdxKnJQsh7GUbu5tZfnRRr
Pyth
Pyth staking-based voting
pytS9TjG1qyAZypk7n8rw8gfW9sUaqqYyMhJQ4E7JCQ
Drift
Drift protocol staking voting
dVoTE1AJqkZVoE1mPbWcqYPmEEvAUBksHY2NiM2UJQe
Parcl
Parcl governance voting
parCLFza3XxuMnoR8M2LhCXMKP7dSaaUvqjXbKre9UT
Sowellian
Prediction market governance
sowEL1Rtn3p479rg34gW7mVPeCNY58Es5rkLpFsCJAW
Core Voter
Vote with Metaplex Core NFTs
cNFTHBQuERFVrbmks1UzqFQPBHzquRmoLbmgpHBbczF
Testing Your Plugin
The governance/addin-mock directory in the SPL Governance repo contains a mock addin program that can be used as a starting point and for testing:
Use it in integration tests to simulate custom voting weights without deploying a full plugin.
Plugin Chaining
Plugins can be chained by configuring one plugin to read the VoterWeightRecord produced by another as its input. The VotePlugin interface supports this via the optional inputWeightProgramId parameter in initPlugin. For example, a Gateway plugin can wrap a VSR plugin to require identity verification on top of token-locked voting power. In this setup, VSR computes the base voter weight, and Gateway reads that weight and gates it behind a Civic Pass check before producing the final VoterWeightRecord consumed by the governance program.
pub enum VoterWeightAction {
CastVote, // Voting on a proposal
CommentProposal, // Commenting on a proposal
CreateGovernance, // Creating a new governance
CreateProposal, // Creating a new proposal
SignOffProposal, // Signing off a proposal
}