# Custom Plugin

## 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:

```rust
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:

```rust
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
}
```

### The MaxVoterWeightRecord Interface

If your plugin needs to define the maximum possible voting weight (used for quorum calculations), implement:

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

    pub realm: Pubkey,
    pub governing_token_mint: Pubkey,

    /// The maximum voter weight
    pub max_voter_weight: u64,

    /// Optional expiry slot
    pub max_voter_weight_expiry: Option<Slot>,

    pub reserved: [u8; 8],
}
```

### 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:**

```
my-plugin/
  programs/
    my-plugin/
      src/
        lib.rs          # Program entrypoint
        instructions/
          create_registrar.rs
          create_voter_weight_record.rs
          update_voter_weight_record.rs
        state/
          registrar.rs   # Plugin config per realm
          voter.rs       # Per-voter state
```

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:

```rust
set_realm_config(
    program_id,
    realm,
    realm_authority,
    council_token_mint,
    payer,
    Some(GoverningTokenConfigAccountArgs {
        voter_weight_addin: Some(your_plugin_program_id),
        max_voter_weight_addin: None, // or Some(your_program_id)
        token_type: GoverningTokenType::Liquid,
    }),
    None, // council config
    min_community_weight_to_create_governance,
    community_mint_max_voter_weight_source,
)
```

#### 4. Build the UI Client

If you want your plugin to work with the Realms UI, implement the `VotePlugin` TypeScript interface:

```typescript
export interface VotePlugin {
  showDepositModal: boolean;

  initPlugin(
    rpcEndpoint: string,
    programId: string,
    voter: string,
    realm: string,
    mint: string,
    delegates: string[],
    inputWeightProgramId?: string
  ): Promise<VotePlugin>;

  getVoterWeight(tokenOwnerRecord?: string): Promise<VoteWeight>;

  getDepositInstructions(amount: BN): Promise<PluginInstructions>;
  getWithdrawInstructions(amount: BN, tokenOwnerRecord: string): Promise<PluginInstructions>;

  getDepositMessage(): DepositMessage | null;
  getPluginDepositMint(): string | null;
  getDepositedTokenAmount(): BN | null;

  getUpdateVoterWeightInstructions(
    tokenOwnerRecord?: string,
    actionTarget?: string,
    action?: VoterWeightAction,
    governance?: string,
    proposal?: string
  ): Promise<UpdateInstructions>;
}
```

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:

```rust
pub enum VoterWeightAddinInstruction {
    SetupVoterWeightRecord {
        voter_weight: u64,
        voter_weight_expiry: Option<Slot>,
        weight_action: Option<VoterWeightAction>,
        weight_action_target: Option<Pubkey>,
    },
    SetupMaxVoterWeightRecord {
        max_voter_weight: u64,
        max_voter_weight_expiry: Option<Slot>,
    },
}
```

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.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.realms.today/developer-resources/custom-plugin.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
