Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
600 changes: 367 additions & 233 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Users can:
# Development

## Rust
* Built and developed using - rust stable(`rustc 1.57.0 (f1edd0429 2021-11-29)`)
* Built and developed using rust toolchain **rustc 1.65.0 (897e37553 2022-11-02)**
* Run rust based tests - `cargo test-sbf`
* `run-generate-anchor-types.sh` generates latest anchor types file and writes to `./voter_stake_registry.ts`
* To install the typescript client, do - `yarn add @blockworks-foundation/voter-stake-registry-client`
Expand Down
13 changes: 7 additions & 6 deletions programs/voter-stake-registry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@ bytemuck = "1.9.1"
# Recently the discriminator for new VoterWeightRecord accounts has changed, and upgrading
# this dependency here without also upgrading the spl-governance program instance beforehand
# would lead to VWR accounts that are unusable until the spl-governance program is upgraded.
spl-governance = { version = "=2.2.1", features = ["no-entrypoint"] }
spl-governance-addin-api = "=0.1.1"
spl-governance = { version = "=3.1.1", features = ["no-entrypoint"] }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to be checked out in relation to the warning above - likely all gov deployments have updated, but need to double check

spl-governance-addin-api = "=0.1.3"

solana-program = "1.14.10"
solana-program = "1.14.22"
static_assertions = "1.1"

[dev-dependencies]
solana-sdk = "1.14.10"
solana-program-test = "1.14.10"
solana-logger = "1.14.10"
solana-sdk = "1.14.22"
solana-program-test = "1.14.22"
solana-logger = "1.14.22"
spl-token = { version = "^3.0.0", features = ["no-entrypoint"] }
spl-associated-token-account = { version = "^1.0.3", features = ["no-entrypoint"] }
bytemuck = "^1.7.2"
Expand All @@ -54,3 +54,4 @@ bincode = "^1.3.1"
log = "0.4.14"
env_logger = "0.9.0"
base64 = "0.13.0"
tempfile = "=3.6.0"
6 changes: 6 additions & 0 deletions programs/voter-stake-registry/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,4 +116,10 @@ pub enum VsrError {
// 6037 / 0x1795
#[msg("")]
InvalidTimestampArguments,
// 6038 / 0x1796
#[msg("")]
BadUnlockDepositAuthority,
// 6039 / 0x1797
#[msg("")]
MintConfigNotUsed,
}
2 changes: 2 additions & 0 deletions programs/voter-stake-registry/src/instructions/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub use internal_transfer_unlocked::*;
pub use log_voter_info::*;
pub use reset_lockup::*;
pub use set_time_offset::*;
pub use unlock_deposit::*;
pub use update_max_vote_weight::*;
pub use update_voter_weight_record::*;
pub use withdraw::*;
Expand All @@ -30,6 +31,7 @@ mod internal_transfer_unlocked;
mod log_voter_info;
mod reset_lockup;
mod set_time_offset;
mod unlock_deposit;
mod update_max_vote_weight;
mod update_voter_weight_record;
mod withdraw;
51 changes: 51 additions & 0 deletions programs/voter-stake-registry/src/instructions/unlock_deposit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::error::*;
use crate::state::*;
use anchor_lang::prelude::*;

#[derive(Accounts)]
pub struct UnlockDeposit<'info> {
pub registrar: AccountLoader<'info, Registrar>,
// checking the PDA address it just an extra precaution,
// the other constraints must be exhaustive
#[account(
mut,
seeds = [registrar.key().as_ref(), b"voter".as_ref(), voter_authority.key().as_ref()],
bump = voter.load()?.voter_bump,
has_one = voter_authority,
has_one = registrar)]
pub voter: AccountLoader<'info, Voter>,
pub voter_authority: Signer<'info>,
/// Authority for making a grant to this voter account
///
/// Instruction validates grant_authority is the VotingMintConfig.grant_authority or
/// Registrar.realm_authority.
pub grant_authority: Signer<'info>,
}

pub fn unlock_deposit(ctx: Context<UnlockDeposit>, deposit_entry_index: u8) -> Result<()> {
// Load accounts.
let registrar = &ctx.accounts.registrar.load()?;
let voter = &mut ctx.accounts.voter.load_mut()?;

let deposit_entry = voter.active_deposit_mut(deposit_entry_index)?;
// Get the grant_authority for the DepositEntry
let mint_idx = deposit_entry.voting_mint_config_idx;
let mint_config: &VotingMintConfig = &registrar.voting_mints[mint_idx as usize];
let grant_authority = ctx.accounts.grant_authority.key();

// Validate the VotingMintConfig was initialized and is in use
require!(mint_config.in_use(), VsrError::MintConfigNotUsed);

// Validate grant_authority is appropriate to unlock deposit
require!(
(grant_authority == registrar.realm_authority
|| grant_authority == mint_config.grant_authority)
&& grant_authority != Pubkey::default(),
VsrError::BadUnlockDepositAuthority
);

// Change the DepositEntry to unlock all unvested tokens
deposit_entry.unlock_deposit();

Ok(())
}
6 changes: 6 additions & 0 deletions programs/voter-stake-registry/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,4 +218,10 @@ pub mod voter_stake_registry {
pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
instructions::set_time_offset(ctx, time_offset)
}

/// _Requires signing by the VotingMintConfig.grant_authority or Registrar.realm_authority_
/// Makes all tokens in a DepositEntry available for immediate withdrawal.
pub fn unlock_deposit(ctx: Context<UnlockDeposit>, deposit_entry_index: u8) -> Result<()> {
instructions::unlock_deposit(ctx, deposit_entry_index)
}
}
5 changes: 5 additions & 0 deletions programs/voter-stake-registry/src/state/deposit_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,11 @@ impl DepositEntry {
require_eq!(self.vested(curr_ts)?, 0, VsrError::InternalProgramError);
Ok(())
}

/// Makes all unvested tokens vested. Changes the LockUp to None
pub fn unlock_deposit(&mut self) {
self.lockup = Lockup::default();
}
}

#[cfg(test)]
Expand Down
Binary file not shown.
43 changes: 43 additions & 0 deletions programs/voter-stake-registry/tests/program_test/addin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -805,6 +805,49 @@ impl AddinCookie {
.await
.unwrap();
}

#[allow(dead_code)]
pub async fn unlock_deposit(
&self,
registrar: &RegistrarCookie,
voter: &VoterCookie,
voter_authority: &Keypair,
grant_authority: &Keypair,
deposit_entry_index: u8,
) -> Result<(), BanksClientError> {
let data =
anchor_lang::InstructionData::data(&voter_stake_registry::instruction::UnlockDeposit {
deposit_entry_index,
});

let accounts = anchor_lang::ToAccountMetas::to_account_metas(
&voter_stake_registry::accounts::UnlockDeposit {
registrar: registrar.address,
voter: voter.address,
voter_authority: voter_authority.pubkey(),
grant_authority: grant_authority.pubkey(),
},
None,
);

let instructions = vec![Instruction {
program_id: self.program_id,
accounts,
data,
}];

// clone the secrets
let voter_secret = Keypair::from_base58_string(&voter_authority.to_base58_string());
let grant_authority_secret =
Keypair::from_base58_string(&grant_authority.to_base58_string());

self.solana
.process_transaction(
&instructions,
Some(&[&voter_secret, &grant_authority_secret]),
)
.await
}
}

impl VotingMintConfigCookie {
Expand Down
51 changes: 37 additions & 14 deletions programs/voter-stake-registry/tests/program_test/governance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,11 +79,17 @@ impl GovernanceCookie {
&community_token_mint.pubkey.unwrap(),
&payer.pubkey(),
None,
Some(*voter_weight_addin),
Some(
spl_governance::state::realm::GoverningTokenConfigAccountArgs {
voter_weight_addin: Some(*voter_weight_addin),
max_voter_weight_addin: None,
token_type: spl_governance::state::realm_config::GoverningTokenType::Liquid,
},
),
None,
name.to_string(),
0,
spl_governance::state::enums::MintMaxVoteWeightSource::SupplyFraction(10000000000),
spl_governance::state::enums::MintMaxVoterWeightSource::SupplyFraction(10000000000),
)];

let signer = Keypair::from_base58_string(&payer.to_base58_string());
Expand Down Expand Up @@ -166,14 +172,22 @@ impl GovernanceRealmCookie {
&authority.pubkey(),
Some(voter.voter_weight_record),
spl_governance::state::governance::GovernanceConfig {
vote_threshold_percentage:
spl_governance::state::enums::VoteThresholdPercentage::YesVote(50),
community_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
min_community_weight_to_create_proposal: 1000,
min_transaction_hold_up_time: 0,
max_voting_time: 10,
vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
proposal_cool_off_time: 0,
voting_base_time: 10,
community_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
council_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
council_veto_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
min_council_weight_to_create_proposal: 1,
council_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
community_veto_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
voting_cool_off_time: 0,
deposit_exempt_proposal_count: 10,
},
),
];
Expand Down Expand Up @@ -221,14 +235,22 @@ impl GovernanceRealmCookie {
&authority.pubkey(),
Some(voter.voter_weight_record),
spl_governance::state::governance::GovernanceConfig {
vote_threshold_percentage:
spl_governance::state::enums::VoteThresholdPercentage::YesVote(50),
community_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
min_community_weight_to_create_proposal: 1000,
min_transaction_hold_up_time: 0,
max_voting_time: 10,
vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
proposal_cool_off_time: 0,
voting_base_time: 10,
community_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
council_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
council_veto_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
min_council_weight_to_create_proposal: 1,
council_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
community_veto_vote_threshold:
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
voting_cool_off_time: 0,
deposit_exempt_proposal_count: 10,
},
true,
),
Expand Down Expand Up @@ -263,7 +285,7 @@ impl GovernanceRealmCookie {
&self.governance.program_id,
&governance,
&self.community_token_mint.pubkey.unwrap(),
&0u32.to_le_bytes(),
&Pubkey::default(),
);

let instructions = vec![
Expand All @@ -282,7 +304,7 @@ impl GovernanceRealmCookie {
proposal::VoteType::SingleChoice,
vec!["yes".into()],
true,
0,
&Pubkey::default(),
),
spl_governance::instruction::add_signatory(
&self.governance.program_id,
Expand Down Expand Up @@ -367,6 +389,7 @@ impl GovernanceRealmCookie {
) -> std::result::Result<(), BanksClientError> {
let instructions = vec![spl_governance::instruction::relinquish_vote(
&self.governance.program_id,
&self.realm,
&governance,
&proposal.address,
&token_owner_record,
Expand Down
7 changes: 2 additions & 5 deletions programs/voter-stake-registry/tests/program_test/solana.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl SolanaCookie {
*self.program_output.write().unwrap() = super::ProgramOutput::default();

let mut context = self.context.borrow_mut();
let recent_blockhash = context.banks_client.get_latest_blockhash().await.unwrap();

let mut transaction =
Transaction::new_with_payer(&instructions, Some(&context.payer.pubkey()));
Expand All @@ -40,11 +41,7 @@ impl SolanaCookie {
all_signers.extend_from_slice(signers);
}

// This fails when warping is involved - https://gitmemory.com/issue/solana-labs/solana/18201/868325078
// let recent_blockhash = self.context.banks_client.get_recent_blockhash().await.unwrap();

transaction.sign(&all_signers, context.last_blockhash);

transaction.sign(&all_signers, recent_blockhash);
context
.banks_client
.process_transaction_with_commitment(
Expand Down
35 changes: 35 additions & 0 deletions programs/voter-stake-registry/tests/program_test/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ use solana_program::program_error::ProgramError;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Keypair;

use super::SolanaCookie;

#[allow(dead_code)]
pub fn gen_signer_seeds<'a>(nonce: &'a u64, acc_pk: &'a Pubkey) -> [&'a [u8]; 2] {
[acc_pk.as_ref(), bytes_of(nonce)]
Expand Down Expand Up @@ -32,3 +34,36 @@ pub fn create_signer_key_and_nonce(program_id: &Pubkey, acc_pk: &Pubkey) -> (Pub
pub fn clone_keypair(keypair: &Keypair) -> Keypair {
Keypair::from_base58_string(&keypair.to_base58_string())
}

#[derive(Debug, PartialEq)]
pub struct LockupData {
/// time since lockup start (saturating at "duration")
pub time_passed: u64,
/// duration of lockup
pub duration: u64,
pub amount_initially_locked_native: u64,
pub amount_deposited_native: u64,
pub amount_unlocked: u64,
}

#[allow(dead_code)]
pub async fn get_lockup_data(
solana: &SolanaCookie,
voter: Pubkey,
index: u8,
time_offset: i64,
) -> LockupData {
let now = solana.get_clock().await.unix_timestamp + time_offset;
let voter = solana
.get_account::<voter_stake_registry::state::Voter>(voter)
.await;
let d = voter.deposits[index as usize];
let duration = d.lockup.periods_total().unwrap() * d.lockup.kind.period_secs();
LockupData {
time_passed: (duration - d.lockup.seconds_left(now)) as u64,
duration,
amount_initially_locked_native: d.amount_initially_locked_native,
amount_deposited_native: d.amount_deposited_native,
amount_unlocked: d.amount_unlocked(now),
}
}
6 changes: 0 additions & 6 deletions programs/voter-stake-registry/tests/test_basic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,6 @@ async fn test_basic() -> Result<(), TransportError> {
.create_voter(&registrar, &token_owner_record, &voter_authority, &payer)
.await;

// create the voter again, should have no effect
context
.addin
.create_voter(&registrar, &token_owner_record, &voter_authority, &payer)
.await;

// test deposit and withdraw

let reference_account = context.users[1].token_accounts[0];
Expand Down
Loading