1 unstable release

0.2.4 May 5, 2022

#8 in #deposit

GPL-3.0-or-later

230KB
2.5K SLoC

Contains (ELF lib, 1MB) tests/fixtures/spl_governance.so

Description

Voter-stake-registry is a voter weight addin for Solana's spl-governance program.

With the addin enabled, the governance realm authority can:

  • Control which token mints can be used to vote, and at what scaling factor.

    That means that tokens from mints other than the governing mint can be used to vote and that their relative weight can be set.

  • Claw back locked tokens from user deposits where the user has enabled it.

    This is intended for use with token grants. Users would not enable clawback for normal deposits.

Users can:

  • Deposit and withdraw tokens of the chosen mints to gain voting weight.

    When an addin is enabled, the default deposit/withdraw flow of the governing token mints is disabled in spl-governance. The addin adds back the ability to deposit and withdraw without lockup.

  • Lock up tokens with different vesting schedules.

    The tokens will only be withdrawable once vested or the lock up has expired. Locked up tokens may have extra voting weight.

  • Use their voting weight to vote on spl-governance proposals.

Development

Rust

  • Built and developed using - rust stable(rustc 1.57.0 (f1edd0429 2021-11-29))
  • Run rust based tests - cargo test-sbf
  • run-generate-anchor-types.sh generates latest anchor types file and writes to ./voter_stake_registry.ts
  • To install the typescript client, do - yarn add @blockworks-foundation/voter-stake-registry-client
  • usage

Node/Typescript

  • Built and developed using - node (v16.13.1)
  • Usage
import { Provider, Wallet } from '@project-serum/anchor';
import { Connection, Keypair } from '@solana/web3.js';
import { VsrClient } from '@blockworks-foundation/voter-stake-registry-client';

async function main() {
  const options = Provider.defaultOptions();
  const connection = new Connection('https://api.devnet.solana.com', options);
  const wallet = new Wallet(Keypair.generate());
  const provider = new Provider(connection, wallet, options);
  const client = await VsrClient.connect(provider, true);
image

Deployment

Users will likely want to compile their own voter-stake-registry and deploy it to an address they control.

Before compiling, look at:

  • Registrar::voting_mints: The length of this array defines the number of configurable voting mints. Adjust as needed.

Devnet

For testing purposes, an instance of voter-stake-registry is deployed on devnet:

voter-stake-registry:  4Q6WW2ouZ6V3iaNm56MTd5n2tnTm4C5fiH8miFHnAFHo
spl-governance master: i7BqPFNUvB7yqwVeCRJHrtZVwRsZZNUJTdBm7Vg2cDb

Usage Scenarios

Setup

To start using the addin, make a governance proposal with the spl-governance realm authority to:

  1. Deploy an instance of the voter-stake-registry.
  2. Create a registrar for the realm with the CreateRegistrar instruction.
  3. Add voting token mints to the registrar by calling the ConfigureVotingMint instruction as often as desired.
  4. Call the SetRealmConfig instruction on spl-governance to set the voter-weight-addin program id and thereby enable the addin.

Deposit and Vote Without Lockup

  1. Call CreateVoter on the addin (first time only). Use the same voter_authority that was used for registering with spl-governance.

  2. Call CreateDepositEntry for the voter with LockupKind::None and the token mint for that tokens are to be deposited. (first time only)

    This creates a new deposit entry that can be used for depositing and withdrawing funds without lockup.

  3. Call Deposit for the voter and same deposit entry id to deposit funds.

  4. To vote, call UpdateVoterWeightRecord on the addin and then call CastVote on spl-governance in the same transaction, passing the voter weight record to both.

  5. Withdraw funds with Withdraw once proposals have resolved.

Give Grants of Locked Tokens

  1. Ask the recepient for their desired address.
  2. Make a proposal to call Grant for depositing tokens into a new locked deposit entry for their address. Use a governance that either is the realm authority or the token mint's grant authority.
  3. If necessary, later make a proposal to call Clawback on their deposit to retrieve all remaining locked tokens.

Manage Constant Maturity Deposits

Constant maturity deposits are useful when there's a vote weight bonus for locking up tokens: With cliff or daily/monthly vested deposits the remaining lockup period decreases as the time of maturity approaches and thus the vote weight decreases over time as well.

Constant maturity lockup keeps tokens at a fixed maturity. That guarantees a fixed vote weight, but also means they need to be manually transitioned to a different lockup type before they can eventually be withdrawn.

Setting up a constant maturity lockup is easy:

  1. Create a deposit entry of Constant lockup type with the chosen number of days.
  2. Deposit tokens into it.
  3. Use it to vote.

If you want access to the tokens again, you need to start the unlocking process by either

  • changing the whole deposit entry to Cliff with ResetLockup, or
  • creating a new Cliff deposit entry and transfering some locked tokens from your Constant deposit entry over with InternalTransferLocked.

In both cases you'll need to wait for the cliff to be reached before being able to access the tokens again.

Instruction Overview

Setup

  • CreateRegistrar

    Creates a Registrar account for a governance realm.

  • ConfigureVotingMint

    Enables voting with tokens from a mint and sets the exchange rate for vote weight.

Usage

  • CreateVoter

    Create a new voter account for a user.

  • CreateDepositEntry

    Create a deposit entry on a voter. A deposit entry is where tokens from a voting mint are deposited, and which may optionally have a lockup period and vesting schedule.

    Each voter can have multiple deposit entries.

  • Deposit

    Add tokens to a deposit entry.

  • Withdraw

    Remove tokens from a deposit entry, either unlocked or vested.

  • ResetLockup

    Re-lock tokens where the lockup has expired, or increase the duration of the lockup or change the lockup kind.

  • InternalTransferLocked

    Transfer locked tokens from one deposit entry to another. Useful for splitting off a chunk of a "constant" lockup deposit entry that you want to start the unlock process on.

  • InternalTransferUnocked

    Transfer unlocked tokens from one deposit entry to another. Useful for splitting off a chunk to be locked again in a different deposit entry without having to withdraw and redeposit.

  • UpdateVoterWeightRecord

    Write the current voter weight to the account that spl-governance can read to prepare for voting.

  • CloseDepositEntry

    Close an empty deposit entry, so it can be reused for a different mint or lockup type.

  • CloseVoter

    Close an empty voter, reclaiming rent.

Special

  • Grant

    As the realm authority or mint's grant authority: create a voter (if needed), create a new deposit and fund it. This instruction is intended for use with DAO proposals.

  • Clawback

    As the clawback authority, claim locked tokens from a voter's deposit entry that has opted-in to clawback.

  • UpdateMaxVoteWeight

    Unfinished instruction for telling spl-governance about the total maximum vote weight.

  • SetTimeOffset

    Debug instruction for advancing time in tests. Not usable.

License

This code is currently not free to use while in development.

References:

Dependencies

~20–32MB
~546K SLoC