voter_stake_registry/
lib.rs

1use anchor_lang::prelude::*;
2use instructions::*;
3use state::*;
4
5mod error;
6pub mod events;
7mod governance;
8mod instructions;
9pub mod state;
10
11#[macro_use]
12extern crate static_assertions;
13
14// The program address.
15declare_id!("4Q6WW2ouZ6V3iaNm56MTd5n2tnTm4C5fiH8miFHnAFHo");
16
17/// # Introduction
18///
19/// The governance registry is an "addin" to the SPL governance program that
20/// allows one to both vote with many different ypes of tokens for voting and to
21/// scale voting power as a linear function of time locked--subject to some
22/// maximum upper bound.
23///
24/// The flow for voting with this program is as follows:
25///
26/// - Create a SPL governance realm.
27/// - Create a governance registry account.
28/// - Add exchange rates for any tokens one wants to deposit. For example,
29///   if one wants to vote with tokens A and B, where token B has twice the
30///   voting power of token A, then the exchange rate of B would be 2 and the
31///   exchange rate of A would be 1.
32/// - Create a voter account.
33/// - Deposit tokens into this program, with an optional lockup period.
34/// - Vote.
35///
36/// Upon voting with SPL governance, a client is expected to call
37/// `decay_voting_power` to get an up to date measurement of a given `Voter`'s
38/// voting power for the given slot. If this is not done, then the transaction
39/// will fail (since the SPL governance program will require the measurement
40/// to be active for the current slot).
41///
42/// # Interacting with SPL Governance
43///
44/// This program does not directly interact with SPL governance via CPI.
45/// Instead, it simply writes a `VoterWeightRecord` account with a well defined
46/// format, which is then used by SPL governance as the voting power measurement
47/// for a given user.
48///
49/// # Max Vote Weight
50///
51/// Given that one can use multiple tokens to vote, the max vote weight needs
52/// to be a function of the total supply of all tokens, converted into a common
53/// currency. For example, if you have Token A and Token B, where 1 Token B =
54/// 10 Token A, then the `max_vote_weight` should be `supply(A) + supply(B)*10`
55/// where both are converted into common decimals. Then, when calculating the
56/// weight of an individual voter, one can convert B into A via the given
57/// exchange rate, which must be fixed.
58///
59/// Note that the above also implies that the `max_vote_weight` must fit into
60/// a u64.
61#[program]
62pub mod voter_stake_registry {
63    use super::*;
64
65    pub fn create_registrar(ctx: Context<CreateRegistrar>, registrar_bump: u8) -> Result<()> {
66        instructions::create_registrar(ctx, registrar_bump)
67    }
68
69    pub fn configure_voting_mint(
70        ctx: Context<ConfigureVotingMint>,
71        idx: u16,
72        digit_shift: i8,
73        baseline_vote_weight_scaled_factor: u64,
74        max_extra_lockup_vote_weight_scaled_factor: u64,
75        lockup_saturation_secs: u64,
76        grant_authority: Option<Pubkey>,
77    ) -> Result<()> {
78        instructions::configure_voting_mint(
79            ctx,
80            idx,
81            digit_shift,
82            baseline_vote_weight_scaled_factor,
83            max_extra_lockup_vote_weight_scaled_factor,
84            lockup_saturation_secs,
85            grant_authority,
86        )
87    }
88
89    pub fn create_voter(
90        ctx: Context<CreateVoter>,
91        voter_bump: u8,
92        voter_weight_record_bump: u8,
93    ) -> Result<()> {
94        instructions::create_voter(ctx, voter_bump, voter_weight_record_bump)
95    }
96
97    pub fn create_deposit_entry(
98        ctx: Context<CreateDepositEntry>,
99        deposit_entry_index: u8,
100        kind: LockupKind,
101        start_ts: Option<u64>,
102        periods: u32,
103        allow_clawback: bool,
104    ) -> Result<()> {
105        instructions::create_deposit_entry(
106            ctx,
107            deposit_entry_index,
108            kind,
109            start_ts,
110            periods,
111            allow_clawback,
112        )
113    }
114
115    pub fn deposit(ctx: Context<Deposit>, deposit_entry_index: u8, amount: u64) -> Result<()> {
116        instructions::deposit(ctx, deposit_entry_index, amount)
117    }
118
119    pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) -> Result<()> {
120        instructions::withdraw(ctx, deposit_entry_index, amount)
121    }
122
123    pub fn grant(
124        ctx: Context<Grant>,
125        voter_bump: u8,
126        voter_weight_record_bump: u8,
127        kind: LockupKind,
128        start_ts: Option<u64>,
129        periods: u32,
130        allow_clawback: bool,
131        amount: u64,
132    ) -> Result<()> {
133        instructions::grant(
134            ctx,
135            voter_bump,
136            voter_weight_record_bump,
137            kind,
138            start_ts,
139            periods,
140            allow_clawback,
141            amount,
142        )
143    }
144
145    pub fn clawback(ctx: Context<Clawback>, deposit_entry_index: u8) -> Result<()> {
146        instructions::clawback(ctx, deposit_entry_index)
147    }
148
149    pub fn close_deposit_entry(
150        ctx: Context<CloseDepositEntry>,
151        deposit_entry_index: u8,
152    ) -> Result<()> {
153        instructions::close_deposit_entry(ctx, deposit_entry_index)
154    }
155
156    pub fn reset_lockup(
157        ctx: Context<ResetLockup>,
158        deposit_entry_index: u8,
159        kind: LockupKind,
160        periods: u32,
161    ) -> Result<()> {
162        instructions::reset_lockup(ctx, deposit_entry_index, kind, periods)
163    }
164
165    pub fn internal_transfer_locked(
166        ctx: Context<InternalTransferLocked>,
167        source_deposit_entry_index: u8,
168        target_deposit_entry_index: u8,
169        amount: u64,
170    ) -> Result<()> {
171        instructions::internal_transfer_locked(
172            ctx,
173            source_deposit_entry_index,
174            target_deposit_entry_index,
175            amount,
176        )
177    }
178
179    pub fn internal_transfer_unlocked(
180        ctx: Context<InternalTransferUnlocked>,
181        source_deposit_entry_index: u8,
182        target_deposit_entry_index: u8,
183        amount: u64,
184    ) -> Result<()> {
185        instructions::internal_transfer_unlocked(
186            ctx,
187            source_deposit_entry_index,
188            target_deposit_entry_index,
189            amount,
190        )
191    }
192
193    pub fn update_voter_weight_record(ctx: Context<UpdateVoterWeightRecord>) -> Result<()> {
194        instructions::update_voter_weight_record(ctx)
195    }
196
197    pub fn update_max_vote_weight(ctx: Context<UpdateMaxVoteWeight>) -> Result<()> {
198        instructions::update_max_vote_weight(ctx)
199    }
200
201    pub fn close_voter<'key, 'accounts, 'remaining, 'info>(
202        ctx: Context<'key, 'accounts, 'remaining, 'info, CloseVoter<'info>>,
203    ) -> Result<()> {
204        instructions::close_voter(ctx)
205    }
206
207    pub fn log_voter_info(
208        ctx: Context<LogVoterInfo>,
209        deposit_entry_begin: u8,
210        deposit_entry_count: u8,
211    ) -> Result<()> {
212        instructions::log_voter_info(ctx, deposit_entry_begin, deposit_entry_count)
213    }
214
215    pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
216        instructions::set_time_offset(ctx, time_offset)
217    }
218}