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",
"test": "anchor test"
},

View File

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

View File

@ -20,6 +20,7 @@ pub const MAX_DAYS_LOCKED: u64 = 2555;
pub struct Registrar {
pub authority: Pubkey,
pub realm: Pubkey,
pub realm_community_mint: Pubkey,
pub warmup_secs: i64,
pub bump: u8,
// The length should be adjusted for one's use case.
@ -32,6 +33,7 @@ pub struct Voter {
pub authority: Pubkey,
pub registrar: Pubkey,
pub voter_bump: u8,
pub voter_weight_record_bump: u8,
pub deposits: [DepositEntry; 32],
}
@ -286,7 +288,7 @@ impl Lockup {
u64::try_from({
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())
}

View File

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

View File

@ -4,6 +4,7 @@ use anchor_lang::prelude::*;
use anchor_spl::token;
use context::*;
use error::*;
use spl_governance::addins::voter_weight::VoterWeightAccountType;
mod access_control;
mod account;
@ -71,6 +72,7 @@ pub mod governance_registry {
let registrar = &mut ctx.accounts.registrar.load_init()?;
registrar.bump = registrar_bump;
registrar.realm = ctx.accounts.realm.key();
registrar.realm_community_mint = ctx.accounts.realm_community_mint.key();
registrar.authority = ctx.accounts.authority.key();
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
/// 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_weight_record = &mut ctx.accounts.voter_weight_record;
// Init the voter.
voter.voter_bump = voter_bump;
voter.voter_weight_record_bump = voter_weight_record_bump;
voter.authority = ctx.accounts.authority.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(())
}
@ -306,11 +324,12 @@ pub mod governance_registry {
///
/// This "revise" instruction should be called in the same transaction,
/// 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 record = &mut ctx.accounts.vote_weight_record;
let record = &mut ctx.accounts.voter_weight_record;
record.voter_weight = voter.weight()?;
record.voter_weight_expiry = Some(Clock::get()?.slot);
Ok(())
}

View File

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