Custom Plugin

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:

  1. VoterWeightRecord - Provides individual voter weight to the governance program

  2. 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:

pub struct VoterWeightRecord {
    /// Discriminator: sha256("account:VoterWeightRecord")[..8]
    pub account_discriminator: [u8; 8],

    /// The Realm this record belongs to
    pub realm: Pubkey,

    /// The governing token mint (community or council)
    pub governing_token_mint: Pubkey,

    /// The voter's wallet address
    pub governing_token_owner: Pubkey,

    /// The computed voter weight
    pub voter_weight: u64,

    /// Optional: slot when this weight expires (None = never expires)
    pub voter_weight_expiry: Option<Slot>,

    /// Optional: the specific governance action this weight applies to
    pub weight_action: Option<VoterWeightAction>,

    /// Optional: the target account for the action (e.g., a specific Proposal)
    pub weight_action_target: Option<Pubkey>,

    pub reserved: [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:

  • Seeds: ['registrar', realm, governing_token_mint]

  • Stores: realm reference, governing token mint, custom config

CreateVoterWeightRecord - Create a voter's weight record:

  • Seeds: ['voter-weight-record', realm, governing_token_mint, governing_token_owner]

  • Initialize with proper discriminator

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.

Last updated