Create and write vote weight records

This commit is contained in:
armaniferrante 2021-10-21 19:16:32 -07:00
parent f00531fb7f
commit eaf6890146
No known key found for this signature in database
GPG Key ID: 58BEF301E91F7828
6 changed files with 114 additions and 47 deletions

View File

@ -1,5 +1,5 @@
{ {
"scripts": { "scripts": {
"lint:fix": "prettier tests/** -w", "lint:fix": "prettier tests/** -w",
"test": "anchor test" "test": "anchor test"
}, },

View File

@ -1,4 +1,3 @@
use crate::account::*;
use crate::context::*; use crate::context::*;
use crate::error::*; use crate::error::*;
use anchor_lang::prelude::*; use anchor_lang::prelude::*;

View File

@ -20,6 +20,7 @@ pub const MAX_DAYS_LOCKED: u64 = 2555;
pub struct Registrar { pub struct Registrar {
pub authority: Pubkey, pub authority: Pubkey,
pub realm: Pubkey, pub realm: Pubkey,
pub realm_community_mint: Pubkey,
pub warmup_secs: i64, pub warmup_secs: i64,
pub bump: u8, pub bump: u8,
// The length should be adjusted for one's use case. // The length should be adjusted for one's use case.
@ -32,6 +33,7 @@ pub struct Voter {
pub authority: Pubkey, pub authority: Pubkey,
pub registrar: Pubkey, pub registrar: Pubkey,
pub voter_bump: u8, pub voter_bump: u8,
pub voter_weight_record_bump: u8,
pub deposits: [DepositEntry; 32], pub deposits: [DepositEntry; 32],
} }
@ -286,7 +288,7 @@ impl Lockup {
u64::try_from({ u64::try_from({
let secs_elapsed = curr_ts.checked_sub(self.start_ts).unwrap(); let secs_elapsed = curr_ts.checked_sub(self.start_ts).unwrap();
secs_elapsed.checked_sub(SECS_PER_DAY).unwrap() secs_elapsed.checked_div(SECS_PER_DAY).unwrap()
}) })
.map_err(|_| ErrorCode::UnableToConvert.into()) .map_err(|_| ErrorCode::UnableToConvert.into())
} }

View File

@ -4,6 +4,8 @@ use anchor_spl::associated_token::AssociatedToken;
use anchor_spl::token::{self, Mint, Token, TokenAccount}; use anchor_spl::token::{self, Mint, Token, TokenAccount};
use std::mem::size_of; use std::mem::size_of;
pub const VOTER_WEIGHT_RECORD: [u8; 19] = *b"voter-weight-record";
#[derive(Accounts)] #[derive(Accounts)]
#[instruction(warmup_secs: i64, registrar_bump: u8)] #[instruction(warmup_secs: i64, registrar_bump: u8)]
pub struct CreateRegistrar<'info> { pub struct CreateRegistrar<'info> {
@ -15,7 +17,10 @@ pub struct CreateRegistrar<'info> {
space = 8 + size_of::<Registrar>() space = 8 + size_of::<Registrar>()
)] )]
pub registrar: AccountLoader<'info, Registrar>, pub registrar: AccountLoader<'info, Registrar>,
// Unsafe and untrusted. This instruction needs to be invoked immediatley
// after the realm is created.
pub realm: UncheckedAccount<'info>, pub realm: UncheckedAccount<'info>,
pub realm_community_mint: Account<'info, Mint>,
pub authority: UncheckedAccount<'info>, pub authority: UncheckedAccount<'info>,
pub payer: Signer<'info>, pub payer: Signer<'info>,
pub system_program: Program<'info, System>, pub system_program: Program<'info, System>,
@ -24,7 +29,7 @@ pub struct CreateRegistrar<'info> {
} }
#[derive(Accounts)] #[derive(Accounts)]
#[instruction(voter_bump: u8)] #[instruction(voter_bump: u8, voter_weight_record_bump: u8)]
pub struct CreateVoter<'info> { pub struct CreateVoter<'info> {
#[account( #[account(
init, init,
@ -34,8 +39,17 @@ pub struct CreateVoter<'info> {
space = 8 + size_of::<Voter>(), space = 8 + size_of::<Voter>(),
)] )]
pub voter: AccountLoader<'info, Voter>, pub voter: AccountLoader<'info, Voter>,
#[account(
init
, seeds = [VOTER_WEIGHT_RECORD.as_ref(), registrar.key().as_ref(), authority.key().as_ref()],
bump = voter_weight_record_bump,
payer = payer,
space = 150,
)]
pub voter_weight_record: Account<'info, VoterWeightRecord>,
pub registrar: AccountLoader<'info, Registrar>, pub registrar: AccountLoader<'info, Registrar>,
pub authority: Signer<'info>, pub authority: Signer<'info>,
pub payer: Signer<'info>,
pub token_program: Program<'info, Token>, pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>, pub associated_token_program: Program<'info, AssociatedToken>,
pub system_program: Program<'info, System>, pub system_program: Program<'info, System>,
@ -237,9 +251,9 @@ pub struct UpdateSchedule<'info> {
} }
#[derive(Accounts)] #[derive(Accounts)]
pub struct DecayVotingPower<'info> { pub struct UpdateVoterWeightRecord<'info> {
#[account( #[account(
seeds = [vote_weight_record.realm.as_ref()], seeds = [voter_weight_record.realm.as_ref()],
bump = registrar.load()?.bump, bump = registrar.load()?.bump,
)] )]
pub registrar: AccountLoader<'info, Registrar>, pub registrar: AccountLoader<'info, Registrar>,
@ -249,11 +263,15 @@ pub struct DecayVotingPower<'info> {
)] )]
pub voter: AccountLoader<'info, Voter>, pub voter: AccountLoader<'info, Voter>,
#[account( #[account(
mut, mut,
constraint = vote_weight_record.governing_token_owner == voter.load()?.authority, seeds = [VOTER_WEIGHT_RECORD.as_ref(), registrar.key().as_ref(), authority.key().as_ref()],
bump = voter.load()?.voter_weight_record_bump,
constraint = voter_weight_record.realm == registrar.load()?.realm,
constraint = voter_weight_record.governing_token_owner == voter.load()?.authority,
)] )]
pub vote_weight_record: Account<'info, VoterWeightRecord>, pub voter_weight_record: Account<'info, VoterWeightRecord>,
pub authority: Signer<'info>, pub authority: Signer<'info>,
pub system_program: Program<'info, System>,
} }
#[derive(Accounts)] #[derive(Accounts)]

View File

@ -4,6 +4,7 @@ use anchor_lang::prelude::*;
use anchor_spl::token; use anchor_spl::token;
use context::*; use context::*;
use error::*; use error::*;
use spl_governance::addins::voter_weight::VoterWeightAccountType;
mod access_control; mod access_control;
mod account; mod account;
@ -71,6 +72,7 @@ pub mod governance_registry {
let registrar = &mut ctx.accounts.registrar.load_init()?; let registrar = &mut ctx.accounts.registrar.load_init()?;
registrar.bump = registrar_bump; registrar.bump = registrar_bump;
registrar.realm = ctx.accounts.realm.key(); registrar.realm = ctx.accounts.realm.key();
registrar.realm_community_mint = ctx.accounts.realm_community_mint.key();
registrar.authority = ctx.accounts.authority.key(); registrar.authority = ctx.accounts.authority.key();
registrar.warmup_secs = warmup_secs; registrar.warmup_secs = warmup_secs;
@ -97,12 +99,28 @@ pub mod governance_registry {
/// Creates a new voter account. There can only be a single voter per /// Creates a new voter account. There can only be a single voter per
/// user wallet. /// user wallet.
pub fn create_voter(ctx: Context<CreateVoter>, voter_bump: u8) -> Result<()> { pub fn create_voter(
ctx: Context<CreateVoter>,
voter_bump: u8,
voter_weight_record_bump: u8,
) -> Result<()> {
// Load accounts.
let registrar = &ctx.accounts.registrar.load()?;
let voter = &mut ctx.accounts.voter.load_init()?; let voter = &mut ctx.accounts.voter.load_init()?;
let voter_weight_record = &mut ctx.accounts.voter_weight_record;
// Init the voter.
voter.voter_bump = voter_bump; voter.voter_bump = voter_bump;
voter.voter_weight_record_bump = voter_weight_record_bump;
voter.authority = ctx.accounts.authority.key(); voter.authority = ctx.accounts.authority.key();
voter.registrar = ctx.accounts.registrar.key(); voter.registrar = ctx.accounts.registrar.key();
// Init the voter weight record.
voter_weight_record.account_type = VoterWeightAccountType::VoterWeightRecord;
voter_weight_record.realm = registrar.realm;
voter_weight_record.governing_token_mint = registrar.realm_community_mint;
voter_weight_record.governing_token_owner = ctx.accounts.authority.key();
Ok(()) Ok(())
} }
@ -306,11 +324,12 @@ pub mod governance_registry {
/// ///
/// This "revise" instruction should be called in the same transaction, /// This "revise" instruction should be called in the same transaction,
/// immediately before voting. /// immediately before voting.
pub fn decay_voting_power(ctx: Context<DecayVotingPower>) -> Result<()> { pub fn update_voter_weight_record(ctx: Context<UpdateVoterWeightRecord>) -> Result<()> {
let voter = ctx.accounts.voter.load()?; let voter = ctx.accounts.voter.load()?;
let record = &mut ctx.accounts.vote_weight_record; let record = &mut ctx.accounts.voter_weight_record;
record.voter_weight = voter.weight()?; record.voter_weight = voter.weight()?;
record.voter_weight_expiry = Some(Clock::get()?.slot); record.voter_weight_expiry = Some(Clock::get()?.slot);
Ok(()) Ok(())
} }

View File

@ -32,21 +32,27 @@ describe("voting-rights", () => {
// Uninitialized variables shared across tests. // Uninitialized variables shared across tests.
let registrar: PublicKey, let registrar: PublicKey,
votingMintA: PublicKey, votingMintA: PublicKey,
votingMintB: PublicKey, votingMintB: PublicKey,
voter: PublicKey, voter: PublicKey,
voterWeightRecord: PublicKey,
votingToken: PublicKey, votingToken: PublicKey,
exchangeVaultA: PublicKey, exchangeVaultA: PublicKey,
exchangeVaultB: PublicKey; exchangeVaultB: PublicKey;
let registrarBump: number, let registrarBump: number,
votingMintBumpA: number, votingMintBumpA: number,
votingMintBumpB: number, votingMintBumpB: number,
voterBump: number; voterBump: number,
let mintA: PublicKey, mintB: PublicKey, godA: PublicKey, godB: PublicKey; voterWeightRecordBump: number;
let mintA: PublicKey,
mintB: PublicKey,
godA: PublicKey,
godB: PublicKey,
realmCommunityMint: PublicKey;
let tokenAClient: Token, let tokenAClient: Token,
tokenBClient: Token, tokenBClient: Token,
votingTokenClientA: Token, votingTokenClientA: Token,
votingTokenClientB: Token; votingTokenClientB: Token;
it("Creates tokens and mints", async () => { it("Creates tokens and mints", async () => {
const [_mintA, _godA] = await createMintAndVault( const [_mintA, _godA] = await createMintAndVault(
@ -66,6 +72,7 @@ describe("voting-rights", () => {
mintB = _mintB; mintB = _mintB;
godA = _godA; godA = _godA;
godB = _godB; godB = _godB;
realmCommunityMint = mintA;
}); });
it("Creates PDAs", async () => { it("Creates PDAs", async () => {
@ -85,6 +92,15 @@ describe("voting-rights", () => {
[_registrar.toBuffer(), program.provider.wallet.publicKey.toBuffer()], [_registrar.toBuffer(), program.provider.wallet.publicKey.toBuffer()],
program.programId program.programId
); );
const [_voterWeightRecord, _voterWeightRecordBump] =
await PublicKey.findProgramAddress(
[
anchor.utils.bytes.utf8.encode("voter-weight-record"),
_registrar.toBuffer(),
program.provider.wallet.publicKey.toBuffer(),
],
program.programId
);
votingToken = await Token.getAssociatedTokenAddress( votingToken = await Token.getAssociatedTokenAddress(
associatedTokenProgram, associatedTokenProgram,
tokenProgram, tokenProgram,
@ -108,13 +124,15 @@ describe("voting-rights", () => {
registrar = _registrar; registrar = _registrar;
votingMintA = _votingMintA; votingMintA = _votingMintA;
votingMintB = _votingMintB; votingMintB = _votingMintB;
voter = _voter; voter = _voter;
registrarBump = _registrarBump; registrarBump = _registrarBump;
votingMintBumpA = _votingMintBumpA; votingMintBumpA = _votingMintBumpA;
votingMintBumpB = _votingMintBumpB; votingMintBumpB = _votingMintBumpB;
voterBump = _voterBump; voterBump = _voterBump;
voterWeightRecord = _voterWeightRecord;
voterWeightRecordBump = _voterWeightRecordBump;
}); });
it("Creates token clients", async () => { it("Creates token clients", async () => {
@ -149,21 +167,18 @@ describe("voting-rights", () => {
}); });
it("Initializes a registrar", async () => { it("Initializes a registrar", async () => {
await program.rpc.createRegistrar( await program.rpc.createRegistrar(new BN(0), registrarBump, {
new BN(0), accounts: {
registrarBump, registrar,
{ realm,
accounts: { realmCommunityMint,
registrar, authority: program.provider.wallet.publicKey,
realm, payer: program.provider.wallet.publicKey,
authority: program.provider.wallet.publicKey, systemProgram,
payer: program.provider.wallet.publicKey, tokenProgram,
systemProgram, rent,
tokenProgram, },
rent, });
},
}
);
}); });
it("Adds an exchange rate A", async () => { it("Adds an exchange rate A", async () => {
@ -176,7 +191,7 @@ describe("voting-rights", () => {
accounts: { accounts: {
exchangeVault: exchangeVaultA, exchangeVault: exchangeVaultA,
depositMint: mintA, depositMint: mintA,
votingMint: votingMintA, votingMint: votingMintA,
registrar, registrar,
authority: program.provider.wallet.publicKey, authority: program.provider.wallet.publicKey,
rent, rent,
@ -197,7 +212,7 @@ describe("voting-rights", () => {
accounts: { accounts: {
exchangeVault: exchangeVaultB, exchangeVault: exchangeVaultB,
depositMint: mintB, depositMint: mintB,
votingMint: votingMintB, votingMint: votingMintB,
registrar, registrar,
authority: program.provider.wallet.publicKey, authority: program.provider.wallet.publicKey,
rent, rent,
@ -209,11 +224,13 @@ describe("voting-rights", () => {
}); });
it("Initializes a voter", async () => { it("Initializes a voter", async () => {
await program.rpc.createVoter(voterBump, { await program.rpc.createVoter(voterBump, voterWeightRecordBump, {
accounts: { accounts: {
voter, voter,
voterWeightRecord,
registrar, registrar,
authority: program.provider.wallet.publicKey, authority: program.provider.wallet.publicKey,
payer: program.provider.wallet.publicKey,
systemProgram, systemProgram,
associatedTokenProgram, associatedTokenProgram,
tokenProgram, tokenProgram,
@ -238,9 +255,9 @@ describe("voting-rights", () => {
depositMint: mintA, depositMint: mintA,
votingMint: votingMintA, votingMint: votingMintA,
tokenProgram, tokenProgram,
systemProgram, systemProgram,
associatedTokenProgram, associatedTokenProgram,
rent, rent,
}, },
}, },
}); });
@ -300,9 +317,9 @@ describe("voting-rights", () => {
depositMint: mintA, depositMint: mintA,
votingMint: votingMintA, votingMint: votingMintA,
tokenProgram, tokenProgram,
systemProgram, systemProgram,
associatedTokenProgram, associatedTokenProgram,
rent, rent,
}, },
}, },
}); });
@ -313,4 +330,16 @@ describe("voting-rights", () => {
assert.ok(deposit.amountDeposited.toNumber() === 10); assert.ok(deposit.amountDeposited.toNumber() === 10);
assert.ok(deposit.rateIdx === 0); assert.ok(deposit.rateIdx === 0);
}); });
it("Updates a vote weight record", async () => {
await program.rpc.updateVoterWeightRecord({
accounts: {
registrar,
voter,
voterWeightRecord,
authority: program.provider.wallet.publicKey,
systemProgram,
},
});
});
}); });