use std::str::FromStr; use gemachain_program::{ bpf_loader_upgradeable::{self, UpgradeableLoaderState}, clock::UnixTimestamp, instruction::{AccountMeta, Instruction}, program_error::ProgramError, program_pack::{IsInitialized, Pack}, pubkey::Pubkey, system_program, }; use gemachain_program_test::*; use gemachain_sdk::signature::{Keypair, Signer}; use gpl_governance::{ addins::voter_weight::{VoterWeightAccountType, VoterWeightRecord}, instruction::{ add_signatory, cancel_proposal, cast_vote, create_account_governance, create_mint_governance, create_program_governance, create_proposal, create_realm, create_token_governance, create_token_owner_record, deposit_governing_tokens, execute_instruction, finalize_vote, flag_instruction_error, insert_instruction, relinquish_vote, remove_instruction, remove_signatory, set_governance_config, set_governance_delegate, set_realm_authority, set_realm_config, sign_off_proposal, withdraw_governing_tokens, Vote, }, processor::process_instruction, state::{ enums::{ GovernanceAccountType, InstructionExecutionFlags, InstructionExecutionStatus, MintMaxVoteWeightSource, ProposalState, VoteThresholdPercentage, VoteWeight, }, governance::{ get_account_governance_address, get_mint_governance_address, get_program_governance_address, get_token_governance_address, Governance, GovernanceConfig, }, proposal::{get_proposal_address, Proposal}, proposal_instruction::{ get_proposal_instruction_address, InstructionData, ProposalInstruction, }, realm::{ get_governing_token_holding_address, get_realm_address, Realm, RealmConfig, RealmConfigArgs, }, realm_config::{get_realm_config_address, RealmConfigAccount}, signatory_record::{get_signatory_record_address, SignatoryRecord}, token_owner_record::{get_token_owner_record_address, TokenOwnerRecord}, vote_record::{get_vote_record_address, VoteRecord}, }, tools::bpf_loader_upgradeable::get_program_data_address, }; pub mod addins; pub mod cookies; use crate::program_test::cookies::{ RealmConfigCookie, SignatoryRecordCookie, VoterWeightRecordCookie, }; use gpl_governance_test_sdk::{ tools::{clone_keypair, NopOverride}, ProgramTestBench, TestBenchProgram, }; use self::{ addins::ensure_voter_weight_addin_is_built, cookies::{ GovernanceCookie, GovernedAccountCookie, GovernedMintCookie, GovernedProgramCookie, GovernedTokenCookie, ProposalCookie, ProposalInstructionCookie, RealmCookie, TokenOwnerRecordCookie, VoteRecordCookie, }, }; pub struct GovernanceProgramTest { pub bench: ProgramTestBench, pub next_realm_id: u8, pub program_id: Pubkey, pub voter_weight_addin_id: Option, } impl GovernanceProgramTest { #[allow(dead_code)] pub async fn start_new() -> Self { Self::start_impl(false).await } #[allow(dead_code)] pub async fn start_with_voter_weight_addin() -> Self { ensure_voter_weight_addin_is_built(); Self::start_impl(true).await } #[allow(dead_code)] async fn start_impl(use_voter_weight_addin: bool) -> Self { let mut programs = vec![]; let program_id = Pubkey::from_str("Governance111111111111111111111111111111111").unwrap(); let program = TestBenchProgram { program_name: "gpl_governance", program_id, process_instruction: processor!(process_instruction), }; programs.push(program); let voter_weight_addin_id = if use_voter_weight_addin { let voter_weight_addin_id = Pubkey::from_str("VoterWeight11111111111111111111111111111111").unwrap(); let vote_weight_addin = TestBenchProgram { program_name: "gpl_governance_voter_weight_addin", program_id: voter_weight_addin_id, process_instruction: None, }; programs.push(vote_weight_addin); Some(voter_weight_addin_id) } else { None }; let bench = ProgramTestBench::start_new(&programs).await; Self { bench, next_realm_id: 0, program_id, voter_weight_addin_id, } } #[allow(dead_code)] pub fn get_default_realm_config_args(&mut self) -> RealmConfigArgs { RealmConfigArgs { use_council_mint: true, community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, min_community_tokens_to_create_governance: 10, use_community_voter_weight_addin: self.voter_weight_addin_id.is_some(), } } #[allow(dead_code)] pub async fn with_realm(&mut self) -> RealmCookie { let realm_config_args = self.get_default_realm_config_args(); self.with_realm_using_config_args(&realm_config_args).await } #[allow(dead_code)] pub async fn with_realm_using_config_args( &mut self, config_args: &RealmConfigArgs, ) -> RealmCookie { let name = format!("Realm #{}", self.next_realm_id).to_string(); self.next_realm_id += 1; let realm_address = get_realm_address(&self.program_id, &name); let community_token_mint_keypair = Keypair::new(); let community_token_mint_authority = Keypair::new(); let community_token_holding_address = get_governing_token_holding_address( &self.program_id, &realm_address, &community_token_mint_keypair.pubkey(), ); self.bench .create_mint( &community_token_mint_keypair, &community_token_mint_authority.pubkey(), ) .await; let ( council_token_mint_pubkey, council_token_holding_address, council_token_mint_authority, ) = if config_args.use_council_mint { let council_token_mint_keypair = Keypair::new(); let council_token_mint_authority = Keypair::new(); let council_token_holding_address = get_governing_token_holding_address( &self.program_id, &realm_address, &council_token_mint_keypair.pubkey(), ); self.bench .create_mint( &council_token_mint_keypair, &council_token_mint_authority.pubkey(), ) .await; ( Some(council_token_mint_keypair.pubkey()), Some(council_token_holding_address), Some(council_token_mint_authority), ) } else { (None, None, None) }; let realm_authority = Keypair::new(); let community_voter_weight_addin = if config_args.use_community_voter_weight_addin { self.voter_weight_addin_id } else { None }; let create_realm_instruction = create_realm( &self.program_id, &realm_authority.pubkey(), &community_token_mint_keypair.pubkey(), &self.bench.payer.pubkey(), council_token_mint_pubkey, community_voter_weight_addin, name.clone(), config_args.min_community_tokens_to_create_governance, config_args.community_mint_max_vote_weight_source.clone(), ); self.bench .process_transaction(&[create_realm_instruction], None) .await .unwrap(); let account = Realm { account_type: GovernanceAccountType::Realm, community_mint: community_token_mint_keypair.pubkey(), name, reserved: [0; 8], authority: Some(realm_authority.pubkey()), config: RealmConfig { council_mint: council_token_mint_pubkey, reserved: [0; 7], min_community_tokens_to_create_governance: config_args .min_community_tokens_to_create_governance, community_mint_max_vote_weight_source: config_args .community_mint_max_vote_weight_source .clone(), use_community_voter_weight_addin: false, }, }; let realm_config_cookie = if config_args.use_community_voter_weight_addin { Some(RealmConfigCookie { address: get_realm_config_address(&self.program_id, &realm_address), account: RealmConfigAccount { account_type: GovernanceAccountType::RealmConfig, realm: realm_address, community_voter_weight_addin: self.voter_weight_addin_id, community_max_vote_weight_addin: None, council_voter_weight_addin: None, council_max_vote_weight_addin: None, reserved: [0; 128], }, }) } else { None }; RealmCookie { address: realm_address, account, community_mint_authority: community_token_mint_authority, community_token_holding_account: community_token_holding_address, council_token_holding_account: council_token_holding_address, council_mint_authority: council_token_mint_authority, realm_authority: Some(realm_authority), realm_config: realm_config_cookie, } } #[allow(dead_code)] pub async fn with_realm_using_mints(&mut self, realm_cookie: &RealmCookie) -> RealmCookie { let name = format!("Realm #{}", self.next_realm_id).to_string(); self.next_realm_id += 1; let realm_address = get_realm_address(&self.program_id, &name); let council_mint = realm_cookie.account.config.council_mint.unwrap(); let realm_authority = Keypair::new(); let community_mint_max_vote_weight_source = MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION; let min_community_tokens_to_create_governance = 10; let create_realm_instruction = create_realm( &self.program_id, &realm_authority.pubkey(), &realm_cookie.account.community_mint, &self.bench.context.payer.pubkey(), Some(council_mint), None, name.clone(), min_community_tokens_to_create_governance, community_mint_max_vote_weight_source, ); self.bench .process_transaction(&[create_realm_instruction], None) .await .unwrap(); let account = Realm { account_type: GovernanceAccountType::Realm, community_mint: realm_cookie.account.community_mint, name, reserved: [0; 8], authority: Some(realm_authority.pubkey()), config: RealmConfig { council_mint: Some(council_mint), reserved: [0; 7], community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, min_community_tokens_to_create_governance, use_community_voter_weight_addin: false, }, }; let community_token_holding_address = get_governing_token_holding_address( &self.program_id, &realm_address, &realm_cookie.account.community_mint, ); let council_token_holding_address = get_governing_token_holding_address(&self.program_id, &realm_address, &council_mint); RealmCookie { address: realm_address, account, community_mint_authority: clone_keypair(&realm_cookie.community_mint_authority), community_token_holding_account: community_token_holding_address, council_token_holding_account: Some(council_token_holding_address), council_mint_authority: Some(clone_keypair( realm_cookie.council_mint_authority.as_ref().unwrap(), )), realm_authority: Some(realm_authority), realm_config: None, } } // Creates TokenOwner which owns 100 community tokens and deposits them into the given Realm #[allow(dead_code)] pub async fn with_community_token_deposit( &mut self, realm_cookie: &RealmCookie, ) -> Result { self.with_initial_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.community_mint, &realm_cookie.community_mint_authority, 100, ) .await } #[allow(dead_code)] pub async fn with_token_owner_record( &mut self, realm_cookie: &RealmCookie, ) -> TokenOwnerRecordCookie { let token_owner = Keypair::new(); let create_token_owner_record_ix = create_token_owner_record( &self.program_id, &realm_cookie.address, &token_owner.pubkey(), &realm_cookie.account.community_mint, &self.bench.payer.pubkey(), ); self.bench .process_transaction(&[create_token_owner_record_ix], None) .await .unwrap(); let account = TokenOwnerRecord { account_type: GovernanceAccountType::TokenOwnerRecord, realm: realm_cookie.address, governing_token_mint: realm_cookie.account.community_mint, governing_token_owner: token_owner.pubkey(), governing_token_deposit_amount: 0, governance_delegate: None, unrelinquished_votes_count: 0, total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], }; let token_owner_record_address = get_token_owner_record_address( &self.program_id, &realm_cookie.address, &realm_cookie.account.community_mint, &token_owner.pubkey(), ); TokenOwnerRecordCookie { address: token_owner_record_address, account, token_source_amount: 0, token_source: Pubkey::new_unique(), token_owner, governance_authority: None, governance_delegate: Keypair::new(), voter_weight_record: None, } } #[allow(dead_code)] pub async fn with_community_token_deposit_amount( &mut self, realm_cookie: &RealmCookie, amount: u64, ) -> Result { self.with_initial_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.community_mint, &realm_cookie.community_mint_authority, amount, ) .await } #[allow(dead_code)] pub async fn with_subsequent_community_token_deposit( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, amount: u64, ) { self.with_subsequent_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.community_mint, &realm_cookie.community_mint_authority, token_owner_record_cookie, amount, ) .await; } #[allow(dead_code)] pub async fn with_subsequent_council_token_deposit( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, amount: u64, ) { self.with_subsequent_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.config.council_mint.unwrap(), realm_cookie.council_mint_authority.as_ref().unwrap(), token_owner_record_cookie, amount, ) .await; } #[allow(dead_code)] pub async fn with_council_token_deposit_amount( &mut self, realm_cookie: &RealmCookie, amount: u64, ) -> Result { self.with_initial_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.config.council_mint.unwrap(), &realm_cookie.council_mint_authority.as_ref().unwrap(), amount, ) .await } #[allow(dead_code)] pub async fn with_council_token_deposit( &mut self, realm_cookie: &RealmCookie, ) -> Result { self.with_initial_governing_token_deposit( &realm_cookie.address, &realm_cookie.account.config.council_mint.unwrap(), realm_cookie.council_mint_authority.as_ref().unwrap(), 100, ) .await } #[allow(dead_code)] pub async fn with_initial_governing_token_deposit( &mut self, realm_address: &Pubkey, governing_mint: &Pubkey, governing_mint_authority: &Keypair, amount: u64, ) -> Result { let token_owner = Keypair::new(); let token_source = Keypair::new(); let transfer_authority = Keypair::new(); self.bench .create_token_account_with_transfer_authority( &token_source, governing_mint, governing_mint_authority, amount, &token_owner, &transfer_authority.pubkey(), ) .await; let deposit_governing_tokens_instruction = deposit_governing_tokens( &self.program_id, realm_address, &token_source.pubkey(), &token_owner.pubkey(), &token_owner.pubkey(), &self.bench.payer.pubkey(), amount, governing_mint, ); self.bench .process_transaction( &[deposit_governing_tokens_instruction], Some(&[&token_owner]), ) .await?; let token_owner_record_address = get_token_owner_record_address( &self.program_id, realm_address, governing_mint, &token_owner.pubkey(), ); let account = TokenOwnerRecord { account_type: GovernanceAccountType::TokenOwnerRecord, realm: *realm_address, governing_token_mint: *governing_mint, governing_token_owner: token_owner.pubkey(), governing_token_deposit_amount: amount, governance_delegate: None, unrelinquished_votes_count: 0, total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], }; let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string()); Ok(TokenOwnerRecordCookie { address: token_owner_record_address, account, token_source_amount: amount, token_source: token_source.pubkey(), token_owner, governance_authority: None, governance_delegate, voter_weight_record: None, }) } #[allow(dead_code)] pub async fn mint_community_tokens(&mut self, realm_cookie: &RealmCookie, amount: u64) { let token_account_keypair = Keypair::new(); self.bench .create_empty_token_account( &token_account_keypair, &realm_cookie.account.community_mint, &self.bench.payer.pubkey(), ) .await; self.bench .mint_tokens( &realm_cookie.account.community_mint, &realm_cookie.community_mint_authority, &token_account_keypair.pubkey(), amount, ) .await; } #[allow(dead_code)] async fn with_subsequent_governing_token_deposit( &mut self, realm: &Pubkey, governing_token_mint: &Pubkey, governing_token_mint_authority: &Keypair, token_owner_record_cookie: &TokenOwnerRecordCookie, amount: u64, ) { self.bench .mint_tokens( governing_token_mint, governing_token_mint_authority, &token_owner_record_cookie.token_source, amount, ) .await; let deposit_governing_tokens_instruction = deposit_governing_tokens( &self.program_id, realm, &token_owner_record_cookie.token_source, &token_owner_record_cookie.token_owner.pubkey(), &token_owner_record_cookie.token_owner.pubkey(), &self.bench.payer.pubkey(), amount, governing_token_mint, ); self.bench .process_transaction( &[deposit_governing_tokens_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await .unwrap(); } #[allow(dead_code)] pub async fn with_community_governance_delegate( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &mut TokenOwnerRecordCookie, ) { self.with_governing_token_governance_delegate( realm_cookie, &realm_cookie.account.community_mint, token_owner_record_cookie, ) .await; } #[allow(dead_code)] pub async fn with_council_governance_delegate( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &mut TokenOwnerRecordCookie, ) { self.with_governing_token_governance_delegate( realm_cookie, &realm_cookie.account.config.council_mint.unwrap(), token_owner_record_cookie, ) .await; } #[allow(dead_code)] pub async fn with_governing_token_governance_delegate( &mut self, realm_cookie: &RealmCookie, governing_token_mint: &Pubkey, token_owner_record_cookie: &mut TokenOwnerRecordCookie, ) { let new_governance_delegate = Keypair::new(); self.set_governance_delegate( realm_cookie, token_owner_record_cookie, &token_owner_record_cookie.token_owner, governing_token_mint, &Some(new_governance_delegate.pubkey()), ) .await; token_owner_record_cookie.governance_delegate = new_governance_delegate; } #[allow(dead_code)] pub async fn set_governance_delegate( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, signing_governance_authority: &Keypair, governing_token_mint: &Pubkey, new_governance_delegate: &Option, ) { let set_governance_delegate_instruction = set_governance_delegate( &self.program_id, &signing_governance_authority.pubkey(), &realm_cookie.address, governing_token_mint, &token_owner_record_cookie.token_owner.pubkey(), new_governance_delegate, ); self.bench .process_transaction( &[set_governance_delegate_instruction], Some(&[signing_governance_authority]), ) .await .unwrap(); } #[allow(dead_code)] pub async fn set_realm_authority( &mut self, realm_cookie: &RealmCookie, new_realm_authority: &Option, ) -> Result<(), ProgramError> { self.set_realm_authority_using_instruction( realm_cookie, new_realm_authority, NopOverride, None, ) .await } #[allow(dead_code)] pub async fn set_realm_authority_using_instruction( &mut self, realm_cookie: &RealmCookie, new_realm_authority: &Option, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result<(), ProgramError> { let mut set_realm_authority_ix = set_realm_authority( &self.program_id, &realm_cookie.address, &realm_cookie.realm_authority.as_ref().unwrap().pubkey(), new_realm_authority, ); instruction_override(&mut set_realm_authority_ix); let default_signers = &[realm_cookie.realm_authority.as_ref().unwrap()]; let signers = signers_override.unwrap_or(default_signers); self.bench .process_transaction(&[set_realm_authority_ix], Some(signers)) .await } #[allow(dead_code)] pub async fn set_realm_config( &mut self, realm_cookie: &mut RealmCookie, realm_config_args: &RealmConfigArgs, ) -> Result<(), ProgramError> { self.set_realm_config_using_instruction(realm_cookie, realm_config_args, NopOverride, None) .await } #[allow(dead_code)] pub async fn set_realm_config_using_instruction( &mut self, realm_cookie: &mut RealmCookie, realm_config_args: &RealmConfigArgs, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result<(), ProgramError> { let council_token_mint = if realm_config_args.use_council_mint { realm_cookie.account.config.council_mint } else { None }; let community_voter_weight_addin = if realm_config_args.use_community_voter_weight_addin { self.voter_weight_addin_id } else { None }; let mut set_realm_config_ix = set_realm_config( &self.program_id, &realm_cookie.address, &realm_cookie.realm_authority.as_ref().unwrap().pubkey(), council_token_mint, &self.bench.payer.pubkey(), community_voter_weight_addin, realm_config_args.min_community_tokens_to_create_governance, realm_config_args .community_mint_max_vote_weight_source .clone(), ); instruction_override(&mut set_realm_config_ix); let default_signers = &[realm_cookie.realm_authority.as_ref().unwrap()]; let signers = signers_override.unwrap_or(default_signers); realm_cookie.account.config.council_mint = council_token_mint; realm_cookie .account .config .community_mint_max_vote_weight_source = realm_config_args .community_mint_max_vote_weight_source .clone(); if realm_config_args.use_community_voter_weight_addin { let community_voter_weight_addin_index = if realm_config_args.use_council_mint { 7 } else { 5 }; realm_cookie.realm_config = Some(RealmConfigCookie { address: get_realm_config_address(&self.program_id, &realm_cookie.address), account: RealmConfigAccount { account_type: GovernanceAccountType::RealmConfig, realm: realm_cookie.address, community_voter_weight_addin: Some( set_realm_config_ix.accounts[community_voter_weight_addin_index].pubkey, ), community_max_vote_weight_addin: None, council_voter_weight_addin: None, council_max_vote_weight_addin: None, reserved: [0; 128], }, }) } self.bench .process_transaction(&[set_realm_config_ix], Some(signers)) .await } #[allow(dead_code)] pub async fn withdraw_community_tokens( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result<(), ProgramError> { self.withdraw_governing_tokens( realm_cookie, token_owner_record_cookie, &realm_cookie.account.community_mint, &token_owner_record_cookie.token_owner, ) .await } #[allow(dead_code)] pub async fn withdraw_council_tokens( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result<(), ProgramError> { self.withdraw_governing_tokens( realm_cookie, token_owner_record_cookie, &realm_cookie.account.config.council_mint.unwrap(), &token_owner_record_cookie.token_owner, ) .await } #[allow(dead_code)] async fn withdraw_governing_tokens( &mut self, realm_cookie: &RealmCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, governing_token_mint: &Pubkey, governing_token_owner: &Keypair, ) -> Result<(), ProgramError> { let deposit_governing_tokens_instruction = withdraw_governing_tokens( &self.program_id, &realm_cookie.address, &token_owner_record_cookie.token_source, &governing_token_owner.pubkey(), governing_token_mint, ); self.bench .process_transaction( &[deposit_governing_tokens_instruction], Some(&[governing_token_owner]), ) .await } #[allow(dead_code)] pub async fn with_governed_account(&mut self) -> GovernedAccountCookie { GovernedAccountCookie { address: Pubkey::new_unique(), } } #[allow(dead_code)] pub async fn with_governed_mint(&mut self) -> GovernedMintCookie { let mint_keypair = Keypair::new(); let mint_authority = Keypair::new(); self.bench .create_mint(&mint_keypair, &mint_authority.pubkey()) .await; GovernedMintCookie { address: mint_keypair.pubkey(), mint_authority, transfer_mint_authority: true, } } #[allow(dead_code)] pub async fn with_governed_token(&mut self) -> GovernedTokenCookie { let mint_keypair = Keypair::new(); let mint_authority = Keypair::new(); self.bench .create_mint(&mint_keypair, &mint_authority.pubkey()) .await; let token_keypair = Keypair::new(); let token_owner = Keypair::new(); self.bench .create_empty_token_account( &token_keypair, &mint_keypair.pubkey(), &token_owner.pubkey(), ) .await; self.bench .mint_tokens( &mint_keypair.pubkey(), &mint_authority, &token_keypair.pubkey(), 100, ) .await; GovernedTokenCookie { address: token_keypair.pubkey(), token_owner, transfer_token_owner: true, token_mint: mint_keypair.pubkey(), } } pub fn get_default_governance_config(&mut self) -> GovernanceConfig { GovernanceConfig { min_community_tokens_to_create_proposal: 5, min_council_tokens_to_create_proposal: 2, min_instruction_hold_up_time: 10, max_voting_time: 10, vote_threshold_percentage: VoteThresholdPercentage::YesVote(60), vote_weight_source: gpl_governance::state::enums::VoteWeightSource::Deposit, proposal_cool_off_time: 0, } } #[allow(dead_code)] pub async fn with_account_governance( &mut self, realm_cookie: &RealmCookie, governed_account_cookie: &GovernedAccountCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { let config = self.get_default_governance_config(); self.with_account_governance_using_config( realm_cookie, governed_account_cookie, token_owner_record_cookie, &config, ) .await } #[allow(dead_code)] pub async fn with_account_governance_using_config( &mut self, realm_cookie: &RealmCookie, governed_account_cookie: &GovernedAccountCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, governance_config: &GovernanceConfig, ) -> Result { let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let create_account_governance_instruction = create_account_governance( &self.program_id, &realm_cookie.address, &governed_account_cookie.address, &token_owner_record_cookie.address, &self.bench.payer.pubkey(), &token_owner_record_cookie.token_owner.pubkey(), voter_weight_record, governance_config.clone(), ); let account = Governance { account_type: GovernanceAccountType::AccountGovernance, realm: realm_cookie.address, governed_account: governed_account_cookie.address, config: governance_config.clone(), proposals_count: 0, reserved: [0; 8], }; self.bench .process_transaction( &[create_account_governance_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; let account_governance_address = get_account_governance_address( &self.program_id, &realm_cookie.address, &governed_account_cookie.address, ); Ok(GovernanceCookie { address: account_governance_address, account, next_proposal_index: 0, }) } #[allow(dead_code)] pub async fn with_governed_program(&mut self) -> GovernedProgramCookie { let program_keypair = Keypair::new(); let program_buffer_keypair = Keypair::new(); let program_upgrade_authority_keypair = Keypair::new(); let program_data_address = get_program_data_address(&program_keypair.pubkey()); // Load gemachain_bpf_rust_upgradeable program taken from gemachain test programs let path_buf = find_file("gemachain_bpf_rust_upgradeable.so").unwrap(); let program_data = read_file(path_buf); let program_buffer_rent = self .bench .rent .minimum_balance(UpgradeableLoaderState::programdata_len(program_data.len()).unwrap()); let mut instructions = bpf_loader_upgradeable::create_buffer( &self.bench.payer.pubkey(), &program_buffer_keypair.pubkey(), &program_upgrade_authority_keypair.pubkey(), program_buffer_rent, program_data.len(), ) .unwrap(); let chunk_size = 800; for (chunk, i) in program_data.chunks(chunk_size).zip(0..) { instructions.push(bpf_loader_upgradeable::write( &program_buffer_keypair.pubkey(), &program_upgrade_authority_keypair.pubkey(), (i * chunk_size) as u32, chunk.to_vec(), )); } let program_account_rent = self .bench .rent .minimum_balance(UpgradeableLoaderState::program_len().unwrap()); let deploy_instructions = bpf_loader_upgradeable::deploy_with_max_program_len( &self.bench.payer.pubkey(), &program_keypair.pubkey(), &program_buffer_keypair.pubkey(), &program_upgrade_authority_keypair.pubkey(), program_account_rent, program_data.len(), ) .unwrap(); instructions.extend_from_slice(&deploy_instructions); self.bench .process_transaction( &instructions[..], Some(&[ &program_upgrade_authority_keypair, &program_keypair, &program_buffer_keypair, ]), ) .await .unwrap(); GovernedProgramCookie { address: program_keypair.pubkey(), upgrade_authority: program_upgrade_authority_keypair, data_address: program_data_address, transfer_upgrade_authority: true, } } #[allow(dead_code)] pub async fn with_program_governance( &mut self, realm_cookie: &RealmCookie, governed_program_cookie: &GovernedProgramCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { self.with_program_governance_using_instruction( realm_cookie, governed_program_cookie, token_owner_record_cookie, NopOverride, None, ) .await } #[allow(dead_code)] pub async fn with_program_governance_using_instruction( &mut self, realm_cookie: &RealmCookie, governed_program_cookie: &GovernedProgramCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result { let config = self.get_default_governance_config(); let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let mut create_program_governance_instruction = create_program_governance( &self.program_id, &realm_cookie.address, &governed_program_cookie.address, &governed_program_cookie.upgrade_authority.pubkey(), &token_owner_record_cookie.address, &self.bench.payer.pubkey(), &token_owner_record_cookie.token_owner.pubkey(), voter_weight_record, config.clone(), governed_program_cookie.transfer_upgrade_authority, ); instruction_override(&mut create_program_governance_instruction); let default_signers = &[ &governed_program_cookie.upgrade_authority, &token_owner_record_cookie.token_owner, ]; let signers = signers_override.unwrap_or(default_signers); self.bench .process_transaction(&[create_program_governance_instruction], Some(signers)) .await?; let account = Governance { account_type: GovernanceAccountType::ProgramGovernance, realm: realm_cookie.address, governed_account: governed_program_cookie.address, config, proposals_count: 0, reserved: [0; 8], }; let program_governance_address = get_program_governance_address( &self.program_id, &realm_cookie.address, &governed_program_cookie.address, ); Ok(GovernanceCookie { address: program_governance_address, account, next_proposal_index: 0, }) } #[allow(dead_code)] pub async fn with_mint_governance( &mut self, realm_cookie: &RealmCookie, governed_mint_cookie: &GovernedMintCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { self.with_mint_governance_using_instruction( realm_cookie, governed_mint_cookie, token_owner_record_cookie, NopOverride, None, ) .await } #[allow(dead_code)] pub async fn with_mint_governance_using_instruction( &mut self, realm_cookie: &RealmCookie, governed_mint_cookie: &GovernedMintCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result { let config = self.get_default_governance_config(); let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let mut create_mint_governance_instruction = create_mint_governance( &self.program_id, &realm_cookie.address, &governed_mint_cookie.address, &governed_mint_cookie.mint_authority.pubkey(), &token_owner_record_cookie.address, &self.bench.payer.pubkey(), &token_owner_record_cookie.token_owner.pubkey(), voter_weight_record, config.clone(), governed_mint_cookie.transfer_mint_authority, ); instruction_override(&mut create_mint_governance_instruction); let default_signers = &[ &governed_mint_cookie.mint_authority, &token_owner_record_cookie.token_owner, ]; let signers = signers_override.unwrap_or(default_signers); self.bench .process_transaction(&[create_mint_governance_instruction], Some(signers)) .await?; let account = Governance { account_type: GovernanceAccountType::MintGovernance, realm: realm_cookie.address, governed_account: governed_mint_cookie.address, config, proposals_count: 0, reserved: [0; 8], }; let mint_governance_address = get_mint_governance_address( &self.program_id, &realm_cookie.address, &governed_mint_cookie.address, ); Ok(GovernanceCookie { address: mint_governance_address, account, next_proposal_index: 0, }) } #[allow(dead_code)] pub async fn with_token_governance( &mut self, realm_cookie: &RealmCookie, governed_token_cookie: &GovernedTokenCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { self.with_token_governance_using_instruction( realm_cookie, governed_token_cookie, &token_owner_record_cookie, NopOverride, None, ) .await } #[allow(dead_code)] pub async fn with_token_governance_using_instruction( &mut self, realm_cookie: &RealmCookie, governed_token_cookie: &GovernedTokenCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result { let config = self.get_default_governance_config(); let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let mut create_token_governance_instruction = create_token_governance( &self.program_id, &realm_cookie.address, &governed_token_cookie.address, &governed_token_cookie.token_owner.pubkey(), &token_owner_record_cookie.address, &self.bench.payer.pubkey(), &token_owner_record_cookie.token_owner.pubkey(), voter_weight_record, config.clone(), governed_token_cookie.transfer_token_owner, ); instruction_override(&mut create_token_governance_instruction); let default_signers = &[ &governed_token_cookie.token_owner, &token_owner_record_cookie.token_owner, ]; let signers = signers_override.unwrap_or(default_signers); self.bench .process_transaction(&[create_token_governance_instruction], Some(signers)) .await?; let account = Governance { account_type: GovernanceAccountType::TokenGovernance, realm: realm_cookie.address, governed_account: governed_token_cookie.address, config, proposals_count: 0, reserved: [0; 8], }; let token_governance_address = get_token_governance_address( &self.program_id, &realm_cookie.address, &governed_token_cookie.address, ); Ok(GovernanceCookie { address: token_governance_address, account, next_proposal_index: 0, }) } #[allow(dead_code)] pub async fn with_proposal( &mut self, token_owner_record_cookie: &TokenOwnerRecordCookie, governance_cookie: &mut GovernanceCookie, ) -> Result { self.with_proposal_using_instruction( token_owner_record_cookie, governance_cookie, NopOverride, ) .await } #[allow(dead_code)] pub async fn with_signed_off_proposal( &mut self, token_owner_record_cookie: &TokenOwnerRecordCookie, governance_cookie: &mut GovernanceCookie, ) -> Result { let proposal_cookie = self .with_proposal(token_owner_record_cookie, governance_cookie) .await?; let signatory_record_cookie = self .with_signatory(&proposal_cookie, token_owner_record_cookie) .await?; self.sign_off_proposal(&proposal_cookie, &signatory_record_cookie) .await?; Ok(proposal_cookie) } #[allow(dead_code)] pub async fn with_proposal_using_instruction( &mut self, token_owner_record_cookie: &TokenOwnerRecordCookie, governance_cookie: &mut GovernanceCookie, instruction_override: F, ) -> Result { let proposal_index = governance_cookie.next_proposal_index; governance_cookie.next_proposal_index += 1; let name = format!("Proposal #{}", proposal_index); let description_link = "Proposal Description".to_string(); let governance_authority = token_owner_record_cookie.get_governance_authority(); let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let mut create_proposal_instruction = create_proposal( &self.program_id, &governance_cookie.address, &token_owner_record_cookie.address, &governance_authority.pubkey(), &self.bench.payer.pubkey(), voter_weight_record, &governance_cookie.account.realm, name.clone(), description_link.clone(), &token_owner_record_cookie.account.governing_token_mint, proposal_index, ); instruction_override(&mut create_proposal_instruction); self.bench .process_transaction( &[create_proposal_instruction], Some(&[governance_authority]), ) .await?; let clock = self.bench.get_clock().await; let account = Proposal { account_type: GovernanceAccountType::Proposal, description_link, name: name.clone(), governance: governance_cookie.address, governing_token_mint: token_owner_record_cookie.account.governing_token_mint, state: ProposalState::Draft, signatories_count: 0, draft_at: clock.unix_timestamp, signing_off_at: None, voting_at: None, voting_at_slot: None, voting_completed_at: None, executing_at: None, closed_at: None, instructions_executed_count: 0, instructions_count: 0, instructions_next_index: 0, token_owner_record: token_owner_record_cookie.address, signatories_signed_off_count: 0, yes_votes_count: 0, no_votes_count: 0, execution_flags: InstructionExecutionFlags::None, max_vote_weight: None, vote_threshold_percentage: None, }; let proposal_address = get_proposal_address( &self.program_id, &governance_cookie.address, &token_owner_record_cookie.account.governing_token_mint, &proposal_index.to_le_bytes(), ); Ok(ProposalCookie { address: proposal_address, account, proposal_owner: governance_authority.pubkey(), }) } #[allow(dead_code)] pub async fn with_signatory( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { let signatory = Keypair::new(); let add_signatory_instruction = add_signatory( &self.program_id, &proposal_cookie.address, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &self.bench.payer.pubkey(), &signatory.pubkey(), ); self.bench .process_transaction( &[add_signatory_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; let signatory_record_address = get_signatory_record_address( &self.program_id, &proposal_cookie.address, &signatory.pubkey(), ); let signatory_record_data = SignatoryRecord { account_type: GovernanceAccountType::SignatoryRecord, proposal: proposal_cookie.address, signatory: signatory.pubkey(), signed_off: false, }; let signatory_record_cookie = SignatoryRecordCookie { address: signatory_record_address, account: signatory_record_data, signatory, }; Ok(signatory_record_cookie) } #[allow(dead_code)] pub async fn remove_signatory( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, signatory_record_cookie: &SignatoryRecordCookie, ) -> Result<(), ProgramError> { let remove_signatory_instruction = remove_signatory( &self.program_id, &proposal_cookie.address, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &signatory_record_cookie.account.signatory, &token_owner_record_cookie.token_owner.pubkey(), ); self.bench .process_transaction( &[remove_signatory_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; Ok(()) } #[allow(dead_code)] pub async fn sign_off_proposal( &mut self, proposal_cookie: &ProposalCookie, signatory_record_cookie: &SignatoryRecordCookie, ) -> Result<(), ProgramError> { self.sign_off_proposal_using_instruction( proposal_cookie, signatory_record_cookie, NopOverride, None, ) .await } #[allow(dead_code)] pub async fn sign_off_proposal_using_instruction( &mut self, proposal_cookie: &ProposalCookie, signatory_record_cookie: &SignatoryRecordCookie, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result<(), ProgramError> { let mut sign_off_proposal_ix = sign_off_proposal( &self.program_id, &proposal_cookie.address, &signatory_record_cookie.signatory.pubkey(), ); instruction_override(&mut sign_off_proposal_ix); let default_signers = &[&signatory_record_cookie.signatory]; let signers = signers_override.unwrap_or(default_signers); self.bench .process_transaction(&[sign_off_proposal_ix], Some(signers)) .await?; Ok(()) } #[allow(dead_code)] pub async fn finalize_vote( &mut self, realm_cookie: &RealmCookie, proposal_cookie: &ProposalCookie, ) -> Result<(), ProgramError> { let finalize_vote_instruction = finalize_vote( &self.program_id, &realm_cookie.address, &proposal_cookie.account.governance, &proposal_cookie.address, &proposal_cookie.account.token_owner_record, &proposal_cookie.account.governing_token_mint, ); self.bench .process_transaction(&[finalize_vote_instruction], None) .await?; Ok(()) } #[allow(dead_code)] pub async fn relinquish_vote( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result<(), ProgramError> { self.relinquish_vote_using_instruction( proposal_cookie, token_owner_record_cookie, NopOverride, ) .await } #[allow(dead_code)] pub async fn relinquish_vote_using_instruction( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, instruction_override: F, ) -> Result<(), ProgramError> { let mut relinquish_vote_instruction = relinquish_vote( &self.program_id, &proposal_cookie.account.governance, &proposal_cookie.address, &token_owner_record_cookie.address, &proposal_cookie.account.governing_token_mint, Some(token_owner_record_cookie.token_owner.pubkey()), Some(self.bench.payer.pubkey()), ); instruction_override(&mut relinquish_vote_instruction); self.bench .process_transaction( &[relinquish_vote_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; Ok(()) } #[allow(dead_code)] pub async fn cancel_proposal( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result<(), ProgramError> { let cancel_proposal_instruction = cancel_proposal( &self.program_id, &proposal_cookie.address, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &proposal_cookie.account.governance, ); self.bench .process_transaction( &[cancel_proposal_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; Ok(()) } #[allow(dead_code)] pub async fn with_cast_vote( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, vote: Vote, ) -> Result { let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { Some(voter_weight_record.address) } else { None }; let vote_instruction = cast_vote( &self.program_id, &token_owner_record_cookie.account.realm, &proposal_cookie.account.governance, &proposal_cookie.address, &proposal_cookie.account.token_owner_record, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &proposal_cookie.account.governing_token_mint, &self.bench.payer.pubkey(), voter_weight_record, vote.clone(), ); self.bench .process_transaction( &[vote_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; let vote_amount = token_owner_record_cookie .account .governing_token_deposit_amount; let vote_weight = match vote { Vote::Yes => VoteWeight::Yes(vote_amount), Vote::No => VoteWeight::No(vote_amount), }; let account = VoteRecord { account_type: GovernanceAccountType::VoteRecord, proposal: proposal_cookie.address, governing_token_owner: token_owner_record_cookie.token_owner.pubkey(), vote_weight, is_relinquished: false, }; let vote_record_cookie = VoteRecordCookie { address: get_vote_record_address( &self.program_id, &proposal_cookie.address, &token_owner_record_cookie.address, ), account, }; Ok(vote_record_cookie) } #[allow(dead_code)] pub async fn with_set_governance_config_instruction( &mut self, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, governance_config: &GovernanceConfig, ) -> Result { let mut set_governance_config_ix = set_governance_config( &self.program_id, &proposal_cookie.account.governance, governance_config.clone(), ); self.with_instruction( proposal_cookie, token_owner_record_cookie, None, &mut set_governance_config_ix, ) .await } #[allow(dead_code)] pub async fn with_mint_tokens_instruction( &mut self, governed_mint_cookie: &GovernedMintCookie, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, index: Option, ) -> Result { let token_account_keypair = Keypair::new(); self.bench .create_empty_token_account( &token_account_keypair, &governed_mint_cookie.address, &self.bench.payer.pubkey(), ) .await; let mut instruction = gpl_token::instruction::mint_to( &gpl_token::id(), &governed_mint_cookie.address, &token_account_keypair.pubkey(), &proposal_cookie.account.governance, &[], 10, ) .unwrap(); self.with_instruction( proposal_cookie, token_owner_record_cookie, index, &mut instruction, ) .await } #[allow(dead_code)] pub async fn with_transfer_tokens_instruction( &mut self, governed_token_cookie: &GovernedTokenCookie, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, index: Option, ) -> Result { let token_account_keypair = Keypair::new(); self.bench .create_empty_token_account( &token_account_keypair, &governed_token_cookie.token_mint, &self.bench.payer.pubkey(), ) .await; let mut instruction = gpl_token::instruction::transfer( &gpl_token::id(), &governed_token_cookie.address, &token_account_keypair.pubkey(), &proposal_cookie.account.governance, &[], 15, ) .unwrap(); self.with_instruction( proposal_cookie, token_owner_record_cookie, index, &mut instruction, ) .await } #[allow(dead_code)] pub async fn with_upgrade_program_instruction( &mut self, governance_cookie: &GovernanceCookie, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, ) -> Result { let program_buffer_keypair = Keypair::new(); let buffer_authority_keypair = Keypair::new(); // Load gemachain_bpf_rust_upgraded program taken from gemachain test programs let path_buf = find_file("gemachain_bpf_rust_upgraded.so").unwrap(); let program_data = read_file(path_buf); let program_buffer_rent = self .bench .rent .minimum_balance(UpgradeableLoaderState::programdata_len(program_data.len()).unwrap()); let mut instructions = bpf_loader_upgradeable::create_buffer( &self.bench.payer.pubkey(), &program_buffer_keypair.pubkey(), &buffer_authority_keypair.pubkey(), program_buffer_rent, program_data.len(), ) .unwrap(); let chunk_size = 800; for (chunk, i) in program_data.chunks(chunk_size).zip(0..) { instructions.push(bpf_loader_upgradeable::write( &program_buffer_keypair.pubkey(), &buffer_authority_keypair.pubkey(), (i * chunk_size) as u32, chunk.to_vec(), )); } instructions.push(bpf_loader_upgradeable::set_buffer_authority( &program_buffer_keypair.pubkey(), &buffer_authority_keypair.pubkey(), &governance_cookie.address, )); self.bench .process_transaction( &instructions[..], Some(&[&program_buffer_keypair, &buffer_authority_keypair]), ) .await .unwrap(); let mut upgrade_instruction = bpf_loader_upgradeable::upgrade( &governance_cookie.account.governed_account, &program_buffer_keypair.pubkey(), &governance_cookie.address, &governance_cookie.address, ); self.with_instruction( proposal_cookie, token_owner_record_cookie, None, &mut upgrade_instruction, ) .await } #[allow(dead_code)] pub async fn with_nop_instruction( &mut self, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, index: Option, ) -> Result { // Create NOP instruction as a placeholder // Note: The actual instruction is irrelevant because we do not execute it in tests let mut instruction = Instruction { program_id: Pubkey::new_unique(), accounts: vec![], data: vec![], }; self.with_instruction( proposal_cookie, token_owner_record_cookie, index, &mut instruction, ) .await } #[allow(dead_code)] pub async fn with_instruction( &mut self, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, index: Option, instruction: &mut Instruction, ) -> Result { let hold_up_time = 15; let instruction_data: InstructionData = instruction.clone().into(); let instruction_index = index.unwrap_or(proposal_cookie.account.instructions_next_index); proposal_cookie.account.instructions_next_index += 1; let insert_instruction_instruction = insert_instruction( &self.program_id, &proposal_cookie.account.governance, &proposal_cookie.address, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &self.bench.payer.pubkey(), instruction_index, hold_up_time, instruction_data.clone(), ); self.bench .process_transaction( &[insert_instruction_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; let proposal_instruction_address = get_proposal_instruction_address( &self.program_id, &proposal_cookie.address, &instruction_index.to_le_bytes(), ); let proposal_instruction_data = ProposalInstruction { account_type: GovernanceAccountType::ProposalInstruction, instruction_index, hold_up_time, instruction: instruction_data, executed_at: None, execution_status: InstructionExecutionStatus::None, proposal: proposal_cookie.address, }; instruction.accounts = instruction .accounts .iter() .map(|a| AccountMeta { pubkey: a.pubkey, is_signer: false, // Remove signer since the Governance account PDA will be signing the instruction for us is_writable: a.is_writable, }) .collect(); let proposal_instruction_cookie = ProposalInstructionCookie { address: proposal_instruction_address, account: proposal_instruction_data, instruction: instruction.clone(), }; Ok(proposal_instruction_cookie) } #[allow(dead_code)] pub async fn remove_instruction( &mut self, proposal_cookie: &mut ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, proposal_instruction_cookie: &ProposalInstructionCookie, ) -> Result<(), ProgramError> { let remove_instruction_instruction = remove_instruction( &self.program_id, &proposal_cookie.address, &token_owner_record_cookie.address, &token_owner_record_cookie.token_owner.pubkey(), &proposal_instruction_cookie.address, &self.bench.payer.pubkey(), ); self.bench .process_transaction( &[remove_instruction_instruction], Some(&[&token_owner_record_cookie.token_owner]), ) .await?; Ok(()) } #[allow(dead_code)] pub async fn execute_instruction( &mut self, proposal_cookie: &ProposalCookie, proposal_instruction_cookie: &ProposalInstructionCookie, ) -> Result<(), ProgramError> { let execute_instruction_instruction = execute_instruction( &self.program_id, &proposal_cookie.account.governance, &proposal_cookie.address, &proposal_instruction_cookie.address, &proposal_instruction_cookie.instruction.program_id, &proposal_instruction_cookie.instruction.accounts, ); self.bench .process_transaction(&[execute_instruction_instruction], None) .await } #[allow(dead_code)] pub async fn flag_instruction_error( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, proposal_instruction_cookie: &ProposalInstructionCookie, ) -> Result<(), ProgramError> { let governance_authority = token_owner_record_cookie.get_governance_authority(); let flag_instruction_error_ix = flag_instruction_error( &self.program_id, &proposal_cookie.address, &proposal_cookie.account.token_owner_record, &governance_authority.pubkey(), &proposal_instruction_cookie.address, ); self.bench .process_transaction(&[flag_instruction_error_ix], Some(&[&governance_authority])) .await } #[allow(dead_code)] pub async fn get_token_owner_record_account(&mut self, address: &Pubkey) -> TokenOwnerRecord { self.bench .get_borsh_account::(address) .await } #[allow(dead_code)] pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> Realm { self.bench.get_borsh_account::(realm_address).await } #[allow(dead_code)] pub async fn get_realm_config_data( &mut self, realm_config_address: &Pubkey, ) -> RealmConfigAccount { self.bench .get_borsh_account::(realm_config_address) .await } #[allow(dead_code)] pub async fn get_governance_account(&mut self, governance_address: &Pubkey) -> Governance { self.bench .get_borsh_account::(governance_address) .await } #[allow(dead_code)] pub async fn get_proposal_account(&mut self, proposal_address: &Pubkey) -> Proposal { self.bench .get_borsh_account::(proposal_address) .await } #[allow(dead_code)] pub async fn get_vote_record_account(&mut self, vote_record_address: &Pubkey) -> VoteRecord { self.bench .get_borsh_account::(vote_record_address) .await } #[allow(dead_code)] pub async fn get_proposal_instruction_account( &mut self, proposal_instruction_address: &Pubkey, ) -> ProposalInstruction { self.bench .get_borsh_account::(proposal_instruction_address) .await } #[allow(dead_code)] pub async fn get_signatory_record_account( &mut self, proposal_address: &Pubkey, ) -> SignatoryRecord { self.bench .get_borsh_account::(proposal_address) .await } #[allow(dead_code)] async fn get_packed_account(&mut self, address: &Pubkey) -> T { self.bench .context .banks_client .get_packed_account_data::(*address) .await .unwrap() } #[allow(dead_code)] pub async fn advance_clock_past_timestamp(&mut self, unix_timestamp: UnixTimestamp) { let mut clock = self.bench.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.bench .context .warp_to_slot(clock.slot + n * 400) .unwrap(); n += 1; clock = self.bench.get_clock().await; } } #[allow(dead_code)] pub async fn advance_clock_by_min_timespan(&mut self, time_span: u64) { let clock = self.bench.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.bench.get_clock().await; self.bench.context.warp_to_slot(clock.slot + 2).unwrap(); } #[allow(dead_code)] pub async fn get_upgradable_loader_account( &mut self, address: &Pubkey, ) -> UpgradeableLoaderState { self.bench.get_bincode_account(address).await } #[allow(dead_code)] pub async fn get_token_account(&mut self, address: &Pubkey) -> gpl_token::state::Account { self.get_packed_account(address).await } #[allow(dead_code)] pub async fn get_mint_account(&mut self, address: &Pubkey) -> gpl_token::state::Mint { self.get_packed_account(address).await } /// ----------- VoterWeight Addin ----------------------------- #[allow(dead_code)] pub async fn with_voter_weight_addin_deposit( &mut self, token_owner_record_cookie: &mut TokenOwnerRecordCookie, ) -> Result { let voter_weight_record_account = Keypair::new(); // Governance program has no dependency on the voter-weight-addin program and hence we can't use its instruction creator here // and the instruction has to be created manually let accounts = vec![ AccountMeta::new_readonly(self.program_id, false), AccountMeta::new_readonly(token_owner_record_cookie.account.realm, false), AccountMeta::new_readonly( token_owner_record_cookie.account.governing_token_mint, false, ), AccountMeta::new_readonly(token_owner_record_cookie.address, false), AccountMeta::new(voter_weight_record_account.pubkey(), true), AccountMeta::new_readonly(self.bench.payer.pubkey(), true), AccountMeta::new_readonly(system_program::id(), false), ]; let deposit_ix = Instruction { program_id: self.voter_weight_addin_id.unwrap(), accounts, data: vec![1, 100, 0, 0, 0, 0, 0, 0, 0], // 1 - Deposit instruction, 100 amount (u64) }; self.bench .process_transaction(&[deposit_ix], Some(&[&voter_weight_record_account])) .await?; let voter_weight_record_cookie = VoterWeightRecordCookie { address: voter_weight_record_account.pubkey(), account: VoterWeightRecord { account_type: VoterWeightAccountType::VoterWeightRecord, realm: token_owner_record_cookie.account.realm, governing_token_mint: token_owner_record_cookie.account.governing_token_mint, governing_token_owner: token_owner_record_cookie.account.governing_token_owner, voter_weight: 100, voter_weight_expiry: None, }, }; token_owner_record_cookie.voter_weight_record = Some(voter_weight_record_cookie.clone()); Ok(voter_weight_record_cookie) } }