Address unaligned references, add padding
- Make Registrar zero-copy. While it was using borsh for serialization, the array of voting mint configs couldn't be serialized without unaligned references. - Reorganize all zero_copy fields such that switching to repr(C) would not make a difference. (just for safety in case that happens) - Add static asserts on the sizes of all account structs, so any changes are visible very early. - Add padding to structs that didn't have it yet.
This commit is contained in:
parent
ddf37c4de0
commit
decdd1230c
|
@ -3223,6 +3223,7 @@ dependencies = [
|
||||||
"spl-associated-token-account",
|
"spl-associated-token-account",
|
||||||
"spl-governance",
|
"spl-governance",
|
||||||
"spl-token 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"spl-token 3.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -27,6 +27,7 @@ anchor-spl = { git = "https://github.com/microwavedcola1/anchor.git", branch = "
|
||||||
# programs/voter-stake-registry/tests/fixtures/spl_governance.so is built from.
|
# programs/voter-stake-registry/tests/fixtures/spl_governance.so is built from.
|
||||||
spl-governance = { git = "https://github.com/solana-labs/solana-program-library", rev = "75ddd9bb229396427977fb679a0763630e83bce6", features = ["no-entrypoint"] }
|
spl-governance = { git = "https://github.com/solana-labs/solana-program-library", rev = "75ddd9bb229396427977fb679a0763630e83bce6", features = ["no-entrypoint"] }
|
||||||
solana-program = "^1.8.1"
|
solana-program = "^1.8.1"
|
||||||
|
static_assertions = "1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-sdk = "^1.8.1"
|
solana-sdk = "^1.8.1"
|
||||||
|
|
|
@ -7,7 +7,7 @@ use anchor_spl::token::{Token, TokenAccount};
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Clawback<'info> {
|
pub struct Clawback<'info> {
|
||||||
#[account(has_one = clawback_authority)]
|
#[account(has_one = clawback_authority)]
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
pub clawback_authority: Signer<'info>,
|
pub clawback_authority: Signer<'info>,
|
||||||
|
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
|
@ -65,7 +65,7 @@ impl<'info> Clawback<'info> {
|
||||||
/// that have already vested in place.
|
/// that have already vested in place.
|
||||||
pub fn clawback(ctx: Context<Clawback>, deposit_entry_index: u8) -> Result<()> {
|
pub fn clawback(ctx: Context<Clawback>, deposit_entry_index: u8) -> Result<()> {
|
||||||
// Load the accounts.
|
// Load the accounts.
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = &mut ctx.accounts.voter.load_mut()?;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
let deposit_entry = voter.active_deposit_mut(deposit_entry_index)?;
|
let deposit_entry = voter.active_deposit_mut(deposit_entry_index)?;
|
||||||
require!(
|
require!(
|
||||||
|
|
|
@ -9,7 +9,7 @@ use anchor_spl::token::{Mint, Token, TokenAccount};
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct ConfigureVotingMint<'info> {
|
pub struct ConfigureVotingMint<'info> {
|
||||||
#[account(mut, has_one = realm_authority)]
|
#[account(mut, has_one = realm_authority)]
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
pub realm_authority: Signer<'info>,
|
pub realm_authority: Signer<'info>,
|
||||||
|
|
||||||
/// Token account that all funds for this mint will be stored in
|
/// Token account that all funds for this mint will be stored in
|
||||||
|
@ -91,7 +91,7 @@ pub fn configure_voting_mint(
|
||||||
grant_authority: Option<Pubkey>,
|
grant_authority: Option<Pubkey>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
require!(lockup_saturation_secs > 0, LockupSaturationMustBePositive);
|
require!(lockup_saturation_secs > 0, LockupSaturationMustBePositive);
|
||||||
let registrar = &mut ctx.accounts.registrar;
|
let registrar = &mut ctx.accounts.registrar.load_mut()?;
|
||||||
require!(
|
require!(
|
||||||
(idx as usize) < registrar.voting_mints.len(),
|
(idx as usize) < registrar.voting_mints.len(),
|
||||||
OutOfBoundsVotingMintConfigIndex
|
OutOfBoundsVotingMintConfigIndex
|
||||||
|
@ -107,6 +107,7 @@ pub fn configure_voting_mint(
|
||||||
lockup_scaled_factor,
|
lockup_scaled_factor,
|
||||||
lockup_saturation_secs,
|
lockup_saturation_secs,
|
||||||
grant_authority: grant_authority.unwrap_or(Pubkey::new_from_array([0; 32])),
|
grant_authority: grant_authority.unwrap_or(Pubkey::new_from_array([0; 32])),
|
||||||
|
padding: [0; 31],
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for overflow in vote weight
|
// Check for overflow in vote weight
|
||||||
|
|
|
@ -5,7 +5,7 @@ use anchor_spl::token::Mint;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct CreateDepositEntry<'info> {
|
pub struct CreateDepositEntry<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
// the other constraints must be exhaustive
|
// the other constraints must be exhaustive
|
||||||
|
@ -39,7 +39,7 @@ pub fn create_deposit_entry(
|
||||||
allow_clawback: bool,
|
allow_clawback: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = &mut ctx.accounts.voter.load_mut()?;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
|
|
||||||
// Get the exchange rate entry associated with this deposit.
|
// Get the exchange rate entry associated with this deposit.
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct CreateRegistrar<'info> {
|
||||||
payer = payer,
|
payer = payer,
|
||||||
space = 8 + size_of::<Registrar>()
|
space = 8 + size_of::<Registrar>()
|
||||||
)]
|
)]
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
/// An spl-governance realm
|
/// An spl-governance realm
|
||||||
///
|
///
|
||||||
|
@ -52,7 +52,7 @@ pub struct CreateRegistrar<'info> {
|
||||||
/// To use the registrar, call ConfigVotingMint to register token mints that may be
|
/// To use the registrar, call ConfigVotingMint to register token mints that may be
|
||||||
/// used for voting.
|
/// used for voting.
|
||||||
pub fn create_registrar(ctx: Context<CreateRegistrar>, registrar_bump: u8) -> Result<()> {
|
pub fn create_registrar(ctx: Context<CreateRegistrar>, registrar_bump: u8) -> Result<()> {
|
||||||
let registrar = &mut ctx.accounts.registrar;
|
let registrar = &mut ctx.accounts.registrar.load_init()?;
|
||||||
registrar.bump = registrar_bump;
|
registrar.bump = registrar_bump;
|
||||||
registrar.governance_program_id = ctx.accounts.governance_program_id.key();
|
registrar.governance_program_id = ctx.accounts.governance_program_id.key();
|
||||||
registrar.realm = ctx.accounts.realm.key();
|
registrar.realm = ctx.accounts.realm.key();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::mem::size_of;
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
#[instruction(voter_bump: u8, voter_weight_record_bump: u8)]
|
#[instruction(voter_bump: u8, voter_weight_record_bump: u8)]
|
||||||
pub struct CreateVoter<'info> {
|
pub struct CreateVoter<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
#[account(
|
#[account(
|
||||||
init_if_needed,
|
init_if_needed,
|
||||||
|
@ -78,7 +78,7 @@ pub fn create_voter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter_authority = ctx.accounts.voter_authority.key();
|
let voter_authority = ctx.accounts.voter_authority.key();
|
||||||
|
|
||||||
// Init the voter if is hasn't been already.
|
// Init the voter if is hasn't been already.
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::convert::TryFrom;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Deposit<'info> {
|
pub struct Deposit<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
// the other constraints must be exhaustive
|
// the other constraints must be exhaustive
|
||||||
|
@ -59,7 +59,7 @@ pub fn deposit(ctx: Context<Deposit>, deposit_entry_index: u8, amount: u64) -> R
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = &mut ctx.accounts.voter.load_mut()?;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
|
|
||||||
let d_entry = voter.active_deposit_mut(deposit_entry_index)?;
|
let d_entry = voter.active_deposit_mut(deposit_entry_index)?;
|
||||||
|
@ -101,13 +101,15 @@ pub fn deposit(ctx: Context<Deposit>, deposit_entry_index: u8, amount: u64) -> R
|
||||||
d_entry.amount_deposited_native += amount;
|
d_entry.amount_deposited_native += amount;
|
||||||
d_entry.amount_initially_locked_native += amount;
|
d_entry.amount_initially_locked_native += amount;
|
||||||
|
|
||||||
|
let start_ts = d_entry.lockup.start_ts;
|
||||||
|
let end_ts = d_entry.lockup.end_ts;
|
||||||
msg!(
|
msg!(
|
||||||
"Deposited amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
"Deposited amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
||||||
amount,
|
amount,
|
||||||
deposit_entry_index,
|
deposit_entry_index,
|
||||||
d_entry.lockup.kind,
|
d_entry.lockup.kind,
|
||||||
d_entry.lockup.start_ts,
|
start_ts,
|
||||||
d_entry.lockup.end_ts,
|
end_ts,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -16,7 +16,7 @@ use std::mem::size_of;
|
||||||
amount: u64,
|
amount: u64,
|
||||||
)]
|
)]
|
||||||
pub struct Grant<'info> {
|
pub struct Grant<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
#[account(
|
#[account(
|
||||||
init_if_needed,
|
init_if_needed,
|
||||||
|
@ -89,7 +89,7 @@ pub fn grant(
|
||||||
amount: u64,
|
amount: u64,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Load accounts.
|
// Load accounts.
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter_authority = ctx.accounts.voter_authority.key();
|
let voter_authority = ctx.accounts.voter_authority.key();
|
||||||
|
|
||||||
// Get the exchange rate entry associated with this deposit.
|
// Get the exchange rate entry associated with this deposit.
|
||||||
|
@ -145,13 +145,15 @@ pub fn grant(
|
||||||
d_entry.amount_deposited_native = amount;
|
d_entry.amount_deposited_native = amount;
|
||||||
d_entry.amount_initially_locked_native = amount;
|
d_entry.amount_initially_locked_native = amount;
|
||||||
|
|
||||||
|
let start_ts = d_entry.lockup.start_ts;
|
||||||
|
let end_ts = d_entry.lockup.end_ts;
|
||||||
msg!(
|
msg!(
|
||||||
"Granted amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
"Granted amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
||||||
amount,
|
amount,
|
||||||
free_entry_idx,
|
free_entry_idx,
|
||||||
d_entry.lockup.kind,
|
d_entry.lockup.kind,
|
||||||
d_entry.lockup.start_ts,
|
start_ts,
|
||||||
d_entry.lockup.end_ts,
|
end_ts,
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -6,7 +6,7 @@ use anchor_lang::prelude::*;
|
||||||
pub struct ResetLockup<'info> {
|
pub struct ResetLockup<'info> {
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
// the other constraints must be exhaustive
|
// the other constraints must be exhaustive
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
#[account(
|
#[account(
|
||||||
mut,
|
mut,
|
||||||
seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()],
|
seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()],
|
||||||
|
@ -25,7 +25,7 @@ pub fn reset_lockup(
|
||||||
deposit_entry_index: u8,
|
deposit_entry_index: u8,
|
||||||
periods: u32,
|
periods: u32,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = &mut ctx.accounts.voter.load_mut()?;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
let d = voter.active_deposit_mut(deposit_entry_index)?;
|
let d = voter.active_deposit_mut(deposit_entry_index)?;
|
||||||
|
|
||||||
|
|
|
@ -7,14 +7,14 @@ use std::str::FromStr;
|
||||||
#[instruction(time_offset: i64)]
|
#[instruction(time_offset: i64)]
|
||||||
pub struct SetTimeOffset<'info> {
|
pub struct SetTimeOffset<'info> {
|
||||||
#[account(mut, has_one = realm_authority)]
|
#[account(mut, has_one = realm_authority)]
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
pub realm_authority: Signer<'info>,
|
pub realm_authority: Signer<'info>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A debug-only instruction that advances the time.
|
/// A debug-only instruction that advances the time.
|
||||||
pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
|
pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
|
||||||
let allowed_program = Pubkey::from_str("GovernanceProgram11111111111111111111111111").unwrap();
|
let allowed_program = Pubkey::from_str("GovernanceProgram11111111111111111111111111").unwrap();
|
||||||
let registrar = &mut ctx.accounts.registrar;
|
let registrar = &mut ctx.accounts.registrar.load_mut()?;
|
||||||
require!(
|
require!(
|
||||||
registrar.governance_program_id == allowed_program,
|
registrar.governance_program_id == allowed_program,
|
||||||
ErrorCode::DebugInstruction
|
ErrorCode::DebugInstruction
|
||||||
|
|
|
@ -6,7 +6,7 @@ use anchor_lang::prelude::*;
|
||||||
// exchange rates.
|
// exchange rates.
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct UpdateMaxVoteWeight<'info> {
|
pub struct UpdateMaxVoteWeight<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
// TODO: SPL governance has not yet implemented this.
|
// TODO: SPL governance has not yet implemented this.
|
||||||
pub max_vote_weight_record: UncheckedAccount<'info>,
|
pub max_vote_weight_record: UncheckedAccount<'info>,
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ pub struct UpdateMaxVoteWeight<'info> {
|
||||||
/// all tokens fits into a u64 *after* converting into common decimals, as
|
/// all tokens fits into a u64 *after* converting into common decimals, as
|
||||||
/// defined by the registrar's `rate_decimal` field.
|
/// defined by the registrar's `rate_decimal` field.
|
||||||
pub fn update_max_vote_weight<'info>(ctx: Context<UpdateMaxVoteWeight>) -> Result<()> {
|
pub fn update_max_vote_weight<'info>(ctx: Context<UpdateMaxVoteWeight>) -> Result<()> {
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let _max_vote_weight = registrar.max_vote_weight(ctx.remaining_accounts)?;
|
let _max_vote_weight = registrar.max_vote_weight(ctx.remaining_accounts)?;
|
||||||
// TODO: SPL governance has not yet implemented this feature.
|
// TODO: SPL governance has not yet implemented this feature.
|
||||||
// When it has, probably need to write the result into an account,
|
// When it has, probably need to write the result into an account,
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anchor_lang::prelude::*;
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct UpdateVoterWeightRecord<'info> {
|
pub struct UpdateVoterWeightRecord<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
// the other constraints must be exhaustive
|
// the other constraints must be exhaustive
|
||||||
|
@ -18,9 +18,9 @@ pub struct UpdateVoterWeightRecord<'info> {
|
||||||
mut,
|
mut,
|
||||||
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter.load()?.voter_authority.key().as_ref()],
|
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter.load()?.voter_authority.key().as_ref()],
|
||||||
bump = voter.load()?.voter_weight_record_bump,
|
bump = voter.load()?.voter_weight_record_bump,
|
||||||
constraint = voter_weight_record.realm == registrar.realm,
|
constraint = voter_weight_record.realm == registrar.load()?.realm,
|
||||||
constraint = voter_weight_record.governing_token_owner == voter.load()?.voter_authority,
|
constraint = voter_weight_record.governing_token_owner == voter.load()?.voter_authority,
|
||||||
constraint = voter_weight_record.governing_token_mint == registrar.realm_governing_token_mint,
|
constraint = voter_weight_record.governing_token_mint == registrar.load()?.realm_governing_token_mint,
|
||||||
)]
|
)]
|
||||||
pub voter_weight_record: Account<'info, VoterWeightRecord>,
|
pub voter_weight_record: Account<'info, VoterWeightRecord>,
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ pub struct UpdateVoterWeightRecord<'info> {
|
||||||
/// This "revise" instruction must be called immediately before voting, in
|
/// This "revise" instruction must be called immediately before voting, in
|
||||||
/// the same transaction.
|
/// the same transaction.
|
||||||
pub fn update_voter_weight_record(ctx: Context<UpdateVoterWeightRecord>) -> Result<()> {
|
pub fn update_voter_weight_record(ctx: Context<UpdateVoterWeightRecord>) -> Result<()> {
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = ctx.accounts.voter.load()?;
|
let voter = ctx.accounts.voter.load()?;
|
||||||
let record = &mut ctx.accounts.voter_weight_record;
|
let record = &mut ctx.accounts.voter_weight_record;
|
||||||
record.voter_weight = voter.weight(®istrar)?;
|
record.voter_weight = voter.weight(®istrar)?;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use anchor_spl::token::{self, Token, TokenAccount};
|
||||||
|
|
||||||
#[derive(Accounts)]
|
#[derive(Accounts)]
|
||||||
pub struct Withdraw<'info> {
|
pub struct Withdraw<'info> {
|
||||||
pub registrar: Box<Account<'info, Registrar>>,
|
pub registrar: AccountLoader<'info, Registrar>,
|
||||||
|
|
||||||
// checking the PDA address it just an extra precaution,
|
// checking the PDA address it just an extra precaution,
|
||||||
// the other constraints must be exhaustive
|
// the other constraints must be exhaustive
|
||||||
|
@ -36,9 +36,9 @@ pub struct Withdraw<'info> {
|
||||||
mut,
|
mut,
|
||||||
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter_authority.key().as_ref()],
|
seeds = [registrar.key().as_ref(), b"voter-weight-record".as_ref(), voter_authority.key().as_ref()],
|
||||||
bump = voter.load()?.voter_weight_record_bump,
|
bump = voter.load()?.voter_weight_record_bump,
|
||||||
constraint = voter_weight_record.realm == registrar.realm,
|
constraint = voter_weight_record.realm == registrar.load()?.realm,
|
||||||
constraint = voter_weight_record.governing_token_owner == voter.load()?.voter_authority,
|
constraint = voter_weight_record.governing_token_owner == voter.load()?.voter_authority,
|
||||||
constraint = voter_weight_record.governing_token_mint == registrar.realm_governing_token_mint,
|
constraint = voter_weight_record.governing_token_mint == registrar.load()?.realm_governing_token_mint,
|
||||||
)]
|
)]
|
||||||
pub voter_weight_record: Account<'info, VoterWeightRecord>,
|
pub voter_weight_record: Account<'info, VoterWeightRecord>,
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ impl<'info> Withdraw<'info> {
|
||||||
/// `amount` is in units of the native currency being withdrawn.
|
/// `amount` is in units of the native currency being withdrawn.
|
||||||
pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) -> Result<()> {
|
pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) -> Result<()> {
|
||||||
// Load the accounts.
|
// Load the accounts.
|
||||||
let registrar = &ctx.accounts.registrar;
|
let registrar = &ctx.accounts.registrar.load()?;
|
||||||
let voter = &mut ctx.accounts.voter.load_mut()?;
|
let voter = &mut ctx.accounts.voter.load_mut()?;
|
||||||
|
|
||||||
// Governance may forbid withdraws, for example when engaged in a vote.
|
// Governance may forbid withdraws, for example when engaged in a vote.
|
||||||
|
@ -110,13 +110,15 @@ pub fn withdraw(ctx: Context<Withdraw>, deposit_entry_index: u8, amount: u64) ->
|
||||||
amount,
|
amount,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
let start_ts = deposit_entry.lockup.start_ts;
|
||||||
|
let end_ts = deposit_entry.lockup.end_ts;
|
||||||
msg!(
|
msg!(
|
||||||
"Withdrew amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
"Withdrew amount {} at deposit index {} with lockup kind {:?}, and start ts {}, end ts {}",
|
||||||
amount,
|
amount,
|
||||||
deposit_entry_index,
|
deposit_entry_index,
|
||||||
deposit_entry.lockup.kind,
|
deposit_entry.lockup.kind,
|
||||||
deposit_entry.lockup.start_ts,
|
start_ts,
|
||||||
deposit_entry.lockup.end_ts,
|
end_ts,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update the voter weight record
|
// Update the voter weight record
|
||||||
|
|
|
@ -7,6 +7,9 @@ mod error;
|
||||||
mod instructions;
|
mod instructions;
|
||||||
pub mod state;
|
pub mod state;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate static_assertions;
|
||||||
|
|
||||||
// The program address.
|
// The program address.
|
||||||
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");
|
||||||
|
|
||||||
|
|
|
@ -9,11 +9,8 @@ use std::convert::TryFrom;
|
||||||
#[zero_copy]
|
#[zero_copy]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct DepositEntry {
|
pub struct DepositEntry {
|
||||||
// True if the deposit entry is being used.
|
// Locked state.
|
||||||
pub is_used: bool,
|
pub lockup: Lockup,
|
||||||
|
|
||||||
// Points to the VotingMintConfig this deposit uses.
|
|
||||||
pub voting_mint_config_idx: u8,
|
|
||||||
|
|
||||||
/// Amount in deposited, in native currency. Withdraws of vested tokens
|
/// Amount in deposited, in native currency. Withdraws of vested tokens
|
||||||
/// directly reduce this amount.
|
/// directly reduce this amount.
|
||||||
|
@ -32,11 +29,18 @@ pub struct DepositEntry {
|
||||||
/// which should not change due to withdraws.
|
/// which should not change due to withdraws.
|
||||||
pub amount_initially_locked_native: u64,
|
pub amount_initially_locked_native: u64,
|
||||||
|
|
||||||
|
// True if the deposit entry is being used.
|
||||||
|
pub is_used: bool,
|
||||||
|
|
||||||
|
/// If the clawback authority is allowed to extract locked tokens.
|
||||||
pub allow_clawback: bool,
|
pub allow_clawback: bool,
|
||||||
|
|
||||||
// Locked state.
|
// Points to the VotingMintConfig this deposit uses.
|
||||||
pub lockup: Lockup,
|
pub voting_mint_config_idx: u8,
|
||||||
|
|
||||||
|
pub padding: [u8; 13],
|
||||||
}
|
}
|
||||||
|
const_assert!(std::mem::size_of::<DepositEntry>() == 32 + 2 * 8 + 3 + 13);
|
||||||
|
|
||||||
impl DepositEntry {
|
impl DepositEntry {
|
||||||
/// # Voting Power Caclulation
|
/// # Voting Power Caclulation
|
||||||
|
|
|
@ -21,9 +21,7 @@ pub const SECS_PER_MONTH: i64 = 10;
|
||||||
pub const SECS_PER_MONTH: i64 = 365 * SECS_PER_DAY / 12;
|
pub const SECS_PER_MONTH: i64 = 365 * SECS_PER_DAY / 12;
|
||||||
|
|
||||||
#[zero_copy]
|
#[zero_copy]
|
||||||
#[derive(AnchorSerialize, AnchorDeserialize)]
|
|
||||||
pub struct Lockup {
|
pub struct Lockup {
|
||||||
pub kind: LockupKind,
|
|
||||||
/// Start of the lockup.
|
/// Start of the lockup.
|
||||||
///
|
///
|
||||||
/// Note, that if start_ts is in the future, the funds are nevertheless
|
/// Note, that if start_ts is in the future, the funds are nevertheless
|
||||||
|
@ -32,12 +30,17 @@ pub struct Lockup {
|
||||||
/// Similarly vote power computations don't care about start_ts and always
|
/// Similarly vote power computations don't care about start_ts and always
|
||||||
/// assume the full interval from now to end_ts.
|
/// assume the full interval from now to end_ts.
|
||||||
pub start_ts: i64,
|
pub start_ts: i64,
|
||||||
// End of the lockup.
|
|
||||||
|
/// End of the lockup.
|
||||||
pub end_ts: i64,
|
pub end_ts: i64,
|
||||||
|
|
||||||
|
/// Type of lockup.
|
||||||
|
pub kind: LockupKind,
|
||||||
|
|
||||||
// Empty bytes for future upgrades.
|
// Empty bytes for future upgrades.
|
||||||
// TODO: what kinds of upgrades do we foresee?
|
pub padding: [u8; 15],
|
||||||
pub padding: [u8; 16],
|
|
||||||
}
|
}
|
||||||
|
const_assert!(std::mem::size_of::<Lockup>() == 2 * 8 + 1 + 15);
|
||||||
|
|
||||||
impl Default for Lockup {
|
impl Default for Lockup {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -45,7 +48,7 @@ impl Default for Lockup {
|
||||||
kind: LockupKind::None,
|
kind: LockupKind::None,
|
||||||
start_ts: 0,
|
start_ts: 0,
|
||||||
end_ts: 0,
|
end_ts: 0,
|
||||||
padding: [0; 16],
|
padding: [0; 15],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,7 +62,7 @@ impl Lockup {
|
||||||
end_ts: start_ts
|
end_ts: start_ts
|
||||||
.checked_add(i64::from(periods).checked_mul(kind.period_secs()).unwrap())
|
.checked_add(i64::from(periods).checked_mul(kind.period_secs()).unwrap())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
padding: [0; 16],
|
padding: [0; 15],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -651,7 +654,7 @@ mod tests {
|
||||||
kind: LockupKind::Cliff,
|
kind: LockupKind::Cliff,
|
||||||
start_ts,
|
start_ts,
|
||||||
end_ts,
|
end_ts,
|
||||||
padding: [0u8; 16],
|
padding: [0u8; 15],
|
||||||
};
|
};
|
||||||
let days_left = l.periods_left(curr_ts)?;
|
let days_left = l.periods_left(curr_ts)?;
|
||||||
assert_eq!(days_left, t.expected_days_left);
|
assert_eq!(days_left, t.expected_days_left);
|
||||||
|
@ -666,7 +669,7 @@ mod tests {
|
||||||
kind: LockupKind::Monthly,
|
kind: LockupKind::Monthly,
|
||||||
start_ts,
|
start_ts,
|
||||||
end_ts,
|
end_ts,
|
||||||
padding: [0u8; 16],
|
padding: [0u8; 15],
|
||||||
};
|
};
|
||||||
let months_left = l.periods_left(curr_ts)?;
|
let months_left = l.periods_left(curr_ts)?;
|
||||||
assert_eq!(months_left, t.expected_months_left);
|
assert_eq!(months_left, t.expected_months_left);
|
||||||
|
@ -686,8 +689,9 @@ mod tests {
|
||||||
start_ts,
|
start_ts,
|
||||||
end_ts,
|
end_ts,
|
||||||
kind: t.kind,
|
kind: t.kind,
|
||||||
padding: [0u8; 16],
|
padding: [0u8; 15],
|
||||||
},
|
},
|
||||||
|
padding: [0; 13],
|
||||||
};
|
};
|
||||||
let curr_ts = start_ts + days_to_secs(t.curr_day);
|
let curr_ts = start_ts + days_to_secs(t.curr_day);
|
||||||
let power = d.voting_power_locked(curr_ts, t.amount_deposited, MAX_SECS_LOCKED)?;
|
let power = d.voting_power_locked(curr_ts, t.amount_deposited, MAX_SECS_LOCKED)?;
|
||||||
|
|
|
@ -4,7 +4,7 @@ use anchor_lang::prelude::*;
|
||||||
use anchor_spl::token::Mint;
|
use anchor_spl::token::Mint;
|
||||||
|
|
||||||
/// Instance of a voting rights distributor.
|
/// Instance of a voting rights distributor.
|
||||||
#[account]
|
#[account(zero_copy)]
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Registrar {
|
pub struct Registrar {
|
||||||
pub governance_program_id: Pubkey,
|
pub governance_program_id: Pubkey,
|
||||||
|
@ -12,13 +12,15 @@ pub struct Registrar {
|
||||||
pub realm_governing_token_mint: Pubkey,
|
pub realm_governing_token_mint: Pubkey,
|
||||||
pub realm_authority: Pubkey,
|
pub realm_authority: Pubkey,
|
||||||
pub clawback_authority: Pubkey,
|
pub clawback_authority: Pubkey,
|
||||||
pub bump: u8,
|
|
||||||
// The length should be adjusted for one's use case.
|
// The length should be adjusted for one's use case.
|
||||||
pub voting_mints: [VotingMintConfig; 2],
|
pub voting_mints: [VotingMintConfig; 2],
|
||||||
|
|
||||||
/// Debug only: time offset, to allow tests to move forward in time.
|
/// Debug only: time offset, to allow tests to move forward in time.
|
||||||
pub time_offset: i64,
|
pub time_offset: i64,
|
||||||
|
pub bump: u8,
|
||||||
|
pub padding: [u8; 31],
|
||||||
}
|
}
|
||||||
|
const_assert!(std::mem::size_of::<Registrar>() == 5 * 32 + 2 * 120 + 8 + 1 + 31);
|
||||||
|
|
||||||
impl Registrar {
|
impl Registrar {
|
||||||
pub fn clock_unix_timestamp(&self) -> i64 {
|
pub fn clock_unix_timestamp(&self) -> i64 {
|
||||||
|
|
|
@ -9,10 +9,12 @@ use spl_governance::state::token_owner_record;
|
||||||
pub struct Voter {
|
pub struct Voter {
|
||||||
pub voter_authority: Pubkey,
|
pub voter_authority: Pubkey,
|
||||||
pub registrar: Pubkey,
|
pub registrar: Pubkey,
|
||||||
|
pub deposits: [DepositEntry; 32],
|
||||||
pub voter_bump: u8,
|
pub voter_bump: u8,
|
||||||
pub voter_weight_record_bump: u8,
|
pub voter_weight_record_bump: u8,
|
||||||
pub deposits: [DepositEntry; 32],
|
pub padding: [u8; 30],
|
||||||
}
|
}
|
||||||
|
const_assert!(std::mem::size_of::<Voter>() == 2 * 32 + 32 * 64 + 2 + 30);
|
||||||
|
|
||||||
impl Voter {
|
impl Voter {
|
||||||
pub fn weight(&self, registrar: &Registrar) -> Result<u64> {
|
pub fn weight(&self, registrar: &Registrar) -> Result<u64> {
|
||||||
|
|
|
@ -10,13 +10,13 @@ const SCALED_FACTOR_BASE: u64 = 1_000_000_000;
|
||||||
/// See documentation of configure_voting_mint for details on how
|
/// See documentation of configure_voting_mint for details on how
|
||||||
/// native token amounts convert to vote weight.
|
/// native token amounts convert to vote weight.
|
||||||
#[zero_copy]
|
#[zero_copy]
|
||||||
#[derive(AnchorSerialize, AnchorDeserialize, Default)]
|
#[derive(Default)]
|
||||||
pub struct VotingMintConfig {
|
pub struct VotingMintConfig {
|
||||||
/// Mint for this entry.
|
/// Mint for this entry.
|
||||||
pub mint: Pubkey,
|
pub mint: Pubkey,
|
||||||
|
|
||||||
/// Number of digits to shift native amounts, applying a 10^digit_shift factor.
|
/// The authority that is allowed to push grants into voters
|
||||||
pub digit_shift: i8,
|
pub grant_authority: Pubkey,
|
||||||
|
|
||||||
/// Vote weight factor for deposits, in 1/SCALED_FACTOR_BASE units.
|
/// Vote weight factor for deposits, in 1/SCALED_FACTOR_BASE units.
|
||||||
pub deposit_scaled_factor: u64,
|
pub deposit_scaled_factor: u64,
|
||||||
|
@ -27,9 +27,13 @@ pub struct VotingMintConfig {
|
||||||
/// Number of seconds of lockup needed to reach the maximum lockup bonus.
|
/// Number of seconds of lockup needed to reach the maximum lockup bonus.
|
||||||
pub lockup_saturation_secs: u64,
|
pub lockup_saturation_secs: u64,
|
||||||
|
|
||||||
/// The authority that is allowed to push grants into voters
|
/// Number of digits to shift native amounts, applying a 10^digit_shift factor.
|
||||||
pub grant_authority: Pubkey,
|
pub digit_shift: i8,
|
||||||
|
|
||||||
|
// Empty bytes for future upgrades.
|
||||||
|
pub padding: [u8; 31],
|
||||||
}
|
}
|
||||||
|
const_assert!(std::mem::size_of::<VotingMintConfig>() == 2 * 32 + 3 * 8 + 1 + 31);
|
||||||
|
|
||||||
impl VotingMintConfig {
|
impl VotingMintConfig {
|
||||||
/// Converts an amount in this voting mints's native currency
|
/// Converts an amount in this voting mints's native currency
|
||||||
|
|
Loading…
Reference in New Issue