1use anchor_lang::prelude::*;
7use anchor_spl::token::*;
8use govern::{proposal::ProposalState, Governor, Proposal, Vote};
9use vipers::*;
10
11mod account_validators;
12pub mod macros;
13mod processor;
14mod state;
15mod token_cpi;
16
17pub use state::*;
18
19declare_id!("Tok6iuA69RLN1QrpXgQKnDgE1YYbLzQsZGSoz75fQdz");
20
21#[program]
22pub mod simple_voter {
23 use super::*;
24
25 #[access_control(ctx.accounts.validate())]
26 pub fn initialize_electorate(
27 ctx: Context<InitializeElectorate>,
28 _bump: u8,
29 proposal_threshold: u64,
30 ) -> Result<()> {
31 let electorate = &mut ctx.accounts.electorate;
32 electorate.bump = unwrap_bump!(ctx, "electorate");
33 electorate.proposal_threshold = proposal_threshold;
34 electorate.base = ctx.accounts.base.key();
35 electorate.governor = ctx.accounts.governor.key();
36 electorate.gov_token_mint = ctx.accounts.gov_token_mint.key();
37
38 Ok(())
39 }
40
41 #[access_control(ctx.accounts.validate())]
42 pub fn initialize_token_record(ctx: Context<InitializeTokenRecord>, _bump: u8) -> Result<()> {
43 let token_record = &mut ctx.accounts.token_record;
44 token_record.bump = unwrap_bump!(ctx, "token_record");
45 token_record.balance = ctx.accounts.gov_token_vault.amount;
46 token_record.authority = ctx.accounts.authority.key();
47 token_record.electorate = ctx.accounts.electorate.key();
48 token_record.token_vault_key = ctx.accounts.gov_token_vault.key();
49
50 Ok(())
51 }
52
53 #[access_control(ctx.accounts.validate())]
54 pub fn activate_proposal(ctx: Context<ActivateProposal>) -> Result<()> {
55 processor::proposer::activate_proposal(ctx)
56 }
57
58 #[access_control(ctx.accounts.validate())]
59 pub fn deposit_tokens(ctx: Context<TokenContext>, amount: u64) -> Result<()> {
60 ctx.accounts.transfer_to_vault(amount)?;
61
62 let token_record = &mut ctx.accounts.token_record;
63 let vault = &mut ctx.accounts.gov_token_vault;
64 vault.reload()?;
65 token_record.balance = vault.amount;
66
67 Ok(())
68 }
69
70 #[access_control(ctx.accounts.validate())]
71 pub fn withdraw_tokens(ctx: Context<TokenContext>, amount: u64) -> Result<()> {
72 ctx.accounts.transfer_from_vault(amount)?;
73
74 let token_record = &mut ctx.accounts.token_record;
75 invariant!(
76 token_record.unfinalized_votes == 0,
77 "some votes not finalized"
78 );
79 let vault = &mut ctx.accounts.gov_token_vault;
80 vault.reload()?;
81 token_record.balance = vault.amount;
82
83 Ok(())
84 }
85
86 #[access_control(ctx.accounts.validate())]
87 pub fn cast_votes(ctx: Context<VoterContext>, vote_side: u8) -> Result<()> {
88 processor::voter::process_cast_votes(ctx, vote_side)
89 }
90
91 #[access_control(ctx.accounts.validate())]
92 pub fn withdraw_votes(ctx: Context<VoterContext>) -> Result<()> {
93 processor::voter::process_withdraw_votes(ctx)
94 }
95
96 pub fn finalize_votes(ctx: Context<FinalizeVote>) -> Result<()> {
97 invariant!(ctx.accounts.proposal.get_state()? != ProposalState::Active);
98 let token_record = &mut ctx.accounts.token_record;
99 token_record.unfinalized_votes = unwrap_int!(token_record.unfinalized_votes.checked_sub(1));
100
101 Ok(())
102 }
103}
104
105#[derive(Accounts)]
106pub struct InitializeElectorate<'info> {
107 pub base: Signer<'info>,
109 #[account(
111 init,
112 seeds = [
113 b"SimpleElectorate".as_ref(),
114 base.key().to_bytes().as_ref()
115 ],
116 bump,
117 payer = payer,
118 space = 8 + Electorate::LEN
119 )]
120 pub electorate: Account<'info, Electorate>,
121 pub governor: Account<'info, Governor>,
123 pub gov_token_mint: Account<'info, Mint>,
125 #[account(mut)]
127 pub payer: Signer<'info>,
128 pub system_program: Program<'info, System>,
130}
131
132#[derive(Accounts)]
133pub struct InitializeTokenRecord<'info> {
134 pub authority: Signer<'info>,
135 #[account(
137 init,
138 seeds = [
139 b"SimpleTokenRecord".as_ref(),
140 authority.key().to_bytes().as_ref(),
141 electorate.key().to_bytes().as_ref()
142 ],
143 bump,
144 payer = payer,
145 space = 8 + TokenRecord::LEN
146 )]
147 pub token_record: Account<'info, state::TokenRecord>,
148 #[account(mut)]
149 pub electorate: Account<'info, state::Electorate>,
150 pub gov_token_vault: Account<'info, TokenAccount>,
152 #[account(mut)]
154 pub payer: Signer<'info>,
155 pub system_program: Program<'info, System>,
157}
158
159#[derive(Accounts)]
160pub struct FinalizeVote<'info> {
161 pub authority: Signer<'info>,
163 #[account(mut)]
165 pub governor: Account<'info, Governor>,
166 #[account(mut)]
168 pub proposal: Account<'info, Proposal>,
169 #[account(mut)]
171 pub token_record: Account<'info, state::TokenRecord>,
172}
173
174#[derive(Accounts)]
175pub struct TribecaContext<'info> {
176 #[account(mut)]
178 pub governor: Account<'info, Governor>,
179 pub program: Program<'info, govern::program::Govern>,
181}
182
183#[derive(Accounts)]
184pub struct ActivateProposal<'info> {
185 #[account(has_one = governor)]
186 pub electorate: Account<'info, state::Electorate>,
187 pub governor: Account<'info, Governor>,
188 #[account(mut, has_one = governor)]
189 pub proposal: Account<'info, Proposal>,
190 pub govern_program: Program<'info, govern::program::Govern>,
192}
193
194#[derive(Accounts)]
195pub struct TokenContext<'info> {
196 pub authority: Signer<'info>,
198 #[account(mut)]
200 pub token_record: Account<'info, state::TokenRecord>,
201 #[account(mut)]
203 pub gov_token_account: Account<'info, TokenAccount>,
204 #[account(mut)]
206 pub gov_token_vault: Account<'info, TokenAccount>,
207 pub token_program: Program<'info, Token>,
209}
210
211#[derive(Accounts)]
212pub struct VoterContext<'info> {
213 pub electorate: Account<'info, Electorate>,
215 pub authority: Signer<'info>,
217 #[account(mut)]
219 pub proposal: Account<'info, Proposal>,
220 #[account(mut)]
222 pub token_record: Account<'info, state::TokenRecord>,
223 #[account(mut)]
225 pub vote: Account<'info, Vote>,
226 pub tribeca: TribecaContext<'info>,
228}
229
230#[error_code]
231pub enum ErrorCode {
232 #[msg("Below proposing threshold.")]
233 BelowProposingThreshold,
234}