Governance: Change timestamps and time periods to UnixTimestamp (#1984)
* feat: change signing_off_at and closed_at to UnixTimestamp * feat: change executing_at to UnixTimestamp * feat: change voting_at and voting_completed_at to UnixTimestamp * feat: change draft_at to UnixTimestamp * chore: cleanup slot/timestamp comments * chore: fix merge conflicts
This commit is contained in:
parent
3c561381fd
commit
775e536842
|
@ -133,7 +133,7 @@ pub enum GovernanceInstruction {
|
|||
transfer_upgrade_authority: bool,
|
||||
},
|
||||
|
||||
/// Creates Proposal account for Instructions that will be executed at various slots in the future
|
||||
/// Creates Proposal account for Instructions that will be executed at some point in the future
|
||||
///
|
||||
/// 0. `[writable]` Proposal account. PDA seeds ['governance',governance, governing_token_mint, proposal_index]
|
||||
/// 1. `[writable]` Governance account
|
||||
|
@ -202,8 +202,8 @@ pub enum GovernanceInstruction {
|
|||
/// Instruction index to be inserted at.
|
||||
index: u16,
|
||||
#[allow(dead_code)]
|
||||
/// Slot waiting time between vote period ending and this being eligible for execution
|
||||
hold_up_time: u64,
|
||||
/// Waiting time (in seconds) between vote period ending and this being eligible for execution
|
||||
hold_up_time: u32,
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Instruction Data
|
||||
|
@ -907,7 +907,7 @@ pub fn insert_instruction(
|
|||
payer: &Pubkey,
|
||||
// Args
|
||||
index: u16,
|
||||
hold_up_time: u64,
|
||||
hold_up_time: u32,
|
||||
instruction: InstructionData,
|
||||
) -> Instruction {
|
||||
let proposal_instruction_address =
|
||||
|
|
|
@ -37,7 +37,7 @@ pub fn process_cancel_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) ->
|
|||
token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
|
||||
|
||||
proposal_data.state = ProposalState::Cancelled;
|
||||
proposal_data.closed_at = Some(clock.slot);
|
||||
proposal_data.closed_at = Some(clock.unix_timestamp);
|
||||
|
||||
proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ pub fn process_cast_vote(
|
|||
governance_info.key,
|
||||
governing_token_mint_info.key,
|
||||
)?;
|
||||
proposal_data.assert_can_cast_vote(&governance_data.config, clock.slot)?;
|
||||
proposal_data.assert_can_cast_vote(&governance_data.config, clock.unix_timestamp)?;
|
||||
|
||||
let mut token_owner_record_data = get_token_owner_record_data_for_realm_and_governing_mint(
|
||||
program_id,
|
||||
|
@ -105,7 +105,11 @@ pub fn process_cast_vote(
|
|||
};
|
||||
|
||||
let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?;
|
||||
proposal_data.try_tip_vote(governing_token_supply, &governance_data.config, clock.slot);
|
||||
proposal_data.try_tip_vote(
|
||||
governing_token_supply,
|
||||
&governance_data.config,
|
||||
clock.unix_timestamp,
|
||||
);
|
||||
|
||||
proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ pub fn process_create_proposal(
|
|||
name,
|
||||
description_link,
|
||||
|
||||
draft_at: clock.slot,
|
||||
draft_at: clock.unix_timestamp,
|
||||
signing_off_at: None,
|
||||
voting_at: None,
|
||||
voting_completed_at: None,
|
||||
|
|
|
@ -39,7 +39,8 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]
|
|||
proposal_info.key,
|
||||
)?;
|
||||
|
||||
proposal_data.assert_can_execute_instruction(&proposal_instruction_data, clock.slot)?;
|
||||
proposal_data
|
||||
.assert_can_execute_instruction(&proposal_instruction_data, clock.unix_timestamp)?;
|
||||
|
||||
// Execute instruction with Governance PDA as signer
|
||||
let instruction = Instruction::from(&proposal_instruction_data.instruction);
|
||||
|
@ -59,7 +60,7 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]
|
|||
|
||||
// Update proposal and instruction accounts
|
||||
if proposal_data.state == ProposalState::Succeeded {
|
||||
proposal_data.executing_at = Some(clock.slot);
|
||||
proposal_data.executing_at = Some(clock.unix_timestamp);
|
||||
proposal_data.state = ProposalState::Executing;
|
||||
}
|
||||
|
||||
|
@ -71,13 +72,13 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]
|
|||
if proposal_data.state == ProposalState::Executing
|
||||
&& proposal_data.instructions_executed_count == proposal_data.instructions_count
|
||||
{
|
||||
proposal_data.closed_at = Some(clock.slot);
|
||||
proposal_data.closed_at = Some(clock.unix_timestamp);
|
||||
proposal_data.state = ProposalState::Completed;
|
||||
}
|
||||
|
||||
proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;
|
||||
|
||||
proposal_instruction_data.executed_at = Some(clock.slot);
|
||||
proposal_instruction_data.executed_at = Some(clock.unix_timestamp);
|
||||
proposal_instruction_data.serialize(&mut *proposal_instruction_info.data.borrow_mut())?;
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -41,7 +41,11 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P
|
|||
|
||||
let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?;
|
||||
|
||||
proposal_data.finalize_vote(governing_token_supply, &governance_data.config, clock.slot)?;
|
||||
proposal_data.finalize_vote(
|
||||
governing_token_supply,
|
||||
&governance_data.config,
|
||||
clock.unix_timestamp,
|
||||
)?;
|
||||
|
||||
proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ pub fn process_insert_instruction(
|
|||
program_id: &Pubkey,
|
||||
accounts: &[AccountInfo],
|
||||
index: u16,
|
||||
hold_up_time: u64,
|
||||
hold_up_time: u32,
|
||||
instruction: InstructionData,
|
||||
) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
@ -73,7 +73,7 @@ pub fn process_insert_instruction(
|
|||
match index.cmp(&proposal_data.instructions_next_index) {
|
||||
Ordering::Greater => return Err(GovernanceError::InvalidInstructionIndex.into()),
|
||||
// If the index is the same as instructions_next_index then we are adding a new instruction
|
||||
// If the index is below instructions_next_index then we are inserting into an existing empty slot
|
||||
// If the index is below instructions_next_index then we are inserting into an existing empty space
|
||||
Ordering::Equal => {
|
||||
proposal_data.instructions_next_index = proposal_data
|
||||
.instructions_next_index
|
||||
|
|
|
@ -41,7 +41,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo])
|
|||
signatory_record_data.serialize(&mut *signatory_record_info.data.borrow_mut())?;
|
||||
|
||||
if proposal_data.signatories_signed_off_count == 0 {
|
||||
proposal_data.signing_off_at = Some(clock.slot);
|
||||
proposal_data.signing_off_at = Some(clock.unix_timestamp);
|
||||
proposal_data.state = ProposalState::SigningOff;
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo])
|
|||
|
||||
// If all Signatories signed off we can start voting
|
||||
if proposal_data.signatories_signed_off_count == proposal_data.signatories_count {
|
||||
proposal_data.voting_at = Some(clock.slot);
|
||||
proposal_data.voting_at = Some(clock.unix_timestamp);
|
||||
proposal_data.state = ProposalState::Voting;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,11 +31,11 @@ pub struct GovernanceConfig {
|
|||
/// Minimum number of tokens a governance token owner must possess to be able to create a proposal
|
||||
pub min_tokens_to_create_proposal: u16,
|
||||
|
||||
/// Minimum waiting time in slots for an instruction to be executed after proposal is voted on
|
||||
pub min_instruction_hold_up_time: u64,
|
||||
/// Minimum waiting time in seconds for an instruction to be executed after proposal is voted on
|
||||
pub min_instruction_hold_up_time: u32,
|
||||
|
||||
/// Time limit in slots for proposal to be open for voting
|
||||
pub max_voting_time: u64,
|
||||
/// Time limit in seconds for proposal to be open for voting
|
||||
pub max_voting_time: u32,
|
||||
}
|
||||
|
||||
/// Governance Account
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
//! Proposal Account
|
||||
|
||||
use solana_program::clock::UnixTimestamp;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, epoch_schedule::Slot, program_error::ProgramError,
|
||||
program_pack::IsInitialized, pubkey::Pubkey,
|
||||
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
use crate::tools::account::get_account_data;
|
||||
|
@ -54,22 +55,22 @@ pub struct Proposal {
|
|||
pub no_votes_count: u64,
|
||||
|
||||
/// When the Proposal was created and entered Draft state
|
||||
pub draft_at: Slot,
|
||||
pub draft_at: UnixTimestamp,
|
||||
|
||||
/// When Signatories started signing off the Proposal
|
||||
pub signing_off_at: Option<Slot>,
|
||||
pub signing_off_at: Option<UnixTimestamp>,
|
||||
|
||||
/// When the Proposal began voting
|
||||
pub voting_at: Option<Slot>,
|
||||
pub voting_at: Option<UnixTimestamp>,
|
||||
|
||||
/// When the Proposal ended voting and entered either Succeeded or Defeated
|
||||
pub voting_completed_at: Option<Slot>,
|
||||
pub voting_completed_at: Option<UnixTimestamp>,
|
||||
|
||||
/// When the Proposal entered Executing state
|
||||
pub executing_at: Option<Slot>,
|
||||
pub executing_at: Option<UnixTimestamp>,
|
||||
|
||||
/// When the Proposal entered final state Completed or Cancelled and was closed
|
||||
pub closed_at: Option<Slot>,
|
||||
pub closed_at: Option<UnixTimestamp>,
|
||||
|
||||
/// The number of the instructions already executed
|
||||
pub instructions_executed_count: u16,
|
||||
|
@ -135,7 +136,7 @@ impl Proposal {
|
|||
pub fn assert_can_cast_vote(
|
||||
&self,
|
||||
config: &GovernanceConfig,
|
||||
current_slot: Slot,
|
||||
current_unix_timestamp: UnixTimestamp,
|
||||
) -> Result<(), ProgramError> {
|
||||
self.assert_is_voting_state()
|
||||
.map_err(|_| GovernanceError::InvalidStateCannotVote)?;
|
||||
|
@ -144,9 +145,9 @@ impl Proposal {
|
|||
if self
|
||||
.voting_at
|
||||
.unwrap()
|
||||
.checked_add(config.max_voting_time)
|
||||
.checked_add(config.max_voting_time as i64)
|
||||
.unwrap()
|
||||
< current_slot
|
||||
< current_unix_timestamp
|
||||
{
|
||||
return Err(GovernanceError::ProposalVotingTimeExpired.into());
|
||||
}
|
||||
|
@ -158,7 +159,7 @@ impl Proposal {
|
|||
pub fn assert_can_finalize_vote(
|
||||
&self,
|
||||
config: &GovernanceConfig,
|
||||
current_slot: Slot,
|
||||
current_unix_timestamp: UnixTimestamp,
|
||||
) -> Result<(), ProgramError> {
|
||||
self.assert_is_voting_state()
|
||||
.map_err(|_| GovernanceError::InvalidStateCannotFinalize)?;
|
||||
|
@ -167,9 +168,9 @@ impl Proposal {
|
|||
if self
|
||||
.voting_at
|
||||
.unwrap()
|
||||
.checked_add(config.max_voting_time)
|
||||
.checked_add(config.max_voting_time as i64)
|
||||
.unwrap()
|
||||
>= current_slot
|
||||
>= current_unix_timestamp
|
||||
{
|
||||
return Err(GovernanceError::CannotFinalizeVotingInProgress.into());
|
||||
}
|
||||
|
@ -183,12 +184,12 @@ impl Proposal {
|
|||
&mut self,
|
||||
governing_token_supply: u64,
|
||||
config: &GovernanceConfig,
|
||||
current_slot: Slot,
|
||||
current_unix_timestamp: UnixTimestamp,
|
||||
) -> Result<(), ProgramError> {
|
||||
self.assert_can_finalize_vote(config, current_slot)?;
|
||||
self.assert_can_finalize_vote(config, current_unix_timestamp)?;
|
||||
|
||||
self.state = self.get_final_vote_state(governing_token_supply, config);
|
||||
self.voting_completed_at = Some(current_slot);
|
||||
self.voting_completed_at = Some(current_unix_timestamp);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -219,11 +220,11 @@ impl Proposal {
|
|||
&mut self,
|
||||
governing_token_supply: u64,
|
||||
config: &GovernanceConfig,
|
||||
current_slot: Slot,
|
||||
current_unix_timestamp: UnixTimestamp,
|
||||
) {
|
||||
if let Some(tipped_state) = self.try_get_tipped_vote_state(governing_token_supply, config) {
|
||||
self.state = tipped_state;
|
||||
self.voting_completed_at = Some(current_slot);
|
||||
self.voting_completed_at = Some(current_unix_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -282,7 +283,7 @@ impl Proposal {
|
|||
pub fn assert_can_execute_instruction(
|
||||
&self,
|
||||
proposal_instruction_data: &ProposalInstruction,
|
||||
current_slot: Slot,
|
||||
current_unix_timestamp: UnixTimestamp,
|
||||
) -> Result<(), ProgramError> {
|
||||
match self.state {
|
||||
ProposalState::Succeeded | ProposalState::Executing => {}
|
||||
|
@ -299,9 +300,9 @@ impl Proposal {
|
|||
if self
|
||||
.voting_completed_at
|
||||
.unwrap()
|
||||
.checked_add(proposal_instruction_data.hold_up_time)
|
||||
.checked_add(proposal_instruction_data.hold_up_time as i64)
|
||||
.unwrap()
|
||||
>= current_slot
|
||||
>= current_unix_timestamp
|
||||
{
|
||||
return Err(GovernanceError::CannotExecuteInstructionWithinHoldUpTime.into());
|
||||
}
|
||||
|
@ -813,16 +814,16 @@ mod test {
|
|||
let mut governance_config = create_test_governance_config();
|
||||
governance_config.yes_vote_threshold_percentage = test_case.vote_threshold_percentage;
|
||||
|
||||
let current_slot = 15_u64;
|
||||
let current_timestamp = 15_i64;
|
||||
|
||||
// Act
|
||||
proposal.try_tip_vote(test_case.governing_token_supply, &governance_config,current_slot);
|
||||
proposal.try_tip_vote(test_case.governing_token_supply, &governance_config,current_timestamp);
|
||||
|
||||
// Assert
|
||||
assert_eq!(proposal.state,test_case.expected_tipped_state,"CASE: {:?}",test_case);
|
||||
|
||||
if test_case.expected_tipped_state != ProposalState::Voting {
|
||||
assert_eq!(Some(current_slot),proposal.voting_completed_at)
|
||||
assert_eq!(Some(current_timestamp),proposal.voting_completed_at)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -837,14 +838,14 @@ mod test {
|
|||
let mut governance_config = create_test_governance_config();
|
||||
governance_config.yes_vote_threshold_percentage = test_case.vote_threshold_percentage;
|
||||
|
||||
let current_slot = 16_u64;
|
||||
let current_timestamp = 16_i64;
|
||||
|
||||
// Act
|
||||
proposal.finalize_vote(test_case.governing_token_supply, &governance_config,current_slot).unwrap();
|
||||
proposal.finalize_vote(test_case.governing_token_supply, &governance_config,current_timestamp).unwrap();
|
||||
|
||||
// Assert
|
||||
assert_eq!(proposal.state,test_case.expected_finalized_state,"CASE: {:?}",test_case);
|
||||
assert_eq!(Some(current_slot),proposal.voting_completed_at);
|
||||
assert_eq!(Some(current_timestamp),proposal.voting_completed_at);
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -879,11 +880,10 @@ mod test {
|
|||
let mut governance_config = create_test_governance_config();
|
||||
governance_config.yes_vote_threshold_percentage = yes_vote_threshold_percentage;
|
||||
|
||||
let current_slot = 15_u64;
|
||||
|
||||
let current_timestamp = 15_i64;
|
||||
|
||||
// Act
|
||||
proposal.try_tip_vote(governing_token_supply, &governance_config,current_slot);
|
||||
proposal.try_tip_vote(governing_token_supply, &governance_config,current_timestamp);
|
||||
|
||||
// Assert
|
||||
let yes_vote_threshold_count = get_vote_threshold_count(yes_vote_threshold_percentage,governing_token_supply);
|
||||
|
@ -916,10 +916,10 @@ mod test {
|
|||
let mut governance_config = create_test_governance_config();
|
||||
governance_config.yes_vote_threshold_percentage = yes_vote_threshold_percentage;
|
||||
|
||||
let current_slot = 16_u64;
|
||||
let current_timestamp = 16_i64;
|
||||
|
||||
// Act
|
||||
proposal.finalize_vote(governing_token_supply, &governance_config,current_slot).unwrap();
|
||||
proposal.finalize_vote(governing_token_supply, &governance_config,current_timestamp).unwrap();
|
||||
|
||||
// Assert
|
||||
let yes_vote_threshold_count = get_vote_threshold_count(yes_vote_threshold_percentage,governing_token_supply);
|
||||
|
@ -940,11 +940,12 @@ mod test {
|
|||
proposal.state = ProposalState::Voting;
|
||||
let governance_config = create_test_governance_config();
|
||||
|
||||
let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time;
|
||||
let current_timestamp =
|
||||
proposal.voting_at.unwrap() + governance_config.max_voting_time as i64;
|
||||
|
||||
// Act
|
||||
let err = proposal
|
||||
.finalize_vote(100, &governance_config, current_slot)
|
||||
.finalize_vote(100, &governance_config, current_timestamp)
|
||||
.err()
|
||||
.unwrap();
|
||||
|
||||
|
@ -959,10 +960,11 @@ mod test {
|
|||
proposal.state = ProposalState::Voting;
|
||||
let governance_config = create_test_governance_config();
|
||||
|
||||
let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time + 1;
|
||||
let current_timestamp =
|
||||
proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1;
|
||||
|
||||
// Act
|
||||
let result = proposal.finalize_vote(100, &governance_config, current_slot);
|
||||
let result = proposal.finalize_vote(100, &governance_config, current_timestamp);
|
||||
|
||||
// Assert
|
||||
assert_eq!(result, Ok(()));
|
||||
|
@ -975,11 +977,12 @@ mod test {
|
|||
proposal.state = ProposalState::Voting;
|
||||
let governance_config = create_test_governance_config();
|
||||
|
||||
let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time + 1;
|
||||
let current_timestamp =
|
||||
proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1;
|
||||
|
||||
// Act
|
||||
let err = proposal
|
||||
.assert_can_cast_vote(&governance_config, current_slot)
|
||||
.assert_can_cast_vote(&governance_config, current_timestamp)
|
||||
.err()
|
||||
.unwrap();
|
||||
|
||||
|
@ -994,10 +997,11 @@ mod test {
|
|||
proposal.state = ProposalState::Voting;
|
||||
let governance_config = create_test_governance_config();
|
||||
|
||||
let current_slot = proposal.voting_at.unwrap() + governance_config.max_voting_time;
|
||||
let current_timestamp =
|
||||
proposal.voting_at.unwrap() + governance_config.max_voting_time as i64;
|
||||
|
||||
// Act
|
||||
let result = proposal.assert_can_cast_vote(&governance_config, current_slot);
|
||||
let result = proposal.assert_can_cast_vote(&governance_config, current_timestamp);
|
||||
|
||||
// Assert
|
||||
assert_eq!(result, Ok(()));
|
||||
|
|
|
@ -9,7 +9,7 @@ use crate::{
|
|||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo,
|
||||
epoch_schedule::Slot,
|
||||
clock::UnixTimestamp,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
program_pack::IsInitialized,
|
||||
|
@ -86,8 +86,8 @@ pub struct ProposalInstruction {
|
|||
/// The Proposal the instruction belongs to
|
||||
pub proposal: Pubkey,
|
||||
|
||||
/// Minimum waiting time in slots for the instruction to be executed once proposal is voted on
|
||||
pub hold_up_time: u64,
|
||||
/// Minimum waiting time in seconds for the instruction to be executed once proposal is voted on
|
||||
pub hold_up_time: u32,
|
||||
|
||||
/// Instruction to execute
|
||||
/// The instruction will be signed by Governance PDA the Proposal belongs to
|
||||
|
@ -95,12 +95,12 @@ pub struct ProposalInstruction {
|
|||
pub instruction: InstructionData,
|
||||
|
||||
/// Executed at flag
|
||||
pub executed_at: Option<Slot>,
|
||||
pub executed_at: Option<UnixTimestamp>,
|
||||
}
|
||||
|
||||
impl AccountMaxSize for ProposalInstruction {
|
||||
fn get_max_size(&self) -> Option<usize> {
|
||||
Some(self.instruction.accounts.len() * 34 + self.instruction.data.len() + 90)
|
||||
Some(self.instruction.accounts.len() * 34 + self.instruction.data.len() + 86)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ async fn test_cancel_proposal() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
.cancel_proposal(&proposal_cookie, &token_owner_record_cookie)
|
||||
|
@ -41,7 +43,7 @@ async fn test_cancel_proposal() {
|
|||
.await;
|
||||
|
||||
assert_eq!(ProposalState::Cancelled, proposal_account.state);
|
||||
assert_eq!(Some(1), proposal_account.closed_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -29,6 +29,8 @@ async fn test_cast_vote() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
let vote_record_cookie = governance_test
|
||||
.with_cast_vote(&proposal_cookie, &token_owner_record_cookie, Vote::Yes)
|
||||
|
@ -54,7 +56,10 @@ async fn test_cast_vote() {
|
|||
);
|
||||
|
||||
assert_eq!(proposal_account.state, ProposalState::Succeeded);
|
||||
assert_eq!(proposal_account.voting_completed_at, Some(1));
|
||||
assert_eq!(
|
||||
proposal_account.voting_completed_at,
|
||||
Some(clock.unix_timestamp)
|
||||
);
|
||||
|
||||
let token_owner_record = governance_test
|
||||
.get_token_owner_record_account(&token_owner_record_cookie.address)
|
||||
|
@ -501,13 +506,12 @@ async fn test_cast_vote_with_voting_time_expired_error() {
|
|||
.get_proposal_account(&proposal_cookie.address)
|
||||
.await;
|
||||
|
||||
let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time
|
||||
+ proposal_account.voting_at.unwrap()
|
||||
+ 1;
|
||||
let vote_expired_at = proposal_account.voting_at.unwrap()
|
||||
+ account_governance_cookie.account.config.max_voting_time as i64;
|
||||
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(vote_expired_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_past_timestamp(vote_expired_at)
|
||||
.await;
|
||||
|
||||
// Act
|
||||
|
||||
|
@ -553,7 +557,7 @@ async fn test_cast_vote_with_cast_twice_error() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
governance_test.context.warp_to_slot(5).unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
let err = governance_test
|
||||
|
|
|
@ -44,7 +44,6 @@ async fn test_community_proposal_created() {
|
|||
.await;
|
||||
|
||||
assert_eq!(1, account_governance_account.proposals_count);
|
||||
assert_eq!(proposal_account.draft_at, 1);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -108,7 +108,7 @@ async fn test_deposit_subsequent_community_tokens() {
|
|||
.governing_token_deposit_amount
|
||||
+ deposit_amount;
|
||||
|
||||
governance_test.context.warp_to_slot(5).unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
|
@ -154,7 +154,7 @@ async fn test_deposit_subsequent_council_tokens() {
|
|||
.governing_token_deposit_amount
|
||||
+ deposit_amount;
|
||||
|
||||
governance_test.context.warp_to_slot(5).unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
|
|
|
@ -59,13 +59,12 @@ async fn test_execute_mint_instruction() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
|
||||
// Advance timestamp past hold_up_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
|
@ -81,15 +80,15 @@ async fn test_execute_mint_instruction() {
|
|||
|
||||
assert_eq!(1, proposal_account.instructions_executed_count);
|
||||
assert_eq!(ProposalState::Completed, proposal_account.state);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.closed_at);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.executing_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at);
|
||||
|
||||
let proposal_instruction_account = governance_test
|
||||
.get_proposal_instruction_account(&proposal_instruction_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
Some(execute_at_slot),
|
||||
Some(clock.unix_timestamp),
|
||||
proposal_instruction_account.executed_at
|
||||
);
|
||||
|
||||
|
@ -147,13 +146,12 @@ async fn test_execute_transfer_instruction() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
|
||||
// Advance timestamp past hold_up_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
|
@ -169,15 +167,15 @@ async fn test_execute_transfer_instruction() {
|
|||
|
||||
assert_eq!(1, proposal_account.instructions_executed_count);
|
||||
assert_eq!(ProposalState::Completed, proposal_account.state);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.closed_at);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.executing_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at);
|
||||
|
||||
let proposal_instruction_account = governance_test
|
||||
.get_proposal_instruction_account(&proposal_instruction_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
Some(execute_at_slot),
|
||||
Some(clock.unix_timestamp),
|
||||
proposal_instruction_account.executed_at
|
||||
);
|
||||
|
||||
|
@ -234,13 +232,10 @@ async fn test_execute_upgrade_program_instruction() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
|
||||
// Advance timestamp past hold_up_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
// Ensure we can invoke the governed program before upgrade
|
||||
let governed_program_instruction = Instruction::new_with_bytes(
|
||||
|
@ -262,6 +257,8 @@ async fn test_execute_upgrade_program_instruction() {
|
|||
// solana_bpf_rust_upgradable returns CustomError == 42
|
||||
assert_eq!(ProgramError::Custom(42), err);
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
.execute_instruction(&proposal_cookie, &proposal_instruction_cookie)
|
||||
|
@ -276,23 +273,21 @@ async fn test_execute_upgrade_program_instruction() {
|
|||
|
||||
assert_eq!(1, proposal_account.instructions_executed_count);
|
||||
assert_eq!(ProposalState::Completed, proposal_account.state);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.closed_at);
|
||||
assert_eq!(Some(execute_at_slot), proposal_account.executing_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.closed_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.executing_at);
|
||||
|
||||
let proposal_instruction_account = governance_test
|
||||
.get_proposal_instruction_account(&proposal_instruction_cookie.address)
|
||||
.await;
|
||||
|
||||
assert_eq!(
|
||||
Some(execute_at_slot),
|
||||
Some(clock.unix_timestamp),
|
||||
proposal_instruction_account.executed_at
|
||||
);
|
||||
|
||||
// Assert we can invoke the governed program after upgrade
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot + 10)
|
||||
.unwrap();
|
||||
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
let err = governance_test
|
||||
.process_transaction(&[governed_program_instruction.clone()], None)
|
||||
|
@ -411,7 +406,7 @@ async fn test_execute_instruction_with_invalid_state_errors() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
governance_test.context.warp_to_slot(5).unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
let err = governance_test
|
||||
|
@ -427,13 +422,10 @@ async fn test_execute_instruction_with_invalid_state_errors() {
|
|||
);
|
||||
|
||||
// Arrange
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
|
||||
// Advance timestamp past hold_up_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
|
@ -451,10 +443,7 @@ async fn test_execute_instruction_with_invalid_state_errors() {
|
|||
|
||||
// Arrange
|
||||
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot + 10)
|
||||
.unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
let err = governance_test
|
||||
|
@ -517,13 +506,11 @@ async fn test_execute_instruction_for_other_proposal_error() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
// Advance clock past hold_up_time
|
||||
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
let proposal_cookie2 = governance_test
|
||||
.with_proposal(&token_owner_record_cookie, &mut mint_governance_cookie)
|
||||
|
@ -596,23 +583,18 @@ async fn test_execute_mint_instruction_twice_error() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
// Advance slot past hold_up_time
|
||||
let execute_at_slot = 1 + proposal_instruction_cookie.account.hold_up_time + 1;
|
||||
// Advance clock past hold_up_time
|
||||
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_by_min_timespan(proposal_instruction_cookie.account.hold_up_time as u64)
|
||||
.await;
|
||||
|
||||
governance_test
|
||||
.execute_instruction(&proposal_cookie, &proposal_instruction_cookie)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(execute_at_slot + 10)
|
||||
.unwrap();
|
||||
governance_test.advance_clock().await;
|
||||
|
||||
// Act
|
||||
|
||||
|
|
|
@ -56,14 +56,15 @@ async fn test_finalize_vote_to_succeeded() {
|
|||
|
||||
assert_eq!(ProposalState::Voting, proposal_account.state);
|
||||
|
||||
// Advance slot past max_voting_time
|
||||
let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time
|
||||
+ proposal_account.voting_at.unwrap()
|
||||
+ 1;
|
||||
// Advance timestamp past max_voting_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(vote_expired_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_past_timestamp(
|
||||
account_governance_cookie.account.config.max_voting_time as i64
|
||||
+ proposal_account.voting_at.unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
|
||||
|
@ -80,7 +81,7 @@ async fn test_finalize_vote_to_succeeded() {
|
|||
|
||||
assert_eq!(proposal_account.state, ProposalState::Succeeded);
|
||||
assert_eq!(
|
||||
Some(vote_expired_at_slot),
|
||||
Some(clock.unix_timestamp),
|
||||
proposal_account.voting_completed_at
|
||||
);
|
||||
}
|
||||
|
@ -124,14 +125,13 @@ async fn test_finalize_vote_to_defeated() {
|
|||
|
||||
assert_eq!(ProposalState::Voting, proposal_account.state);
|
||||
|
||||
// Advance slot past max_voting_time
|
||||
let vote_expired_at_slot = account_governance_cookie.account.config.max_voting_time
|
||||
+ proposal_account.voting_at.unwrap()
|
||||
+ 1;
|
||||
// Advance clock past max_voting_time
|
||||
governance_test
|
||||
.context
|
||||
.warp_to_slot(vote_expired_at_slot)
|
||||
.unwrap();
|
||||
.advance_clock_past_timestamp(
|
||||
account_governance_cookie.account.config.max_voting_time as i64
|
||||
+ proposal_account.voting_at.unwrap(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Act
|
||||
|
||||
|
|
|
@ -34,6 +34,8 @@ async fn test_sign_off_proposal() {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let clock = governance_test.get_clock().await;
|
||||
|
||||
// Act
|
||||
governance_test
|
||||
.sign_off_proposal(&proposal_cookie, &signatory_record_cookie)
|
||||
|
@ -48,8 +50,8 @@ async fn test_sign_off_proposal() {
|
|||
assert_eq!(1, proposal_account.signatories_count);
|
||||
assert_eq!(1, proposal_account.signatories_signed_off_count);
|
||||
assert_eq!(ProposalState::Voting, proposal_account.state);
|
||||
assert_eq!(Some(1), proposal_account.signing_off_at);
|
||||
assert_eq!(Some(1), proposal_account.voting_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.signing_off_at);
|
||||
assert_eq!(Some(clock.unix_timestamp), proposal_account.voting_at);
|
||||
|
||||
let signatory_record_account = governance_test
|
||||
.get_signatory_record_account(&signatory_record_cookie.address)
|
||||
|
|
|
@ -4,12 +4,13 @@ use borsh::BorshDeserialize;
|
|||
use solana_program::{
|
||||
borsh::try_from_slice_unchecked,
|
||||
bpf_loader_upgradeable::{self, UpgradeableLoaderState},
|
||||
clock::{Clock, UnixTimestamp},
|
||||
instruction::{AccountMeta, Instruction},
|
||||
program_error::ProgramError,
|
||||
program_pack::{IsInitialized, Pack},
|
||||
pubkey::Pubkey,
|
||||
rent::Rent,
|
||||
system_instruction,
|
||||
system_instruction, sysvar,
|
||||
};
|
||||
|
||||
use bincode::deserialize;
|
||||
|
@ -1036,6 +1037,8 @@ impl GovernanceProgramTest {
|
|||
)
|
||||
.await?;
|
||||
|
||||
let clock = self.get_clock().await;
|
||||
|
||||
let account = Proposal {
|
||||
account_type: GovernanceAccountType::Proposal,
|
||||
description_link,
|
||||
|
@ -1045,7 +1048,7 @@ impl GovernanceProgramTest {
|
|||
state: ProposalState::Draft,
|
||||
signatories_count: 0,
|
||||
// Clock always returns 1 when running under the test
|
||||
draft_at: 1,
|
||||
draft_at: clock.unix_timestamp,
|
||||
signing_off_at: None,
|
||||
voting_at: None,
|
||||
voting_completed_at: None,
|
||||
|
@ -1642,6 +1645,39 @@ impl GovernanceProgramTest {
|
|||
.expect(format!("GET-TEST-ACCOUNT-ERROR: Account {}", address).as_str())
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_clock(&mut self) -> Clock {
|
||||
self.get_bincode_account::<Clock>(&sysvar::clock::id())
|
||||
.await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn advance_clock_past_timestamp(&mut self, unix_timestamp: UnixTimestamp) {
|
||||
let mut clock = self.get_clock().await;
|
||||
let mut n = 1;
|
||||
|
||||
while clock.unix_timestamp <= unix_timestamp {
|
||||
// Since the exact time is not deterministic keep wrapping by arbitrary 400 slots until we pass the requested timestamp
|
||||
self.context.warp_to_slot(clock.slot + n * 400).unwrap();
|
||||
|
||||
n = n + 1;
|
||||
clock = self.get_clock().await;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn advance_clock_by_min_timespan(&mut self, time_span: u64) {
|
||||
let clock = self.get_clock().await;
|
||||
self.advance_clock_past_timestamp(clock.unix_timestamp + (time_span as i64))
|
||||
.await;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn advance_clock(&mut self) {
|
||||
let clock = self.get_clock().await;
|
||||
self.context.warp_to_slot(clock.slot + 2).unwrap();
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_upgradable_loader_account(
|
||||
&mut self,
|
||||
|
|
Loading…
Reference in New Issue