voter-stake-registry/programs/voter-stake-registry/src/instructions/create_voter.rs

104 lines
3.9 KiB
Rust

use crate::error::*;
use crate::state::*;
use anchor_lang::prelude::*;
use anchor_lang::solana_program::sysvar::instructions as tx_instructions;
use spl_governance::addins::voter_weight::VoterWeightAccountType;
use std::mem::size_of;
#[derive(Accounts)]
#[instruction(voter_bump: u8, voter_weight_record_bump: u8)]
pub struct CreateVoter<'info> {
pub registrar: Box<Account<'info, Registrar>>,
#[account(
init_if_needed,
seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()],
bump = voter_bump,
payer = payer,
space = 8 + size_of::<Voter>(),
)]
pub voter: AccountLoader<'info, Voter>,
/// The authority controling the voter. Must be the same as the
/// `governing_token_owner` in the token owner record used with
/// spl-governance.
pub voter_authority: UncheckedAccount<'info>,
/// The voter weight record is the account that will be shown to spl-governance
/// to prove how much vote weight the voter has. See update_voter_weight_record.
#[account(
init_if_needed,
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter_authority.key().as_ref()],
bump = voter_weight_record_bump,
payer = payer,
space = 150,
)]
pub voter_weight_record: Account<'info, VoterWeightRecord>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
pub rent: Sysvar<'info, Rent>,
#[account(address = tx_instructions::ID)]
pub instructions: UncheckedAccount<'info>,
}
/// Returns if the anchor discriminator on the account is still unset
pub fn is_freshly_initialized(account_info: &AccountInfo) -> Result<bool> {
let data = account_info.try_borrow_data()?;
let mut disc_bytes = [0u8; 8];
disc_bytes.copy_from_slice(&data[..8]);
let discriminator = u64::from_le_bytes(disc_bytes);
Ok(discriminator == 0)
}
/// Creates a new voter account. There can only be a single voter per
/// voter_authority.
///
/// The user must register with spl-governance using the same voter_authority.
/// Their token owner record will be required for withdrawing funds later.
pub fn create_voter(
ctx: Context<CreateVoter>,
voter_bump: u8,
voter_weight_record_bump: u8,
) -> Result<()> {
// Forbid creating voter accounts from CPI. The goal is to make automation
// impossible that weakens some of the limitations intentionally imposed on
// locked tokens.
{
let ixns = ctx.accounts.instructions.to_account_info();
let current_index = tx_instructions::load_current_index_checked(&ixns)? as usize;
let current_ixn = tx_instructions::load_instruction_at_checked(current_index, &ixns)?;
require!(
current_ixn.program_id == *ctx.program_id,
ErrorCode::ForbiddenCpi
);
}
// Load accounts.
let registrar = &ctx.accounts.registrar;
let voter_authority = ctx.accounts.voter_authority.key();
// Init the voter if is hasn't been already.
if is_freshly_initialized(ctx.accounts.voter.as_ref())? {
let voter = &mut ctx.accounts.voter.load_init()?;
voter.voter_bump = voter_bump;
voter.voter_weight_record_bump = voter_weight_record_bump;
voter.voter_authority = voter_authority;
voter.registrar = ctx.accounts.registrar.key();
// Initializing the voter weight record exactly when setting up the voter is fine.
// Note that vote_weight_record is not an Anchor account, is_freshly_initialized()
// would not work.
let voter_weight_record = &mut ctx.accounts.voter_weight_record;
voter_weight_record.account_type = VoterWeightAccountType::VoterWeightRecord;
voter_weight_record.realm = registrar.realm;
voter_weight_record.governing_token_mint = registrar.realm_governing_token_mint;
voter_weight_record.governing_token_owner = voter_authority;
}
Ok(())
}