Merge 5472db4e02
into 62f96fee96
This commit is contained in:
commit
68c59c38f5
File diff suppressed because it is too large
Load Diff
|
@ -33,7 +33,7 @@ Users can:
|
||||||
# Development
|
# Development
|
||||||
|
|
||||||
## Rust
|
## 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 rust based tests - `cargo test-sbf`
|
||||||
* `run-generate-anchor-types.sh` generates latest anchor types file and writes to `./voter_stake_registry.ts`
|
* `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`
|
* To install the typescript client, do - `yarn add @blockworks-foundation/voter-stake-registry-client`
|
||||||
|
|
|
@ -36,16 +36,16 @@ bytemuck = "1.9.1"
|
||||||
# Recently the discriminator for new VoterWeightRecord accounts has changed, and upgrading
|
# Recently the discriminator for new VoterWeightRecord accounts has changed, and upgrading
|
||||||
# this dependency here without also upgrading the spl-governance program instance beforehand
|
# 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.
|
# 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 = { version = "=3.1.1", features = ["no-entrypoint"] }
|
||||||
spl-governance-addin-api = "=0.1.1"
|
spl-governance-addin-api = "=0.1.3"
|
||||||
|
|
||||||
solana-program = "1.14.10"
|
solana-program = "1.14.22"
|
||||||
static_assertions = "1.1"
|
static_assertions = "1.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
solana-sdk = "1.14.10"
|
solana-sdk = "1.14.22"
|
||||||
solana-program-test = "1.14.10"
|
solana-program-test = "1.14.22"
|
||||||
solana-logger = "1.14.10"
|
solana-logger = "1.14.22"
|
||||||
spl-token = { version = "^3.0.0", features = ["no-entrypoint"] }
|
spl-token = { version = "^3.0.0", features = ["no-entrypoint"] }
|
||||||
spl-associated-token-account = { version = "^1.0.3", features = ["no-entrypoint"] }
|
spl-associated-token-account = { version = "^1.0.3", features = ["no-entrypoint"] }
|
||||||
bytemuck = "^1.7.2"
|
bytemuck = "^1.7.2"
|
||||||
|
@ -54,3 +54,4 @@ bincode = "^1.3.1"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
base64 = "0.13.0"
|
base64 = "0.13.0"
|
||||||
|
tempfile = "=3.6.0"
|
|
@ -116,4 +116,10 @@ pub enum VsrError {
|
||||||
// 6037 / 0x1795
|
// 6037 / 0x1795
|
||||||
#[msg("")]
|
#[msg("")]
|
||||||
InvalidTimestampArguments,
|
InvalidTimestampArguments,
|
||||||
|
// 6038 / 0x1796
|
||||||
|
#[msg("")]
|
||||||
|
BadUnlockDepositAuthority,
|
||||||
|
// 6039 / 0x1797
|
||||||
|
#[msg("")]
|
||||||
|
MintConfigNotUsed,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub use internal_transfer_unlocked::*;
|
||||||
pub use log_voter_info::*;
|
pub use log_voter_info::*;
|
||||||
pub use reset_lockup::*;
|
pub use reset_lockup::*;
|
||||||
pub use set_time_offset::*;
|
pub use set_time_offset::*;
|
||||||
|
pub use unlock_deposit::*;
|
||||||
pub use update_max_vote_weight::*;
|
pub use update_max_vote_weight::*;
|
||||||
pub use update_voter_weight_record::*;
|
pub use update_voter_weight_record::*;
|
||||||
pub use withdraw::*;
|
pub use withdraw::*;
|
||||||
|
@ -30,6 +31,7 @@ mod internal_transfer_unlocked;
|
||||||
mod log_voter_info;
|
mod log_voter_info;
|
||||||
mod reset_lockup;
|
mod reset_lockup;
|
||||||
mod set_time_offset;
|
mod set_time_offset;
|
||||||
|
mod unlock_deposit;
|
||||||
mod update_max_vote_weight;
|
mod update_max_vote_weight;
|
||||||
mod update_voter_weight_record;
|
mod update_voter_weight_record;
|
||||||
mod withdraw;
|
mod withdraw;
|
||||||
|
|
|
@ -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 = ®istrar.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(())
|
||||||
|
}
|
|
@ -218,4 +218,10 @@ pub mod voter_stake_registry {
|
||||||
pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
|
pub fn set_time_offset(ctx: Context<SetTimeOffset>, time_offset: i64) -> Result<()> {
|
||||||
instructions::set_time_offset(ctx, time_offset)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -376,6 +376,11 @@ impl DepositEntry {
|
||||||
require_eq!(self.vested(curr_ts)?, 0, VsrError::InternalProgramError);
|
require_eq!(self.vested(curr_ts)?, 0, VsrError::InternalProgramError);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Makes all unvested tokens vested. Changes the LockUp to None
|
||||||
|
pub fn unlock_deposit(&mut self) {
|
||||||
|
self.lockup = Lockup::default();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Binary file not shown.
|
@ -805,6 +805,49 @@ impl AddinCookie {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.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 {
|
impl VotingMintConfigCookie {
|
||||||
|
|
|
@ -79,11 +79,17 @@ impl GovernanceCookie {
|
||||||
&community_token_mint.pubkey.unwrap(),
|
&community_token_mint.pubkey.unwrap(),
|
||||||
&payer.pubkey(),
|
&payer.pubkey(),
|
||||||
None,
|
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,
|
None,
|
||||||
name.to_string(),
|
name.to_string(),
|
||||||
0,
|
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());
|
let signer = Keypair::from_base58_string(&payer.to_base58_string());
|
||||||
|
@ -166,14 +172,22 @@ impl GovernanceRealmCookie {
|
||||||
&authority.pubkey(),
|
&authority.pubkey(),
|
||||||
Some(voter.voter_weight_record),
|
Some(voter.voter_weight_record),
|
||||||
spl_governance::state::governance::GovernanceConfig {
|
spl_governance::state::governance::GovernanceConfig {
|
||||||
vote_threshold_percentage:
|
community_vote_threshold:
|
||||||
spl_governance::state::enums::VoteThresholdPercentage::YesVote(50),
|
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
|
||||||
min_community_weight_to_create_proposal: 1000,
|
min_community_weight_to_create_proposal: 1000,
|
||||||
min_transaction_hold_up_time: 0,
|
min_transaction_hold_up_time: 0,
|
||||||
max_voting_time: 10,
|
voting_base_time: 10,
|
||||||
vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
|
community_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
|
||||||
proposal_cool_off_time: 0,
|
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,
|
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,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
@ -221,14 +235,22 @@ impl GovernanceRealmCookie {
|
||||||
&authority.pubkey(),
|
&authority.pubkey(),
|
||||||
Some(voter.voter_weight_record),
|
Some(voter.voter_weight_record),
|
||||||
spl_governance::state::governance::GovernanceConfig {
|
spl_governance::state::governance::GovernanceConfig {
|
||||||
vote_threshold_percentage:
|
community_vote_threshold:
|
||||||
spl_governance::state::enums::VoteThresholdPercentage::YesVote(50),
|
spl_governance::state::enums::VoteThreshold::YesVotePercentage(50),
|
||||||
min_community_weight_to_create_proposal: 1000,
|
min_community_weight_to_create_proposal: 1000,
|
||||||
min_transaction_hold_up_time: 0,
|
min_transaction_hold_up_time: 0,
|
||||||
max_voting_time: 10,
|
voting_base_time: 10,
|
||||||
vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
|
community_vote_tipping: spl_governance::state::enums::VoteTipping::Disabled,
|
||||||
proposal_cool_off_time: 0,
|
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,
|
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,
|
true,
|
||||||
),
|
),
|
||||||
|
@ -263,7 +285,7 @@ impl GovernanceRealmCookie {
|
||||||
&self.governance.program_id,
|
&self.governance.program_id,
|
||||||
&governance,
|
&governance,
|
||||||
&self.community_token_mint.pubkey.unwrap(),
|
&self.community_token_mint.pubkey.unwrap(),
|
||||||
&0u32.to_le_bytes(),
|
&Pubkey::default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let instructions = vec![
|
let instructions = vec![
|
||||||
|
@ -282,7 +304,7 @@ impl GovernanceRealmCookie {
|
||||||
proposal::VoteType::SingleChoice,
|
proposal::VoteType::SingleChoice,
|
||||||
vec!["yes".into()],
|
vec!["yes".into()],
|
||||||
true,
|
true,
|
||||||
0,
|
&Pubkey::default(),
|
||||||
),
|
),
|
||||||
spl_governance::instruction::add_signatory(
|
spl_governance::instruction::add_signatory(
|
||||||
&self.governance.program_id,
|
&self.governance.program_id,
|
||||||
|
@ -367,6 +389,7 @@ impl GovernanceRealmCookie {
|
||||||
) -> std::result::Result<(), BanksClientError> {
|
) -> std::result::Result<(), BanksClientError> {
|
||||||
let instructions = vec![spl_governance::instruction::relinquish_vote(
|
let instructions = vec![spl_governance::instruction::relinquish_vote(
|
||||||
&self.governance.program_id,
|
&self.governance.program_id,
|
||||||
|
&self.realm,
|
||||||
&governance,
|
&governance,
|
||||||
&proposal.address,
|
&proposal.address,
|
||||||
&token_owner_record,
|
&token_owner_record,
|
||||||
|
|
|
@ -30,6 +30,7 @@ impl SolanaCookie {
|
||||||
*self.program_output.write().unwrap() = super::ProgramOutput::default();
|
*self.program_output.write().unwrap() = super::ProgramOutput::default();
|
||||||
|
|
||||||
let mut context = self.context.borrow_mut();
|
let mut context = self.context.borrow_mut();
|
||||||
|
let recent_blockhash = context.banks_client.get_latest_blockhash().await.unwrap();
|
||||||
|
|
||||||
let mut transaction =
|
let mut transaction =
|
||||||
Transaction::new_with_payer(&instructions, Some(&context.payer.pubkey()));
|
Transaction::new_with_payer(&instructions, Some(&context.payer.pubkey()));
|
||||||
|
@ -40,11 +41,7 @@ impl SolanaCookie {
|
||||||
all_signers.extend_from_slice(signers);
|
all_signers.extend_from_slice(signers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This fails when warping is involved - https://gitmemory.com/issue/solana-labs/solana/18201/868325078
|
transaction.sign(&all_signers, recent_blockhash);
|
||||||
// let recent_blockhash = self.context.banks_client.get_recent_blockhash().await.unwrap();
|
|
||||||
|
|
||||||
transaction.sign(&all_signers, context.last_blockhash);
|
|
||||||
|
|
||||||
context
|
context
|
||||||
.banks_client
|
.banks_client
|
||||||
.process_transaction_with_commitment(
|
.process_transaction_with_commitment(
|
||||||
|
|
|
@ -3,6 +3,8 @@ use solana_program::program_error::ProgramError;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
|
|
||||||
|
use super::SolanaCookie;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub fn gen_signer_seeds<'a>(nonce: &'a u64, acc_pk: &'a Pubkey) -> [&'a [u8]; 2] {
|
pub fn gen_signer_seeds<'a>(nonce: &'a u64, acc_pk: &'a Pubkey) -> [&'a [u8]; 2] {
|
||||||
[acc_pk.as_ref(), bytes_of(nonce)]
|
[acc_pk.as_ref(), bytes_of(nonce)]
|
||||||
|
@ -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 {
|
pub fn clone_keypair(keypair: &Keypair) -> Keypair {
|
||||||
Keypair::from_base58_string(&keypair.to_base58_string())
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -72,12 +72,6 @@ async fn test_basic() -> Result<(), TransportError> {
|
||||||
.create_voter(®istrar, &token_owner_record, &voter_authority, &payer)
|
.create_voter(®istrar, &token_owner_record, &voter_authority, &payer)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// create the voter again, should have no effect
|
|
||||||
context
|
|
||||||
.addin
|
|
||||||
.create_voter(®istrar, &token_owner_record, &voter_authority, &payer)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// test deposit and withdraw
|
// test deposit and withdraw
|
||||||
|
|
||||||
let reference_account = context.users[1].token_accounts[0];
|
let reference_account = context.users[1].token_accounts[0];
|
||||||
|
|
|
@ -1,36 +1,13 @@
|
||||||
use anchor_spl::token::TokenAccount;
|
use anchor_spl::token::TokenAccount;
|
||||||
use program_test::*;
|
use program_test::*;
|
||||||
use solana_program_test::*;
|
use solana_program_test::*;
|
||||||
use solana_sdk::{pubkey::Pubkey, signature::Keypair, signer::Signer, transport::TransportError};
|
use solana_sdk::{signature::Keypair, signer::Signer, transport::TransportError};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use voter_stake_registry::state::LockupKind;
|
use voter_stake_registry::state::LockupKind;
|
||||||
|
|
||||||
mod program_test;
|
mod program_test;
|
||||||
|
|
||||||
async fn get_lockup_data(
|
|
||||||
solana: &SolanaCookie,
|
|
||||||
voter: Pubkey,
|
|
||||||
index: u8,
|
|
||||||
time_offset: i64,
|
|
||||||
) -> (u64, u64, u64, u64, u64) {
|
|
||||||
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();
|
|
||||||
(
|
|
||||||
// time since lockup start (saturating at "duration")
|
|
||||||
(duration - d.lockup.seconds_left(now)) as u64,
|
|
||||||
// duration of lockup
|
|
||||||
duration,
|
|
||||||
d.amount_initially_locked_native,
|
|
||||||
d.amount_deposited_native,
|
|
||||||
d.amount_unlocked(now),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unaligned_references)]
|
#[allow(unaligned_references)]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_reset_lockup() -> Result<(), TransportError> {
|
async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
|
@ -132,24 +109,78 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
deposit(7, 80).await.unwrap();
|
deposit(7, 80).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 3 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
deposit(7, 10).await.unwrap();
|
deposit(7, 10).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 3 * day, 90, 90, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(7, 2, LockupKind::Daily)
|
reset_lockup(7, 2, LockupKind::Daily)
|
||||||
.await
|
.await
|
||||||
.expect_err("can't relock for less periods");
|
.expect_err("can't relock for less periods");
|
||||||
reset_lockup(7, 3, LockupKind::Daily).await.unwrap(); // just resets start to current timestamp
|
reset_lockup(7, 3, LockupKind::Daily).await.unwrap(); // just resets start to current timestamp
|
||||||
assert_eq!(lockup_status(7).await, (0, 3 * day, 90, 90, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// advance more than a day
|
// advance more than a day
|
||||||
advance_time(day + hour).await;
|
advance_time(day + hour).await;
|
||||||
context.solana.advance_clock_by_slots(2).await;
|
context.solana.advance_clock_by_slots(2).await;
|
||||||
|
|
||||||
assert_eq!(lockup_status(7).await, (day + hour, 3 * day, 90, 90, 30));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: day + hour,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 30
|
||||||
|
}
|
||||||
|
);
|
||||||
deposit(7, 10).await.unwrap();
|
deposit(7, 10).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (hour, 2 * day, 70, 100, 30));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: hour,
|
||||||
|
duration: 2 * day,
|
||||||
|
amount_initially_locked_native: 70,
|
||||||
|
amount_deposited_native: 100,
|
||||||
|
amount_unlocked: 30
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(7, 10, LockupKind::Daily).await.unwrap();
|
reset_lockup(7, 10, LockupKind::Daily).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 10 * day, 100, 100, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 10 * day,
|
||||||
|
amount_initially_locked_native: 100,
|
||||||
|
amount_deposited_native: 100,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// advance four more days
|
// advance four more days
|
||||||
advance_time(4 * day + hour).await;
|
advance_time(4 * day + hour).await;
|
||||||
|
@ -157,20 +188,50 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lockup_status(7).await,
|
lockup_status(7).await,
|
||||||
(4 * day + hour, 10 * day, 100, 100, 40)
|
LockupData {
|
||||||
|
time_passed: 4 * day + hour,
|
||||||
|
duration: 10 * day,
|
||||||
|
amount_initially_locked_native: 100,
|
||||||
|
amount_deposited_native: 100,
|
||||||
|
amount_unlocked: 40
|
||||||
|
}
|
||||||
);
|
);
|
||||||
withdraw(7, 20).await.unwrap(); // partially withdraw vested
|
withdraw(7, 20).await.unwrap(); // partially withdraw vested
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lockup_status(7).await,
|
lockup_status(7).await,
|
||||||
(4 * day + hour, 10 * day, 100, 80, 20)
|
LockupData {
|
||||||
|
time_passed: 4 * day + hour,
|
||||||
|
duration: 10 * day,
|
||||||
|
amount_initially_locked_native: 100,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 20
|
||||||
|
}
|
||||||
);
|
);
|
||||||
reset_lockup(7, 5, LockupKind::Daily)
|
reset_lockup(7, 5, LockupKind::Daily)
|
||||||
.await
|
.await
|
||||||
.expect_err("can't relock for less periods");
|
.expect_err("can't relock for less periods");
|
||||||
reset_lockup(7, 6, LockupKind::Daily).await.unwrap();
|
reset_lockup(7, 6, LockupKind::Daily).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 6 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 6 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(7, 8, LockupKind::Daily).await.unwrap();
|
reset_lockup(7, 8, LockupKind::Daily).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 8 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 8 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// advance three more days
|
// advance three more days
|
||||||
advance_time(3 * day + hour).await;
|
advance_time(3 * day + hour).await;
|
||||||
|
@ -178,20 +239,62 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
lockup_status(7).await,
|
lockup_status(7).await,
|
||||||
(3 * day + hour, 8 * day, 80, 80, 30)
|
LockupData {
|
||||||
|
time_passed: 3 * day + hour,
|
||||||
|
duration: 8 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 30
|
||||||
|
}
|
||||||
);
|
);
|
||||||
deposit(7, 10).await.unwrap();
|
deposit(7, 10).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (hour, 5 * day, 60, 90, 30));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: hour,
|
||||||
|
duration: 5 * day,
|
||||||
|
amount_initially_locked_native: 60,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 30
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
context.solana.advance_clock_by_slots(2).await; // avoid deposit and withdraw in one slot
|
context.solana.advance_clock_by_slots(2).await; // avoid deposit and withdraw in one slot
|
||||||
|
|
||||||
withdraw(7, 20).await.unwrap(); // partially withdraw vested
|
withdraw(7, 20).await.unwrap(); // partially withdraw vested
|
||||||
assert_eq!(lockup_status(7).await, (hour, 5 * day, 60, 70, 10));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: hour,
|
||||||
|
duration: 5 * day,
|
||||||
|
amount_initially_locked_native: 60,
|
||||||
|
amount_deposited_native: 70,
|
||||||
|
amount_unlocked: 10
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(7, 10, LockupKind::Daily).await.unwrap();
|
reset_lockup(7, 10, LockupKind::Daily).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 10 * day, 70, 70, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 10 * day,
|
||||||
|
amount_initially_locked_native: 70,
|
||||||
|
amount_deposited_native: 70,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
reset_lockup(7, 1, LockupKind::Monthly).await.unwrap();
|
reset_lockup(7, 1, LockupKind::Monthly).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 1 * month, 70, 70, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 1 * month,
|
||||||
|
amount_initially_locked_native: 70,
|
||||||
|
amount_deposited_native: 70,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
reset_lockup(7, 31, LockupKind::Daily)
|
reset_lockup(7, 31, LockupKind::Daily)
|
||||||
.await
|
.await
|
||||||
|
@ -203,7 +306,16 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.expect_err("period shortnend");
|
.expect_err("period shortnend");
|
||||||
reset_lockup(7, 31, LockupKind::Cliff).await.unwrap();
|
reset_lockup(7, 31, LockupKind::Cliff).await.unwrap();
|
||||||
assert_eq!(lockup_status(7).await, (0, 31 * day, 70, 70, 0));
|
assert_eq!(
|
||||||
|
lockup_status(7).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 31 * day,
|
||||||
|
amount_initially_locked_native: 70,
|
||||||
|
amount_deposited_native: 70,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// tests for cliff vesting
|
// tests for cliff vesting
|
||||||
addin
|
addin
|
||||||
|
@ -221,37 +333,127 @@ async fn test_reset_lockup() -> Result<(), TransportError> {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
deposit(5, 80).await.unwrap();
|
deposit(5, 80).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 3 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(5, 2, LockupKind::Cliff)
|
reset_lockup(5, 2, LockupKind::Cliff)
|
||||||
.await
|
.await
|
||||||
.expect_err("can't relock for less periods");
|
.expect_err("can't relock for less periods");
|
||||||
reset_lockup(5, 3, LockupKind::Cliff).await.unwrap(); // just resets start to current timestamp
|
reset_lockup(5, 3, LockupKind::Cliff).await.unwrap(); // just resets start to current timestamp
|
||||||
assert_eq!(lockup_status(5).await, (0, 3 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(5, 4, LockupKind::Cliff).await.unwrap();
|
reset_lockup(5, 4, LockupKind::Cliff).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 4 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 4 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// advance to end of cliff
|
// advance to end of cliff
|
||||||
advance_time(4 * day + hour).await;
|
advance_time(4 * day + hour).await;
|
||||||
context.solana.advance_clock_by_slots(2).await;
|
context.solana.advance_clock_by_slots(2).await;
|
||||||
|
|
||||||
assert_eq!(lockup_status(5).await, (4 * day, 4 * day, 80, 80, 80));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 4 * day,
|
||||||
|
duration: 4 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 80
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(5, 1, LockupKind::Cliff).await.unwrap();
|
reset_lockup(5, 1, LockupKind::Cliff).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 1 * day, 80, 80, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 1 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
withdraw(5, 10).await.expect_err("nothing unlocked");
|
withdraw(5, 10).await.expect_err("nothing unlocked");
|
||||||
|
|
||||||
// advance to end of cliff again
|
// advance to end of cliff again
|
||||||
advance_time(day + hour).await;
|
advance_time(day + hour).await;
|
||||||
context.solana.advance_clock_by_slots(2).await;
|
context.solana.advance_clock_by_slots(2).await;
|
||||||
|
|
||||||
assert_eq!(lockup_status(5).await, (day, day, 80, 80, 80));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: day,
|
||||||
|
duration: day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 80
|
||||||
|
}
|
||||||
|
);
|
||||||
withdraw(5, 10).await.unwrap();
|
withdraw(5, 10).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (day, day, 80, 70, 70));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: day,
|
||||||
|
duration: day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 70,
|
||||||
|
amount_unlocked: 70
|
||||||
|
}
|
||||||
|
);
|
||||||
deposit(5, 5).await.unwrap();
|
deposit(5, 5).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 0, 5, 75, 75));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 0,
|
||||||
|
amount_initially_locked_native: 5,
|
||||||
|
amount_deposited_native: 75,
|
||||||
|
amount_unlocked: 75
|
||||||
|
}
|
||||||
|
);
|
||||||
reset_lockup(5, 1, LockupKind::Cliff).await.unwrap();
|
reset_lockup(5, 1, LockupKind::Cliff).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 1 * day, 75, 75, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 1 * day,
|
||||||
|
amount_initially_locked_native: 75,
|
||||||
|
amount_deposited_native: 75,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
deposit(5, 15).await.unwrap();
|
deposit(5, 15).await.unwrap();
|
||||||
assert_eq!(lockup_status(5).await, (0, 1 * day, 90, 90, 0));
|
assert_eq!(
|
||||||
|
lockup_status(5).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 1 * day,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,201 @@
|
||||||
|
use anchor_spl::token::TokenAccount;
|
||||||
|
use program_test::*;
|
||||||
|
use solana_program_test::*;
|
||||||
|
use solana_sdk::{signature::Keypair, signer::Signer, transport::TransportError};
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use voter_stake_registry::state::LockupKind;
|
||||||
|
|
||||||
|
mod program_test;
|
||||||
|
|
||||||
|
#[allow(unaligned_references)]
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_unlock_deposit() -> Result<(), TransportError> {
|
||||||
|
let context = TestContext::new().await;
|
||||||
|
let addin = &context.addin;
|
||||||
|
|
||||||
|
let payer = &context.users[0].key;
|
||||||
|
let realm_authority = Keypair::new();
|
||||||
|
let realm = context
|
||||||
|
.governance
|
||||||
|
.create_realm(
|
||||||
|
"testrealm",
|
||||||
|
realm_authority.pubkey(),
|
||||||
|
&context.mints[0],
|
||||||
|
&payer,
|
||||||
|
&context.addin.program_id,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let voter_authority = &context.users[1].key;
|
||||||
|
let token_owner_record = realm
|
||||||
|
.create_token_owner_record(voter_authority.pubkey(), &payer)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let registrar = addin
|
||||||
|
.create_registrar(&realm, &realm_authority, payer)
|
||||||
|
.await;
|
||||||
|
let mngo_voting_mint = addin
|
||||||
|
.configure_voting_mint(
|
||||||
|
®istrar,
|
||||||
|
&realm_authority,
|
||||||
|
payer,
|
||||||
|
0,
|
||||||
|
&context.mints[0],
|
||||||
|
0,
|
||||||
|
1.0,
|
||||||
|
0.0,
|
||||||
|
5 * 365 * 24 * 60 * 60,
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let voter = addin
|
||||||
|
.create_voter(®istrar, &token_owner_record, &voter_authority, &payer)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let reference_account = context.users[1].token_accounts[0];
|
||||||
|
let withdraw = |index: u8, amount: u64| {
|
||||||
|
addin.withdraw(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&mngo_voting_mint,
|
||||||
|
&voter_authority,
|
||||||
|
reference_account,
|
||||||
|
index,
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let deposit = |index: u8, amount: u64| {
|
||||||
|
addin.deposit(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&mngo_voting_mint,
|
||||||
|
&voter_authority,
|
||||||
|
reference_account,
|
||||||
|
index,
|
||||||
|
amount,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let unlock_deposit = |index: u8| {
|
||||||
|
addin.unlock_deposit(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&voter_authority,
|
||||||
|
&realm_authority,
|
||||||
|
index,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let time_offset = Arc::new(RefCell::new(0i64));
|
||||||
|
let lockup_status =
|
||||||
|
|index: u8| get_lockup_data(&context.solana, voter.address, index, *time_offset.borrow());
|
||||||
|
|
||||||
|
let day = 24 * 60 * 60;
|
||||||
|
|
||||||
|
// Test bad grant_authority
|
||||||
|
let deposit_entry_index = 1;
|
||||||
|
addin
|
||||||
|
.create_deposit_entry(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&voter_authority,
|
||||||
|
&mngo_voting_mint,
|
||||||
|
deposit_entry_index,
|
||||||
|
LockupKind::Daily,
|
||||||
|
None,
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
deposit(deposit_entry_index, 80).await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
lockup_status(deposit_entry_index).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
addin
|
||||||
|
.unlock_deposit(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&voter_authority,
|
||||||
|
&voter_authority,
|
||||||
|
deposit_entry_index,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.expect_err("BadUnlockDepositAuthority");
|
||||||
|
|
||||||
|
// tests for daily vesting
|
||||||
|
let deposit_entry_index = 2;
|
||||||
|
addin
|
||||||
|
.create_deposit_entry(
|
||||||
|
®istrar,
|
||||||
|
&voter,
|
||||||
|
&voter_authority,
|
||||||
|
&mngo_voting_mint,
|
||||||
|
deposit_entry_index,
|
||||||
|
LockupKind::Daily,
|
||||||
|
None,
|
||||||
|
3,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
deposit(deposit_entry_index, 80).await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
lockup_status(deposit_entry_index).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 80,
|
||||||
|
amount_deposited_native: 80,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
deposit(deposit_entry_index, 10).await.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
lockup_status(deposit_entry_index).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 3 * day,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
unlock_deposit(deposit_entry_index).await?;
|
||||||
|
assert_eq!(
|
||||||
|
lockup_status(deposit_entry_index).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 0,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 90,
|
||||||
|
amount_unlocked: 90
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
context.solana.advance_clock_by_slots(2).await; // avoid deposit and withdraw in one slot
|
||||||
|
|
||||||
|
withdraw(deposit_entry_index, 90).await.unwrap(); // withdraw all previously locked tokens
|
||||||
|
assert_eq!(
|
||||||
|
lockup_status(deposit_entry_index).await,
|
||||||
|
LockupData {
|
||||||
|
time_passed: 0,
|
||||||
|
duration: 0,
|
||||||
|
amount_initially_locked_native: 90,
|
||||||
|
amount_deposited_native: 0,
|
||||||
|
amount_unlocked: 0
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
[toolchain]
|
||||||
|
channel = "1.65"
|
||||||
|
components = ["rustfmt", "clippy"]
|
Loading…
Reference in New Issue