Renames
"unlocked_scaled_factor" was a confusing name because the value is also used when computing vote weight for locked deposits. Rename to "baseline_vote_weight_scaled_factor" and generally change "unlocked" to "baseline" in several places. Also rename "lockup_scaled_factor" to "max_extra_lockup_vote_weight_scaled_factor" to highlight that it's just the maximum contribution and that it's "extra" - on top of baseline.
This commit is contained in:
parent
4c5d896912
commit
dd51d3cbdd
|
@ -3,8 +3,10 @@ use anchor_lang::prelude::*;
|
|||
#[event]
|
||||
#[derive(Debug)]
|
||||
pub struct VoterInfo {
|
||||
/// Voter's total voting power
|
||||
pub voting_power: u64,
|
||||
pub voting_power_unlocked_only: u64,
|
||||
/// Voter's total voting power, when ignoring any effects from lockup
|
||||
pub voting_power_baseline: u64,
|
||||
}
|
||||
|
||||
#[derive(AnchorSerialize, AnchorDeserialize, Debug)]
|
||||
|
@ -34,8 +36,8 @@ pub struct DepositEntryInfo {
|
|||
pub unlocked: u64,
|
||||
/// Voting power implied by this deposit entry
|
||||
pub voting_power: u64,
|
||||
/// Voting power that is not based on lockup
|
||||
pub voting_power_unlocked_only: u64,
|
||||
/// Voting power without any adjustments for lockup
|
||||
pub voting_power_baseline: u64,
|
||||
/// Information about locking, if any
|
||||
pub locking: Option<LockingInfo>,
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ pub struct ConfigureVotingMint<'info> {
|
|||
///
|
||||
/// * `idx`: index of the rate to be set
|
||||
/// * `digit_shift`: how many digits to shift the native token amount, see below
|
||||
/// * `unlocked_scaled_factor`: vote weight factor for unlocked deposits, in 1/1e9 units
|
||||
/// * `lockup_scaled_factor`: max extra weight for lockups, in 1/1e9 units
|
||||
/// * `baseline_vote_weight_scaled_factor`: vote weight factor for all funds in vault, in 1/1e9 units
|
||||
/// * `max_extra_lockup_vote_weight_scaled_factor`: max extra weight for lockups, in 1/1e9 units
|
||||
/// * `lockup_saturation_secs`: lockup duration at which the full vote weight
|
||||
/// bonus is given to locked up deposits
|
||||
///
|
||||
|
@ -35,8 +35,8 @@ pub struct ConfigureVotingMint<'info> {
|
|||
/// ```
|
||||
/// vote_weight =
|
||||
/// amount * 10^(digit_shift)
|
||||
/// * (unlocked_scaled_factor/1e9
|
||||
/// + lockup_duration_factor * lockup_scaled_factor/1e9)
|
||||
/// * (baseline_vote_weight_scaled_factor/1e9
|
||||
/// + lockup_duration_factor * max_extra_lockup_vote_weight_scaled_factor/1e9)
|
||||
/// ```
|
||||
/// where lockup_duration_factor is a value between 0 and 1, depending on how long
|
||||
/// the amount is locked up. It is 1 when the lockup duration is greater or equal
|
||||
|
@ -46,8 +46,8 @@ pub struct ConfigureVotingMint<'info> {
|
|||
/// u64 limit! There is a check based on the supply of all configured mints, but
|
||||
/// do your own checking too.
|
||||
///
|
||||
/// If you use a single mint, prefer digit_shift=0 and unlocked_scaled_factor +
|
||||
/// lockup_scaled_factor <= 1e9. That way you won't have issues with overflow no
|
||||
/// If you use a single mint, prefer digit_shift=0 and baseline_vote_weight_scaled_factor +
|
||||
/// max_extra_lockup_vote_weight_scaled_factor <= 1e9. That way you won't have issues with overflow no
|
||||
/// matter the size of the mint's supply.
|
||||
///
|
||||
/// Digit shifting is particularly useful when using several voting token mints
|
||||
|
@ -56,8 +56,8 @@ pub struct ConfigureVotingMint<'info> {
|
|||
///
|
||||
/// Example: If you have token A with 6 decimals and token B with 9 decimals, you
|
||||
/// could set up:
|
||||
/// * A with digit_shift=0, unlocked_scaled_factor=2e9, lockup_scaled_factor=0
|
||||
/// * B with digit_shift=-3, unlocked_scaled_factor=1e9, lockup_scaled_factor=1e9
|
||||
/// * A with digit_shift=0, baseline_vote_weight_scaled_factor=2e9, max_extra_lockup_vote_weight_scaled_factor=0
|
||||
/// * B with digit_shift=-3, baseline_vote_weight_scaled_factor=1e9, max_extra_lockup_vote_weight_scaled_factor=1e9
|
||||
///
|
||||
/// That would make 1.0 decimaled tokens of A as valuable as 2.0 decimaled tokens
|
||||
/// of B when unlocked. B tokens could be locked up to double their vote weight. As
|
||||
|
@ -65,16 +65,16 @@ pub struct ConfigureVotingMint<'info> {
|
|||
///
|
||||
/// Note that in this example, you need 1000 native B tokens before receiving 1
|
||||
/// unit of vote weight. If the supplies were significantly lower, you could use
|
||||
/// * A with digit_shift=3, unlocked_scaled_factor=2e9, lockup_scaled_factor=0
|
||||
/// * B with digit_shift=0, unlocked_scaled_factor=1e9, lockup_scaled_factor=1e9
|
||||
/// * A with digit_shift=3, baseline_vote_weight_scaled_factor=2e9, max_extra_lockup_vote_weight_scaled_factor=0
|
||||
/// * B with digit_shift=0, baseline_vote_weight_scaled_factor=1e9, max_extra_lockup_vote_weight_scaled_factor=1e9
|
||||
/// to not lose precision on B tokens.
|
||||
///
|
||||
pub fn configure_voting_mint(
|
||||
ctx: Context<ConfigureVotingMint>,
|
||||
idx: u16,
|
||||
digit_shift: i8,
|
||||
unlocked_scaled_factor: u64,
|
||||
lockup_scaled_factor: u64,
|
||||
baseline_vote_weight_scaled_factor: u64,
|
||||
max_extra_lockup_vote_weight_scaled_factor: u64,
|
||||
lockup_saturation_secs: u64,
|
||||
grant_authority: Option<Pubkey>,
|
||||
) -> Result<()> {
|
||||
|
@ -100,8 +100,8 @@ pub fn configure_voting_mint(
|
|||
registrar.voting_mints[idx] = VotingMintConfig {
|
||||
mint,
|
||||
digit_shift,
|
||||
unlocked_scaled_factor,
|
||||
lockup_scaled_factor,
|
||||
baseline_vote_weight_scaled_factor,
|
||||
max_extra_lockup_vote_weight_scaled_factor,
|
||||
lockup_saturation_secs,
|
||||
grant_authority: grant_authority.unwrap_or_default(),
|
||||
reserved1: [0; 7],
|
||||
|
|
|
@ -32,7 +32,7 @@ pub fn log_voter_info(
|
|||
msg!("voter");
|
||||
emit!(VoterInfo {
|
||||
voting_power: voter.weight(registrar)?,
|
||||
voting_power_unlocked_only: voter.weight_from_unlocked(registrar)?,
|
||||
voting_power_baseline: voter.weight_baseline(registrar)?,
|
||||
});
|
||||
|
||||
msg!("deposit_entries");
|
||||
|
@ -71,8 +71,8 @@ pub fn log_voter_info(
|
|||
voting_mint_config_index: deposit.voting_mint_config_idx,
|
||||
unlocked: deposit.amount_unlocked(curr_ts),
|
||||
voting_power: deposit.voting_power(voting_mint_config, curr_ts)?,
|
||||
voting_power_unlocked_only: voting_mint_config
|
||||
.unlocked_vote_weight(deposit.amount_deposited_native)?,
|
||||
voting_power_baseline: voting_mint_config
|
||||
.baseline_vote_weight(deposit.amount_deposited_native)?,
|
||||
locking: locking_info,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -71,8 +71,8 @@ pub mod voter_stake_registry {
|
|||
ctx: Context<ConfigureVotingMint>,
|
||||
idx: u16,
|
||||
digit_shift: i8,
|
||||
unlocked_scaled_factor: u64,
|
||||
lockup_scaled_factor: u64,
|
||||
baseline_vote_weight_scaled_factor: u64,
|
||||
max_extra_lockup_vote_weight_scaled_factor: u64,
|
||||
lockup_saturation_secs: u64,
|
||||
grant_authority: Option<Pubkey>,
|
||||
) -> Result<()> {
|
||||
|
@ -80,8 +80,8 @@ pub mod voter_stake_registry {
|
|||
ctx,
|
||||
idx,
|
||||
digit_shift,
|
||||
unlocked_scaled_factor,
|
||||
lockup_scaled_factor,
|
||||
baseline_vote_weight_scaled_factor,
|
||||
max_extra_lockup_vote_weight_scaled_factor,
|
||||
lockup_saturation_secs,
|
||||
grant_authority,
|
||||
)
|
||||
|
|
|
@ -52,12 +52,12 @@ impl DepositEntry {
|
|||
/// For each cliff-locked token, the vote weight is:
|
||||
///
|
||||
/// ```
|
||||
/// voting_power = unlocked_vote_weight
|
||||
/// + lockup_duration_factor * max_lockup_vote_weight
|
||||
/// voting_power = baseline_vote_weight
|
||||
/// + lockup_duration_factor * max_extra_lockup_vote_weight
|
||||
/// ```
|
||||
///
|
||||
/// with
|
||||
/// unlocked_vote_weight and max_lockup_vote_weight from the
|
||||
/// baseline_vote_weight and max_extra_lockup_vote_weight from the
|
||||
/// VotingMintConfig
|
||||
/// lockup_duration_factor = lockup_time_remaining / max_lockup_time
|
||||
///
|
||||
|
@ -90,10 +90,10 @@ impl DepositEntry {
|
|||
/// voting_power_linear_vesting() below.
|
||||
///
|
||||
pub fn voting_power(&self, voting_mint_config: &VotingMintConfig, curr_ts: i64) -> Result<u64> {
|
||||
let unlocked_vote_weight =
|
||||
voting_mint_config.unlocked_vote_weight(self.amount_deposited_native)?;
|
||||
let baseline_vote_weight =
|
||||
voting_mint_config.baseline_vote_weight(self.amount_deposited_native)?;
|
||||
let max_locked_vote_weight =
|
||||
voting_mint_config.max_lockup_vote_weight(self.amount_initially_locked_native)?;
|
||||
voting_mint_config.max_extra_lockup_vote_weight(self.amount_initially_locked_native)?;
|
||||
let locked_vote_weight = self.voting_power_locked(
|
||||
curr_ts,
|
||||
max_locked_vote_weight,
|
||||
|
@ -103,7 +103,7 @@ impl DepositEntry {
|
|||
locked_vote_weight <= max_locked_vote_weight,
|
||||
InternalErrorBadLockupVoteWeight
|
||||
);
|
||||
unlocked_vote_weight
|
||||
baseline_vote_weight
|
||||
.checked_add(locked_vote_weight)
|
||||
.ok_or(Error::ErrorCode(ErrorCode::VoterWeightOverflow))
|
||||
}
|
||||
|
@ -447,19 +447,19 @@ mod tests {
|
|||
let voting_mint_config = VotingMintConfig {
|
||||
mint: Pubkey::default(),
|
||||
grant_authority: Pubkey::default(),
|
||||
unlocked_scaled_factor: 1_000_000_000, // 1x
|
||||
lockup_scaled_factor: 1_000_000_000, // 1x
|
||||
baseline_vote_weight_scaled_factor: 1_000_000_000, // 1x
|
||||
max_extra_lockup_vote_weight_scaled_factor: 1_000_000_000, // 1x
|
||||
lockup_saturation_secs: saturation as u64,
|
||||
digit_shift: 0,
|
||||
reserved1: [0; 7],
|
||||
reserved2: [0; 7],
|
||||
};
|
||||
|
||||
let unlocked_vote_weight =
|
||||
voting_mint_config.unlocked_vote_weight(deposit.amount_deposited_native)?;
|
||||
assert_eq!(unlocked_vote_weight, 10_000);
|
||||
let max_locked_vote_weight =
|
||||
voting_mint_config.max_lockup_vote_weight(deposit.amount_initially_locked_native)?;
|
||||
let baseline_vote_weight =
|
||||
voting_mint_config.baseline_vote_weight(deposit.amount_deposited_native)?;
|
||||
assert_eq!(baseline_vote_weight, 10_000);
|
||||
let max_locked_vote_weight = voting_mint_config
|
||||
.max_extra_lockup_vote_weight(deposit.amount_initially_locked_native)?;
|
||||
assert_eq!(max_locked_vote_weight, 10_000);
|
||||
|
||||
// The timestamp 100_000 is very far before the lockup_start timestamp
|
||||
|
|
|
@ -55,10 +55,10 @@ impl Registrar {
|
|||
.ok_or(Error::ErrorCode(ErrorCode::VotingMintNotFound))?;
|
||||
let mint = Account::<Mint>::try_from(mint_account)?;
|
||||
sum = sum
|
||||
.checked_add(voting_mint_config.unlocked_vote_weight(mint.supply)?)
|
||||
.checked_add(voting_mint_config.baseline_vote_weight(mint.supply)?)
|
||||
.ok_or(Error::ErrorCode(ErrorCode::VoterWeightOverflow))?;
|
||||
sum = sum
|
||||
.checked_add(voting_mint_config.max_lockup_vote_weight(mint.supply)?)
|
||||
.checked_add(voting_mint_config.max_extra_lockup_vote_weight(mint.supply)?)
|
||||
.ok_or(Error::ErrorCode(ErrorCode::VoterWeightOverflow))?;
|
||||
Ok(sum)
|
||||
})
|
||||
|
|
|
@ -18,6 +18,7 @@ const_assert!(std::mem::size_of::<Voter>() == 2 * 32 + 32 * 80 + 2 + 94);
|
|||
const_assert!(std::mem::size_of::<Voter>() % 8 == 0);
|
||||
|
||||
impl Voter {
|
||||
/// The full vote weight available to the voter
|
||||
pub fn weight(&self, registrar: &Registrar) -> Result<u64> {
|
||||
let curr_ts = registrar.clock_unix_timestamp();
|
||||
self.deposits
|
||||
|
@ -32,13 +33,14 @@ impl Voter {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn weight_from_unlocked(&self, registrar: &Registrar) -> Result<u64> {
|
||||
/// The vote weight available to the voter when ignoring any lockup effects
|
||||
pub fn weight_baseline(&self, registrar: &Registrar) -> Result<u64> {
|
||||
self.deposits
|
||||
.iter()
|
||||
.filter(|d| d.is_used)
|
||||
.try_fold(0u64, |sum, d| {
|
||||
registrar.voting_mints[d.voting_mint_config_idx as usize]
|
||||
.unlocked_vote_weight(d.amount_deposited_native)
|
||||
.baseline_vote_weight(d.amount_deposited_native)
|
||||
.map(|vp| sum.checked_add(vp).unwrap())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,11 +18,19 @@ pub struct VotingMintConfig {
|
|||
/// The authority that is allowed to push grants into voters
|
||||
pub grant_authority: Pubkey,
|
||||
|
||||
/// Vote weight factor for unlocked deposits, in 1/SCALED_FACTOR_BASE units.
|
||||
pub unlocked_scaled_factor: u64,
|
||||
/// Vote weight factor for all funds in the account, no matter if locked or not.
|
||||
///
|
||||
/// In 1/SCALED_FACTOR_BASE units.
|
||||
pub baseline_vote_weight_scaled_factor: u64,
|
||||
|
||||
/// Maximum vote weight factor for lockups, in 1/SCALED_FACTOR_BASE units.
|
||||
pub lockup_scaled_factor: u64,
|
||||
/// Maximum extra vote weight factor for lockups.
|
||||
///
|
||||
/// This is the extra votes gained for lockups lasting lockup_saturation_secs or
|
||||
/// longer. Shorter lockups receive only a fraction of the maximum extra vote weight,
|
||||
/// based on lockup_time divided by lockup_saturation_secs.
|
||||
///
|
||||
/// In 1/SCALED_FACTOR_BASE units.
|
||||
pub max_extra_lockup_vote_weight_scaled_factor: u64,
|
||||
|
||||
/// Number of seconds of lockup needed to reach the maximum lockup bonus.
|
||||
pub lockup_saturation_secs: u64,
|
||||
|
@ -41,7 +49,7 @@ impl VotingMintConfig {
|
|||
/// Converts an amount in this voting mints's native currency
|
||||
/// to the base vote weight (without the deposit or lockup scalings)
|
||||
/// by applying the digit_shift factor.
|
||||
pub fn base_vote_weight(&self, amount_native: u64) -> Result<u64> {
|
||||
fn digit_shift_native(&self, amount_native: u64) -> Result<u64> {
|
||||
let compute = || -> Option<u64> {
|
||||
let val = if self.digit_shift < 0 {
|
||||
(amount_native as u128).checked_div(10u128.pow((-self.digit_shift) as u32))?
|
||||
|
@ -54,10 +62,10 @@ impl VotingMintConfig {
|
|||
}
|
||||
|
||||
/// Apply a factor in SCALED_FACTOR_BASE units.
|
||||
fn apply_factor(base_vote_weight: u64, factor: u64) -> Result<u64> {
|
||||
fn apply_factor(base: u64, factor: u64) -> Result<u64> {
|
||||
let compute = || -> Option<u64> {
|
||||
u64::try_from(
|
||||
(base_vote_weight as u128)
|
||||
(base as u128)
|
||||
.checked_mul(factor as u128)?
|
||||
.checked_div(SCALED_FACTOR_BASE as u128)?,
|
||||
)
|
||||
|
@ -67,19 +75,22 @@ impl VotingMintConfig {
|
|||
}
|
||||
|
||||
/// The vote weight a deposit of a number of native tokens should have.
|
||||
pub fn unlocked_vote_weight(&self, amount_native: u64) -> Result<u64> {
|
||||
///
|
||||
/// This vote_weight is a component for all funds in a voter account, no
|
||||
/// matter if locked up or not.
|
||||
pub fn baseline_vote_weight(&self, amount_native: u64) -> Result<u64> {
|
||||
Self::apply_factor(
|
||||
self.base_vote_weight(amount_native)?,
|
||||
self.unlocked_scaled_factor,
|
||||
self.digit_shift_native(amount_native)?,
|
||||
self.baseline_vote_weight_scaled_factor,
|
||||
)
|
||||
}
|
||||
|
||||
/// The maximum vote weight a number of locked up native tokens can have.
|
||||
/// The maximum extra vote weight a number of locked up native tokens can have.
|
||||
/// Will be multiplied with a factor between 0 and 1 for the lockup duration.
|
||||
pub fn max_lockup_vote_weight(&self, amount_native: u64) -> Result<u64> {
|
||||
pub fn max_extra_lockup_vote_weight(&self, amount_native: u64) -> Result<u64> {
|
||||
Self::apply_factor(
|
||||
self.base_vote_weight(amount_native)?,
|
||||
self.lockup_scaled_factor,
|
||||
self.digit_shift_native(amount_native)?,
|
||||
self.max_extra_lockup_vote_weight_scaled_factor,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -94,7 +105,8 @@ impl VotingMintConfig {
|
|||
/// want to use the grant / vesting / clawback functionality for non-voting
|
||||
/// tokens like USDC.
|
||||
pub fn grants_vote_weight(&self) -> bool {
|
||||
self.unlocked_scaled_factor > 0 || self.lockup_scaled_factor > 0
|
||||
self.baseline_vote_weight_scaled_factor > 0
|
||||
|| self.max_extra_lockup_vote_weight_scaled_factor > 0
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -99,8 +99,8 @@ impl AddinCookie {
|
|||
index: u16,
|
||||
mint: &MintCookie,
|
||||
digit_shift: i8,
|
||||
unlocked_scaled_factor: f64,
|
||||
lockup_scaled_factor: f64,
|
||||
baseline_vote_weight_scaled_factor: f64,
|
||||
max_extra_lockup_vote_weight_scaled_factor: f64,
|
||||
lockup_saturation_secs: u64,
|
||||
grant_authority: Option<Pubkey>,
|
||||
other_mints: Option<&[Pubkey]>,
|
||||
|
@ -111,8 +111,10 @@ impl AddinCookie {
|
|||
&voter_stake_registry::instruction::ConfigureVotingMint {
|
||||
idx: index,
|
||||
digit_shift,
|
||||
unlocked_scaled_factor: (unlocked_scaled_factor * 1e9) as u64,
|
||||
lockup_scaled_factor: (lockup_scaled_factor * 1e9) as u64,
|
||||
baseline_vote_weight_scaled_factor: (baseline_vote_weight_scaled_factor * 1e9)
|
||||
as u64,
|
||||
max_extra_lockup_vote_weight_scaled_factor:
|
||||
(max_extra_lockup_vote_weight_scaled_factor * 1e9) as u64,
|
||||
lockup_saturation_secs,
|
||||
grant_authority,
|
||||
},
|
||||
|
|
|
@ -137,7 +137,7 @@ async fn test_log_voter_info() -> Result<(), TransportError> {
|
|||
|
||||
let voter_event =
|
||||
deserialize_event::<voter_stake_registry::events::VoterInfo>(&log[2]).unwrap();
|
||||
assert_eq!(voter_event.voting_power_unlocked_only, 12000);
|
||||
assert_eq!(voter_event.voting_power_baseline, 12000);
|
||||
assert_eq!(
|
||||
voter_event.voting_power,
|
||||
12000 + (1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11) * 1000 / 12
|
||||
|
@ -150,8 +150,8 @@ async fn test_log_voter_info() -> Result<(), TransportError> {
|
|||
assert_eq!(deposit_event.unlocked, 1000);
|
||||
assert_eq!(deposit_event.voting_power, voter_event.voting_power);
|
||||
assert_eq!(
|
||||
deposit_event.voting_power_unlocked_only,
|
||||
voter_event.voting_power_unlocked_only
|
||||
deposit_event.voting_power_baseline,
|
||||
voter_event.voting_power_baseline
|
||||
);
|
||||
assert!(deposit_event.locking.is_some());
|
||||
let locking = deposit_event.locking.unwrap();
|
||||
|
|
Loading…
Reference in New Issue