diff --git a/addin-vesting/program/src/error.rs b/addin-vesting/program/src/error.rs index dae0df0..d631f5f 100644 --- a/addin-vesting/program/src/error.rs +++ b/addin-vesting/program/src/error.rs @@ -53,6 +53,12 @@ pub enum VestingError { #[error("Invalid MaxVoterWeightRecord linkage")] InvalidMaxVoterWeightRecordLinkage, + + #[error("VestingIsNotUnderRealm")] + VestingIsNotUnderRealm, + + #[error("InvalidPercentage")] + InvalidPercentage, } impl From for ProgramError { diff --git a/addin-vesting/program/src/instruction.rs b/addin-vesting/program/src/instruction.rs index 6e73c88..ec99a3b 100644 --- a/addin-vesting/program/src/instruction.rs +++ b/addin-vesting/program/src/instruction.rs @@ -25,10 +25,10 @@ pub enum VestingInstruction { /// * Single owner /// 0. `[]` The system program account /// 1. `[]` The spl-token program account - /// 2. `[writable]` The vesting account (vesting owner account: PDA seeds: [seeds]) - /// 3. `[writable]` The vesting spl-token account (vesting balance account) - /// 4. `[signer]` The source spl-token account owner (from account owner) - /// 5. `[writable]` The source spl-token account (from account) + /// 2. `[writable]` The vesting account. PDA seeds: [seeds] + /// 3. `[writable]` The vesting spl-token account + /// 4. `[signer]` The source spl-token account owner + /// 5. `[writable]` The source spl-token account /// 6. `[]` The Vesting Owner account /// 7. `[signer]` Payer /// @@ -51,8 +51,8 @@ pub enum VestingInstruction { /// /// * Single owner /// 0. `[]` The spl-token program account - /// 1. `[writable]` The vesting account (vesting owner account: PDA [seeds]) - /// 2. `[writable]` The vesting spl-token account (vesting balance account) + /// 1. `[writable]` The vesting account. PDA seeds: [seeds] + /// 2. `[writable]` The vesting spl-token account /// 3. `[writable]` The destination spl-token account /// 4. `[signer]` The Vesting Owner account /// @@ -76,7 +76,7 @@ pub enum VestingInstruction { /// Accounts expected by this instruction: /// /// * Single owner - /// 0. `[writable]` The Vesting account (PDA seeds: [seeds] / [realm, seeds]) + /// 0. `[writable]` The Vesting account. PDA seeds: [seeds] /// 1. `[signer]` The Current Vesting Owner account /// 2. `[]` The New Vesting Owner account /// @@ -104,6 +104,26 @@ pub enum VestingInstruction { /// 5. `[]` The Mint account /// 6. `[writable]` The VoterWeightRecord. PDA seeds: ['voter_weight', realm, token_mint, token_owner] CreateVoterWeightRecord, + + /// Set Vote Percentage for calcalate voter_weight from total_amount of deposited tokens + /// + /// Accounts expected by this instruction: + /// + /// * Single owner + /// 0. `[]` The Vesting account. PDA seeds: [seeds] + /// 1. `[]` The Vesting Owner account + /// 2. `[signer]` The Vesting Authority account + /// 3. `[]` The Governance program account + /// 4. `[]` The Realm account + /// 5. `[]` Governing Owner Record. PDA seeds (governance program): ['governance', realm, token_mint, vesting_owner] + /// 6. `[writable]` The VoterWeight Record. PDA seeds: ['voter_weight', realm, token_mint, vesting_owner] + SetVotePercentage { + #[allow(dead_code)] + seeds: [u8; 32], + + #[allow(dead_code)] + vote_percentage: u16, + }, } /// Creates a `Deposit` instruction to create and initialize the vesting token account @@ -252,7 +272,7 @@ pub fn withdraw_with_realm( }) } -/// Creates a `Withdraw` instruction +/// Creates a `ChangeOwner` instruction pub fn change_owner( program_id: &Pubkey, seeds: [u8; 32], @@ -262,8 +282,8 @@ pub fn change_owner( let vesting_account = Pubkey::create_program_address(&[&seeds], program_id)?; let accounts = vec![ AccountMeta::new(vesting_account, false), - AccountMeta::new(*vesting_owner, true), - AccountMeta::new(*new_vesting_owner, false), + AccountMeta::new_readonly(*vesting_owner, true), + AccountMeta::new_readonly(*new_vesting_owner, false), ]; let instruction = VestingInstruction::ChangeOwner { seeds }; @@ -275,7 +295,7 @@ pub fn change_owner( }) } -/// Creates a `Withdraw` instruction +/// Creates a `ChangeOwner` instruction with realm pub fn change_owner_with_realm( program_id: &Pubkey, seeds: [u8; 32], @@ -287,18 +307,18 @@ pub fn change_owner_with_realm( ) -> Result { let vesting_account = Pubkey::create_program_address(&[&seeds], program_id)?; let current_owner_record_account = get_token_owner_record_address(governance_id, realm, mint, vesting_owner); - let current_voting_weight_record_account = get_voter_weight_record_address(program_id, realm, mint, vesting_owner); - let new_voting_weight_record_account = get_voter_weight_record_address(program_id, realm, mint, new_vesting_owner); + let current_voter_weight_record_account = get_voter_weight_record_address(program_id, realm, mint, vesting_owner); + let new_voter_weight_record_account = get_voter_weight_record_address(program_id, realm, mint, new_vesting_owner); let accounts = vec![ AccountMeta::new(vesting_account, false), - AccountMeta::new(*vesting_owner, true), - AccountMeta::new(*new_vesting_owner, false), + AccountMeta::new_readonly(*vesting_owner, true), + AccountMeta::new_readonly(*new_vesting_owner, false), AccountMeta::new_readonly(*governance_id, false), AccountMeta::new_readonly(*realm, false), AccountMeta::new_readonly(current_owner_record_account, false), - AccountMeta::new(current_voting_weight_record_account, false), - AccountMeta::new(new_voting_weight_record_account, false), + AccountMeta::new(current_voter_weight_record_account, false), + AccountMeta::new(new_voter_weight_record_account, false), ]; let instruction = VestingInstruction::ChangeOwner { seeds }; @@ -340,6 +360,42 @@ pub fn create_voter_weight_record( data: instruction.try_to_vec().unwrap(), }) } + +/// Creates a `ChangeVotePercentage` instruction with realm +pub fn set_vote_percentage_with_realm( + program_id: &Pubkey, + seeds: [u8; 32], + vesting_owner: &Pubkey, + vesting_authority: &Pubkey, + governance_id: &Pubkey, + realm: &Pubkey, + mint: &Pubkey, + vote_percentage: u16, +) -> Result { + let vesting_account = Pubkey::create_program_address(&[&seeds], program_id)?; + let token_owner_record_account = get_token_owner_record_address(governance_id, realm, mint, vesting_owner); + let voter_weight_record_account = get_voter_weight_record_address(program_id, realm, mint, vesting_owner); + let accounts = vec![ + AccountMeta::new_readonly(vesting_account, false), + AccountMeta::new_readonly(*vesting_owner, false), + AccountMeta::new_readonly(*vesting_authority, true), + AccountMeta::new_readonly(*governance_id, false), + AccountMeta::new_readonly(*realm, false), + AccountMeta::new_readonly(token_owner_record_account, false), + AccountMeta::new(voter_weight_record_account, false), + ]; + + let instruction = VestingInstruction::SetVotePercentage { seeds, vote_percentage }; + + Ok(Instruction { + program_id: *program_id, + accounts, + data: instruction.try_to_vec().unwrap(), + }) +} + + + #[cfg(test)] mod test { use super::*; diff --git a/addin-vesting/program/src/processor.rs b/addin-vesting/program/src/processor.rs index c6025ac..501194f 100644 --- a/addin-vesting/program/src/processor.rs +++ b/addin-vesting/program/src/processor.rs @@ -145,7 +145,7 @@ impl Processor { payer_account, voter_weight_record_account, system_program_account, - |record| {record.voter_weight = total_amount; Ok(())}, + |record| {record.increase_total_amount(total_amount)}, )?; } else { let mut voter_weight_record = get_voter_weight_record_data_checked( @@ -155,8 +155,7 @@ impl Processor { &vesting_token_account_data.mint, vesting_owner_account.key)?; - let voter_weight = &mut voter_weight_record.voter_weight; - *voter_weight = voter_weight.checked_add(total_amount).ok_or(VestingError::OverflowAmount)?; + voter_weight_record.increase_total_amount(total_amount)?; voter_weight_record.serialize(&mut *voter_weight_record_account.data.borrow_mut())?; } @@ -296,8 +295,7 @@ impl Processor { &vesting_record.mint, vesting_owner_account.key)?; - let voter_weight = &mut voter_weight_record.voter_weight; - *voter_weight = voter_weight.checked_sub(total_amount_to_transfer).ok_or(VestingError::UnderflowAmount)?; + voter_weight_record.decrease_total_amount(total_amount_to_transfer)?; voter_weight_record.serialize(&mut *voter_weight_record_account.data.borrow_mut())?; let mut max_voter_weight_record = get_max_voter_weight_record_data_checked( @@ -390,8 +388,8 @@ impl Processor { realm_account.key, &vesting_record.mint, vesting_owner_account.key)?; - let voter_weight = &mut voter_weight_record.voter_weight; - *voter_weight = voter_weight.checked_sub(total_amount).ok_or(VestingError::UnderflowAmount)?; + + voter_weight_record.decrease_total_amount(total_amount)?; voter_weight_record.serialize(&mut *voter_weight_record_account.data.borrow_mut())?; let mut new_voter_weight_record = get_voter_weight_record_data_checked( @@ -401,8 +399,7 @@ impl Processor { &vesting_record.mint, new_vesting_owner_account.key)?; - let new_voter_weight = &mut new_voter_weight_record.voter_weight; - *new_voter_weight = new_voter_weight.checked_add(total_amount).ok_or(VestingError::OverflowAmount)?; + new_voter_weight_record.increase_total_amount(total_amount)?; new_voter_weight_record.serialize(&mut *new_voter_weight_record_account.data.borrow_mut())?; } @@ -438,6 +435,62 @@ impl Processor { Ok(()) } + pub fn process_set_vote_percentage( + program_id: &Pubkey, + accounts: &[AccountInfo], + seeds: [u8; 32], + vote_percentage: u16, + ) -> ProgramResult { + let accounts_iter = &mut accounts.iter(); + + let vesting_account = next_account_info(accounts_iter)?; + let vesting_owner_account = next_account_info(accounts_iter)?; + let vesting_authority_account = next_account_info(accounts_iter)?; + let governance_account = next_account_info(accounts_iter)?; + let realm_account = next_account_info(accounts_iter)?; + let owner_record_account = next_account_info(accounts_iter)?; + let voter_weight_record_account = next_account_info(accounts_iter)?; + + let vesting_account_key = Pubkey::create_program_address(&[&seeds], program_id)?; + if vesting_account_key != *vesting_account.key { + return Err(VestingError::InvalidVestingAccount.into()); + } + + let vesting_record = get_account_data::(program_id, vesting_account)?; + + let expected_realm_account = vesting_record.realm.ok_or(VestingError::VestingIsNotUnderRealm)?; + + if *realm_account.key != expected_realm_account { + return Err(VestingError::InvalidRealmAccount.into()) + }; + + let realm_data = get_realm_data(governance_account.key, realm_account)?; + realm_data.assert_is_valid_governing_token_mint(&vesting_record.mint)?; + + let owner_record_data = get_token_owner_record_data_for_seeds( + governance_account.key, + owner_record_account, + &get_token_owner_record_address_seeds( + realm_account.key, + &vesting_record.mint, + vesting_owner_account.key, + ), + )?; + owner_record_data.assert_token_owner_or_delegate_is_signer(vesting_authority_account)?; + + let mut voter_weight_record = get_voter_weight_record_data_checked( + program_id, + voter_weight_record_account, + realm_account.key, + &vesting_record.mint, + vesting_owner_account.key)?; + + voter_weight_record.set_vote_percentage(vote_percentage)?; + voter_weight_record.serialize(&mut *voter_weight_record_account.data.borrow_mut())?; + + Ok(()) + } + pub fn process_instruction( program_id: &Pubkey, accounts: &[AccountInfo], @@ -461,6 +514,9 @@ impl Processor { VestingInstruction::CreateVoterWeightRecord => { Self::process_create_voter_weight_record(program_id, accounts) } + VestingInstruction::SetVotePercentage {seeds, vote_percentage} => { + Self::process_set_vote_percentage(program_id, accounts, seeds, vote_percentage) + } } } } diff --git a/addin-vesting/program/src/voter_weight.rs b/addin-vesting/program/src/voter_weight.rs index faa835c..e8d1b12 100644 --- a/addin-vesting/program/src/voter_weight.rs +++ b/addin-vesting/program/src/voter_weight.rs @@ -1,17 +1,90 @@ use crate::error::VestingError; +use std::convert::TryInto; use solana_program::{ pubkey::Pubkey, program_error::ProgramError, + program_pack::IsInitialized, account_info::AccountInfo, rent::Rent, sysvar::Sysvar, }; -use spl_governance::addins::voter_weight::get_voter_weight_record_data; -use spl_governance_tools::account::create_and_serialize_account_signed; +use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use spl_governance_tools::account::{ + AccountMaxSize, + create_and_serialize_account_signed, + get_account_data, +}; + +use spl_governance_addin_api::voter_weight::VoterWeightRecord; + +/// ExtendedVoterWeightRecord account +/// The account is used as an api interface to provide voting power to the governance program +/// and to save information about total amount of deposited token +#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct ExtendedVoterWeightRecord { + base: VoterWeightRecord, + + /// ExtendedVoterWeightRecord discriminator sha256("account:ExtendedVoterWeightRecord")[..8] + /// Note: The discriminator size must match the addin implementing program discriminator size + /// to ensure it's stored in the private space of the account data and it's unique + account_discriminator: [u8; 8], + + /// Total number of tokens owned by the account + total_amount: u64, + + /// Percentage of the total number of tokens for calculating the voting weight + /// (in hundredths of a percent) + vote_percentage: u16, +} + +impl ExtendedVoterWeightRecord { + /// sha256("account:ExtendedVoterWeightRecord")[..8] + pub const ACCOUNT_DISCRIMINATOR: [u8; 8] = [0x49, 0x6b, 0x79, 0x9a, 0xfd, 0x90, 0x5d, 0xe7]; + + fn recalculate_voter_weight(&mut self) -> Result<(), ProgramError> { + let voter_weight = (self.total_amount as u128) + .checked_mul(self.vote_percentage.into()).ok_or(VestingError::OverflowAmount)? + .checked_div(10000).ok_or(VestingError::OverflowAmount)?; + self.base.voter_weight = voter_weight.try_into().map_err(|_| VestingError::OverflowAmount)?; + Ok(()) + } + + /// Increase total_amount to specified value and recalculate current voter_weight + pub fn increase_total_amount(&mut self, value: u64) -> Result<(), ProgramError> { + self.total_amount = self.total_amount.checked_add(value).ok_or(VestingError::OverflowAmount)?; + self.recalculate_voter_weight()?; + Ok(()) + } -pub use spl_governance_addin_api::voter_weight::VoterWeightRecord; + /// Decrease total_amount to specified value and recalculate current voter_weight + pub fn decrease_total_amount(&mut self, value: u64) -> Result<(), ProgramError> { + self.total_amount = self.total_amount.checked_sub(value).ok_or(VestingError::UnderflowAmount)?; + self.recalculate_voter_weight()?; + Ok(()) + } + + /// Set new value for vote_percentage and recalculate current voter_weight + pub fn set_vote_percentage(&mut self, value: u16) -> Result<(), ProgramError> { + if value > 10000 { + return Err(VestingError::InvalidPercentage.into()); + } + self.vote_percentage = value; + self.recalculate_voter_weight()?; + Ok(()) + } +} + +impl AccountMaxSize for ExtendedVoterWeightRecord {} + +impl IsInitialized for ExtendedVoterWeightRecord { + fn is_initialized(&self) -> bool { + self.account_discriminator == ExtendedVoterWeightRecord::ACCOUNT_DISCRIMINATOR + // Check for legacy discriminator which is not compatible with Anchor but is used by older plugins + || self.account_discriminator == *b"496b799a" + } +} -/// Returns VoterWeightRecord PDA seeds +/// Returns ExtendedVoterWeightRecord PDA seeds pub fn get_voter_weight_record_seeds<'a>( realm: &'a Pubkey, mint: &'a Pubkey, @@ -20,17 +93,25 @@ pub fn get_voter_weight_record_seeds<'a>( [b"voter_weight", realm.as_ref(), mint.as_ref(), owner.as_ref()] } -/// Returns VoterWeightRecord PDA address +/// Returns ExtendedVoterWeightRecord PDA address pub fn get_voter_weight_record_address(program_id: &Pubkey, realm: &Pubkey, mint: &Pubkey, owner: &Pubkey) -> Pubkey { Pubkey::find_program_address(&get_voter_weight_record_seeds(realm, mint, owner), program_id).0 } -/// Deserializes VoterWeightRecord account and checks owner program +/// Deserializes ExtendedVoterWeightRecord account and checks owner program +pub fn get_voter_weight_record_data( + program_id: &Pubkey, + voter_weight_record_info: &AccountInfo, +) -> Result { + get_account_data::(program_id, voter_weight_record_info) +} + +/// Deserializes ExtendedVoterWeightRecord account and checks owner program pub fn get_voter_weight_record_data_for_seeds( program_id: &Pubkey, voter_weight_record_info: &AccountInfo, voter_weight_record_seeds: &[&[u8]], -) -> Result { +) -> Result { let (voter_weight_record_address, _) = Pubkey::find_program_address(voter_weight_record_seeds, program_id); @@ -41,19 +122,19 @@ pub fn get_voter_weight_record_data_for_seeds( get_voter_weight_record_data(program_id, voter_weight_record_info) } -/// Deserialize VoterWeightRecord account and checks owner program and linkage +/// Deserialize ExtendedVoterWeightRecord account and checks owner program and linkage pub fn get_voter_weight_record_data_checked( program_id: &Pubkey, record_info: &AccountInfo, realm: &Pubkey, mint: &Pubkey, owner: &Pubkey, -) -> Result { +) -> Result { let seeds = get_voter_weight_record_seeds(realm, mint, owner); let record = get_voter_weight_record_data_for_seeds(program_id, record_info, &seeds)?; - if record.realm != *realm || - record.governing_token_mint != *mint || - record.governing_token_owner != *owner { + if record.base.realm != *realm || + record.base.governing_token_mint != *mint || + record.base.governing_token_owner != *owner { return Err(VestingError::InvalidVoterWeightRecordLinkage.into()) } Ok(record) @@ -71,21 +152,26 @@ pub fn create_voter_weight_record<'a, I>( system_program_account: &AccountInfo<'a>, initialize_func: I ) -> Result<(), ProgramError> -where I: FnOnce(&mut VoterWeightRecord) -> Result<(), ProgramError> +where I: FnOnce(&mut ExtendedVoterWeightRecord) -> Result<(), ProgramError> { - let mut record_data = VoterWeightRecord { - account_discriminator: VoterWeightRecord::ACCOUNT_DISCRIMINATOR, - realm: *realm, - governing_token_mint: *mint, - governing_token_owner: *owner, - voter_weight: 0, - voter_weight_expiry: None, - weight_action: None, - weight_action_target: None, - reserved: [0u8; 8], + let mut record_data = ExtendedVoterWeightRecord { + base: VoterWeightRecord { + account_discriminator: VoterWeightRecord::ACCOUNT_DISCRIMINATOR, + realm: *realm, + governing_token_mint: *mint, + governing_token_owner: *owner, + voter_weight: 0, + voter_weight_expiry: None, + weight_action: None, + weight_action_target: None, + reserved: [0u8; 8], + }, + account_discriminator: ExtendedVoterWeightRecord::ACCOUNT_DISCRIMINATOR, + total_amount: 0, + vote_percentage: 10_000, }; initialize_func(&mut record_data)?; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_account, record_account, &record_data, diff --git a/addin-vesting/program/tests/functional.rs b/addin-vesting/program/tests/functional.rs index 24cac1c..8f97e51 100644 --- a/addin-vesting/program/tests/functional.rs +++ b/addin-vesting/program/tests/functional.rs @@ -17,21 +17,27 @@ use solana_sdk::{ use spl_governance_addin_vesting::{ entrypoint::process_instruction, state::{VestingSchedule, VestingRecord}, - voter_weight::{VoterWeightRecord, get_voter_weight_record_address}, + voter_weight::{ExtendedVoterWeightRecord, get_voter_weight_record_address}, max_voter_weight::{MaxVoterWeightRecord, get_max_voter_weight_record_address}, instruction::{ deposit, withdraw, change_owner, + deposit_with_realm, withdraw_with_realm, change_owner_with_realm, create_voter_weight_record, + set_vote_percentage_with_realm, }, }; use spl_token::{self, instruction::{initialize_mint, initialize_account, mint_to}}; use spl_governance::{ - instruction::{create_realm, create_token_owner_record}, + instruction::{ + create_realm, + create_token_owner_record, + set_governance_delegate, + }, state::{ enums::MintMaxVoteWeightSource, realm::get_realm_address, @@ -61,7 +67,7 @@ async fn test_token_vesting() { let vesting_token_account = Keypair::new(); let mut program_test = ProgramTest::new( - "token_vesting", + "spl_governance_addin_vesting", program_id, processor!(process_instruction), ); @@ -209,6 +215,7 @@ async fn test_token_vesting_with_realm() { let new_destination_account = Keypair::new(); let new_destination_token_account = Keypair::new(); + let new_destination_delegate = Keypair::new(); let mut seeds = [42u8; 32]; let (vesting_account_key, bump) = Pubkey::find_program_address(&[&seeds[..31]], &program_id); @@ -221,7 +228,7 @@ async fn test_token_vesting_with_realm() { let vesting_token_account2 = Keypair::new(); let mut program_test = ProgramTest::new( - "token_vesting", + "spl_governance_addin_vesting", program_id, processor!(process_instruction), ); @@ -393,6 +400,55 @@ async fn test_token_vesting_with_realm() { banks_client.process_transaction(change_owner_transaction).await.unwrap(); + let set_governance_delegate_instructions = [ + set_governance_delegate( + &governance_id, + &new_destination_account.pubkey(), + &realm_address, + &mint.pubkey(), + &new_destination_account.pubkey(), + &Some(new_destination_delegate.pubkey()), + ), + ]; + let mut set_governance_delegate_transaction = Transaction::new_with_payer( + &set_governance_delegate_instructions, + Some(&payer.pubkey()), + ); + set_governance_delegate_transaction.partial_sign(&[&payer, &new_destination_account], recent_blockhash); + banks_client.process_transaction(set_governance_delegate_transaction).await.unwrap(); + + + let set_vote_percentage_instructions = [ + set_vote_percentage_with_realm( + &program_id, + seeds.clone(), + &new_destination_account.pubkey(), + &new_destination_delegate.pubkey(), + &governance_id, + &realm_address, + &mint.pubkey(), + 30*100, + ).unwrap(), + ]; + let mut set_vote_percentage_transaction = Transaction::new_with_payer( + &set_vote_percentage_instructions, + Some(&payer.pubkey()), + ); + set_vote_percentage_transaction.partial_sign(&[&payer, &new_destination_delegate], recent_blockhash); + banks_client.process_transaction(set_vote_percentage_transaction).await.unwrap(); + + + let voter_weight_record_address2 = get_voter_weight_record_address( + &program_id, + &realm_address, + &mint.pubkey(), + &new_destination_account.pubkey() + ); + let voter_weight_record_account2 = banks_client.get_account(voter_weight_record_address2).await.unwrap().unwrap(); + let voter_weight_record2: ExtendedVoterWeightRecord = try_from_slice_unchecked(&voter_weight_record_account2.data).unwrap(); + println!("VoterWeightRecord before withdraw: {:?}", voter_weight_record2); + + let withdraw_instrictions = [ withdraw_with_realm( &program_id, @@ -426,7 +482,7 @@ async fn test_token_vesting_with_realm() { &destination_account.pubkey() ); let voter_weight_record_account = banks_client.get_account(voter_weight_record_address).await.unwrap().unwrap(); - let voter_weight_record: VoterWeightRecord = try_from_slice_unchecked(&voter_weight_record_account.data).unwrap(); + let voter_weight_record: ExtendedVoterWeightRecord = try_from_slice_unchecked(&voter_weight_record_account.data).unwrap(); println!("VoterWeightRecord: {:?}", voter_weight_record); let voter_weight_record_address2 = get_voter_weight_record_address( @@ -436,7 +492,7 @@ async fn test_token_vesting_with_realm() { &new_destination_account.pubkey() ); let voter_weight_record_account2 = banks_client.get_account(voter_weight_record_address2).await.unwrap().unwrap(); - let voter_weight_record2: VoterWeightRecord = try_from_slice_unchecked(&voter_weight_record_account2.data).unwrap(); + let voter_weight_record2: ExtendedVoterWeightRecord = try_from_slice_unchecked(&voter_weight_record_account2.data).unwrap(); println!("VoterWeightRecord: {:?}", voter_weight_record2); let max_voter_weight_record_address = get_max_voter_weight_record_address(