use anchor_lang::prelude::*;
use anchor_spl::token::*;
use govern::{proposal::ProposalState, Governor, Proposal, Vote};
use vipers::*;
mod account_validators;
pub mod macros;
mod processor;
mod state;
mod token_cpi;
pub use state::*;
declare_id!("Tok6iuA69RLN1QrpXgQKnDgE1YYbLzQsZGSoz75fQdz");
#[program]
pub mod simple_voter {
use super::*;
#[access_control(ctx.accounts.validate())]
pub fn initialize_electorate(
ctx: Context<InitializeElectorate>,
_bump: u8,
proposal_threshold: u64,
) -> Result<()> {
let electorate = &mut ctx.accounts.electorate;
electorate.bump = unwrap_bump!(ctx, "electorate");
electorate.proposal_threshold = proposal_threshold;
electorate.base = ctx.accounts.base.key();
electorate.governor = ctx.accounts.governor.key();
electorate.gov_token_mint = ctx.accounts.gov_token_mint.key();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn initialize_token_record(ctx: Context<InitializeTokenRecord>, _bump: u8) -> Result<()> {
let token_record = &mut ctx.accounts.token_record;
token_record.bump = unwrap_bump!(ctx, "token_record");
token_record.balance = ctx.accounts.gov_token_vault.amount;
token_record.authority = ctx.accounts.authority.key();
token_record.electorate = ctx.accounts.electorate.key();
token_record.token_vault_key = ctx.accounts.gov_token_vault.key();
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn activate_proposal(ctx: Context<ActivateProposal>) -> Result<()> {
processor::proposer::activate_proposal(ctx)
}
#[access_control(ctx.accounts.validate())]
pub fn deposit_tokens(ctx: Context<TokenContext>, amount: u64) -> Result<()> {
ctx.accounts.transfer_to_vault(amount)?;
let token_record = &mut ctx.accounts.token_record;
let vault = &mut ctx.accounts.gov_token_vault;
vault.reload()?;
token_record.balance = vault.amount;
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn withdraw_tokens(ctx: Context<TokenContext>, amount: u64) -> Result<()> {
ctx.accounts.transfer_from_vault(amount)?;
let token_record = &mut ctx.accounts.token_record;
invariant!(
token_record.unfinalized_votes == 0,
"some votes not finalized"
);
let vault = &mut ctx.accounts.gov_token_vault;
vault.reload()?;
token_record.balance = vault.amount;
Ok(())
}
#[access_control(ctx.accounts.validate())]
pub fn cast_votes(ctx: Context<VoterContext>, vote_side: u8) -> Result<()> {
processor::voter::process_cast_votes(ctx, vote_side)
}
#[access_control(ctx.accounts.validate())]
pub fn withdraw_votes(ctx: Context<VoterContext>) -> Result<()> {
processor::voter::process_withdraw_votes(ctx)
}
pub fn finalize_votes(ctx: Context<FinalizeVote>) -> Result<()> {
invariant!(ctx.accounts.proposal.get_state()? != ProposalState::Active);
let token_record = &mut ctx.accounts.token_record;
token_record.unfinalized_votes = unwrap_int!(token_record.unfinalized_votes.checked_sub(1));
Ok(())
}
}
#[derive(Accounts)]
pub struct InitializeElectorate<'info> {
pub base: Signer<'info>,
#[account(
init,
seeds = [
b"SimpleElectorate".as_ref(),
base.key().to_bytes().as_ref()
],
bump,
payer = payer,
space = 8 + Electorate::LEN
)]
pub electorate: Account<'info, Electorate>,
pub governor: Account<'info, Governor>,
pub gov_token_mint: Account<'info, Mint>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct InitializeTokenRecord<'info> {
pub authority: Signer<'info>,
#[account(
init,
seeds = [
b"SimpleTokenRecord".as_ref(),
authority.key().to_bytes().as_ref(),
electorate.key().to_bytes().as_ref()
],
bump,
payer = payer,
space = 8 + TokenRecord::LEN
)]
pub token_record: Account<'info, state::TokenRecord>,
#[account(mut)]
pub electorate: Account<'info, state::Electorate>,
pub gov_token_vault: Account<'info, TokenAccount>,
#[account(mut)]
pub payer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[derive(Accounts)]
pub struct FinalizeVote<'info> {
pub authority: Signer<'info>,
#[account(mut)]
pub governor: Account<'info, Governor>,
#[account(mut)]
pub proposal: Account<'info, Proposal>,
#[account(mut)]
pub token_record: Account<'info, state::TokenRecord>,
}
#[derive(Accounts)]
pub struct TribecaContext<'info> {
#[account(mut)]
pub governor: Account<'info, Governor>,
pub program: Program<'info, govern::program::Govern>,
}
#[derive(Accounts)]
pub struct ActivateProposal<'info> {
#[account(has_one = governor)]
pub electorate: Account<'info, state::Electorate>,
pub governor: Account<'info, Governor>,
#[account(mut, has_one = governor)]
pub proposal: Account<'info, Proposal>,
pub govern_program: Program<'info, govern::program::Govern>,
}
#[derive(Accounts)]
pub struct TokenContext<'info> {
pub authority: Signer<'info>,
#[account(mut)]
pub token_record: Account<'info, state::TokenRecord>,
#[account(mut)]
pub gov_token_account: Account<'info, TokenAccount>,
#[account(mut)]
pub gov_token_vault: Account<'info, TokenAccount>,
pub token_program: Program<'info, Token>,
}
#[derive(Accounts)]
pub struct VoterContext<'info> {
pub electorate: Account<'info, Electorate>,
pub authority: Signer<'info>,
#[account(mut)]
pub proposal: Account<'info, Proposal>,
#[account(mut)]
pub token_record: Account<'info, state::TokenRecord>,
#[account(mut)]
pub vote: Account<'info, Vote>,
pub tribeca: TribecaContext<'info>,
}
#[error_code]
pub enum ErrorCode {
#[msg("Below proposing threshold.")]
BelowProposingThreshold,
}