Governance: add v2 account padding (#2842)
* chore: create RealmV2 account type * feat: add padding to Realm account * chore: move RealmConfigArgsV1 and GovernanceInstructionV1 to tests * chore: Add RealmV2 comments * chore: rename TokenOwnerRecord to V2 and add reserved space * feat: translate TokenOwnerRecordV1 * chore: rename Governance to GovernanceV2 * feat: add reserved_v2 to Governance * chore: make clippy happy * feat: translate GovernanceV1 accounts * chore: fix chat build * fix: update seeds logic for governance V1 accounts * fix: add check for RealmV1 and GovernanceV1 * chore: add padding to ProposalTransaction * chore: add padding to VoteRecordV2 * chore: add padding to SignatoryRecord V2 * feat: translate SignatoryRecordV1 account
This commit is contained in:
parent
925f8f5711
commit
ebc91e871a
|
@ -5,7 +5,7 @@ use solana_program::{
|
|||
account_info::AccountInfo, clock::UnixTimestamp, program_error::ProgramError, pubkey::Pubkey,
|
||||
};
|
||||
|
||||
use spl_governance_tools::account::{assert_is_valid_account, AccountMaxSize};
|
||||
use spl_governance_tools::account::{assert_is_valid_account_of_type, AccountMaxSize};
|
||||
|
||||
/// Defines all GovernanceChat accounts types
|
||||
#[repr(C)]
|
||||
|
@ -67,7 +67,7 @@ pub fn assert_is_valid_chat_message(
|
|||
program_id: &Pubkey,
|
||||
chat_message_info: &AccountInfo,
|
||||
) -> Result<(), ProgramError> {
|
||||
assert_is_valid_account(
|
||||
assert_is_valid_account_of_type(
|
||||
chat_message_info,
|
||||
GovernanceChatAccountType::ChatMessage,
|
||||
program_id,
|
||||
|
|
|
@ -223,7 +223,7 @@ impl GovernanceChatProgramTest {
|
|||
let create_governance_ix = create_governance(
|
||||
&self.governance_program_id,
|
||||
&realm_address,
|
||||
&governed_account_address,
|
||||
Some(&governed_account_address),
|
||||
&token_owner_record_address,
|
||||
&self.bench.payer.pubkey(),
|
||||
&token_owner.pubkey(),
|
||||
|
|
|
@ -7,7 +7,7 @@ use solana_program::{
|
|||
use spl_governance_addin_api::voter_weight::{VoterWeightAction, VoterWeightRecord};
|
||||
use spl_governance_tools::account::get_account_data;
|
||||
|
||||
use crate::{error::GovernanceError, state::token_owner_record::TokenOwnerRecord};
|
||||
use crate::{error::GovernanceError, state::token_owner_record::TokenOwnerRecordV2};
|
||||
|
||||
/// Asserts the VoterWeightRecord hasn't expired and matches the given action and target if specified
|
||||
pub fn assert_is_valid_voter_weight(
|
||||
|
@ -53,7 +53,7 @@ pub fn get_voter_weight_record_data(
|
|||
pub fn get_voter_weight_record_data_for_token_owner_record(
|
||||
program_id: &Pubkey,
|
||||
voter_weight_record_info: &AccountInfo,
|
||||
token_owner_record: &TokenOwnerRecord,
|
||||
token_owner_record: &TokenOwnerRecordV2,
|
||||
) -> Result<VoterWeightRecord, ProgramError> {
|
||||
let voter_weight_record_data =
|
||||
get_voter_weight_record_data(program_id, voter_weight_record_info)?;
|
||||
|
|
|
@ -12,7 +12,7 @@ use spl_governance_tools::account::create_and_serialize_account_signed;
|
|||
use crate::state::{
|
||||
enums::GovernanceAccountType,
|
||||
proposal::get_proposal_data,
|
||||
signatory_record::{get_signatory_record_address_seeds, SignatoryRecord},
|
||||
signatory_record::{get_signatory_record_address_seeds, SignatoryRecordV2},
|
||||
token_owner_record::get_token_owner_record_data_for_proposal_owner,
|
||||
};
|
||||
|
||||
|
@ -46,14 +46,15 @@ pub fn process_add_signatory(
|
|||
|
||||
token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
|
||||
|
||||
let signatory_record_data = SignatoryRecord {
|
||||
account_type: GovernanceAccountType::SignatoryRecord,
|
||||
let signatory_record_data = SignatoryRecordV2 {
|
||||
account_type: GovernanceAccountType::SignatoryRecordV2,
|
||||
proposal: *proposal_info.key,
|
||||
signatory,
|
||||
signed_off: false,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<SignatoryRecord>(
|
||||
create_and_serialize_account_signed::<SignatoryRecordV2>(
|
||||
payer_info,
|
||||
signatory_record_info,
|
||||
&signatory_record_data,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
clock::Clock,
|
||||
|
|
|
@ -26,8 +26,6 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
|
||||
/// Processes CastVote instruction
|
||||
pub fn process_cast_vote(
|
||||
program_id: &Pubkey,
|
||||
|
@ -176,6 +174,8 @@ pub fn process_cast_vote(
|
|||
governance_data.serialize(&mut *governance_info.data.borrow_mut())?;
|
||||
}
|
||||
|
||||
let governing_token_owner = voter_token_owner_record_data.governing_token_owner;
|
||||
|
||||
voter_token_owner_record_data
|
||||
.serialize(&mut *voter_token_owner_record_info.data.borrow_mut())?;
|
||||
|
||||
|
@ -185,10 +185,11 @@ pub fn process_cast_vote(
|
|||
let vote_record_data = VoteRecordV2 {
|
||||
account_type: GovernanceAccountType::VoteRecordV2,
|
||||
proposal: *proposal_info.key,
|
||||
governing_token_owner: voter_token_owner_record_data.governing_token_owner,
|
||||
governing_token_owner,
|
||||
voter_weight,
|
||||
vote,
|
||||
is_relinquished: false,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<VoteRecordV2>(
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
use crate::state::{
|
||||
enums::GovernanceAccountType,
|
||||
governance::{
|
||||
assert_valid_create_governance_args, get_governance_address_seeds, Governance,
|
||||
GovernanceConfig,
|
||||
assert_valid_create_governance_args, get_governance_address_seeds, GovernanceConfig,
|
||||
GovernanceV2,
|
||||
},
|
||||
realm::get_realm_data,
|
||||
};
|
||||
|
@ -51,17 +51,18 @@ pub fn process_create_governance(
|
|||
account_info_iter, // realm_config_info 7, voter_weight_record_info 8
|
||||
)?;
|
||||
|
||||
let governance_data = Governance {
|
||||
account_type: GovernanceAccountType::Governance,
|
||||
let governance_data = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::GovernanceV2,
|
||||
realm: *realm_info.key,
|
||||
governed_account: *governed_account_info.key,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<Governance>(
|
||||
create_and_serialize_account_signed::<GovernanceV2>(
|
||||
payer_info,
|
||||
governance_info,
|
||||
&governance_data,
|
||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
|||
state::{
|
||||
enums::GovernanceAccountType,
|
||||
governance::{
|
||||
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
|
||||
GovernanceConfig,
|
||||
assert_valid_create_governance_args, get_mint_governance_address_seeds,
|
||||
GovernanceConfig, GovernanceV2,
|
||||
},
|
||||
realm::get_realm_data,
|
||||
},
|
||||
|
@ -63,17 +63,18 @@ pub fn process_create_mint_governance(
|
|||
account_info_iter, // realm_config_info 9, voter_weight_record_info 10
|
||||
)?;
|
||||
|
||||
let mint_governance_data = Governance {
|
||||
account_type: GovernanceAccountType::MintGovernance,
|
||||
let mint_governance_data = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::MintGovernanceV2,
|
||||
realm: *realm_info.key,
|
||||
governed_account: *governed_mint_info.key,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<Governance>(
|
||||
create_and_serialize_account_signed::<GovernanceV2>(
|
||||
payer_info,
|
||||
mint_governance_info,
|
||||
&mint_governance_data,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! Program state processor
|
||||
|
||||
use crate::{
|
||||
state::governance::Governance,
|
||||
state::governance::GovernanceV2,
|
||||
state::{
|
||||
enums::GovernanceAccountType,
|
||||
governance::{
|
||||
|
@ -63,17 +63,18 @@ pub fn process_create_program_governance(
|
|||
account_info_iter, // realm_config_info 10, voter_weight_record_info 11
|
||||
)?;
|
||||
|
||||
let program_governance_data = Governance {
|
||||
account_type: GovernanceAccountType::ProgramGovernance,
|
||||
let program_governance_data = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::ProgramGovernanceV2,
|
||||
realm: *realm_info.key,
|
||||
governed_account: *governed_program_info.key,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<Governance>(
|
||||
create_and_serialize_account_signed::<GovernanceV2>(
|
||||
payer_info,
|
||||
program_governance_info,
|
||||
&program_governance_data,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
clock::Clock,
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
|||
enums::GovernanceAccountType,
|
||||
realm::{
|
||||
assert_valid_realm_config_args, get_governing_token_holding_address_seeds,
|
||||
get_realm_address_seeds, Realm, RealmConfig, RealmConfigArgs,
|
||||
get_realm_address_seeds, RealmConfig, RealmConfigArgs, RealmV2,
|
||||
},
|
||||
realm_config::{get_realm_config_address_seeds, RealmConfigAccount},
|
||||
},
|
||||
|
@ -125,8 +125,8 @@ pub fn process_create_realm(
|
|||
)?;
|
||||
}
|
||||
|
||||
let realm_data = Realm {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
let realm_data = RealmV2 {
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
community_mint: *governance_token_mint_info.key,
|
||||
|
||||
name: name.clone(),
|
||||
|
@ -143,9 +143,10 @@ pub fn process_create_realm(
|
|||
use_max_community_voter_weight_addin: config_args.use_max_community_voter_weight_addin,
|
||||
},
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<Realm>(
|
||||
create_and_serialize_account_signed::<RealmV2>(
|
||||
payer_info,
|
||||
realm_info,
|
||||
&realm_data,
|
||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
|||
state::{
|
||||
enums::GovernanceAccountType,
|
||||
governance::{
|
||||
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
|
||||
GovernanceConfig,
|
||||
assert_valid_create_governance_args, get_token_governance_address_seeds,
|
||||
GovernanceConfig, GovernanceV2,
|
||||
},
|
||||
realm::get_realm_data,
|
||||
},
|
||||
|
@ -61,17 +61,18 @@ pub fn process_create_token_governance(
|
|||
account_info_iter, // realm_config_info 9, voter_weight_record_info 10
|
||||
)?;
|
||||
|
||||
let token_governance_data = Governance {
|
||||
account_type: GovernanceAccountType::TokenGovernance,
|
||||
let token_governance_data = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::TokenGovernanceV2,
|
||||
realm: *realm_info.key,
|
||||
governed_account: *governed_token_info.key,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<Governance>(
|
||||
create_and_serialize_account_signed::<GovernanceV2>(
|
||||
payer_info,
|
||||
token_governance_info,
|
||||
&token_governance_data,
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
state::{
|
||||
enums::GovernanceAccountType,
|
||||
realm::get_realm_data,
|
||||
token_owner_record::{get_token_owner_record_address_seeds, TokenOwnerRecord},
|
||||
token_owner_record::{get_token_owner_record_address_seeds, TokenOwnerRecordV2},
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -40,8 +40,8 @@ pub fn process_create_token_owner_record(
|
|||
return Err(GovernanceError::TokenOwnerRecordAlreadyExists.into());
|
||||
}
|
||||
|
||||
let token_owner_record_data = TokenOwnerRecord {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
let token_owner_record_data = TokenOwnerRecordV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
realm: *realm_info.key,
|
||||
governing_token_owner: *governing_token_owner_info.key,
|
||||
governing_token_deposit_amount: 0,
|
||||
|
@ -51,6 +51,7 @@ pub fn process_create_token_owner_record(
|
|||
total_votes_count: 0,
|
||||
outstanding_proposal_count: 0,
|
||||
reserved: [0; 7],
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed(
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
|
@ -17,7 +16,7 @@ use crate::{
|
|||
realm::get_realm_data,
|
||||
token_owner_record::{
|
||||
get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds,
|
||||
TokenOwnerRecord,
|
||||
TokenOwnerRecordV2,
|
||||
},
|
||||
},
|
||||
tools::spl_token::{get_spl_token_mint, get_spl_token_owner, transfer_spl_tokens},
|
||||
|
@ -79,8 +78,8 @@ pub fn process_deposit_governing_tokens(
|
|||
return Err(GovernanceError::GoverningTokenOwnerMustSign.into());
|
||||
}
|
||||
|
||||
let token_owner_record_data = TokenOwnerRecord {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
let token_owner_record_data = TokenOwnerRecordV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
realm: *realm_info.key,
|
||||
governing_token_owner: *governing_token_owner_info.key,
|
||||
governing_token_deposit_amount: amount,
|
||||
|
@ -90,6 +89,7 @@ pub fn process_deposit_governing_tokens(
|
|||
total_votes_count: 0,
|
||||
outstanding_proposal_count: 0,
|
||||
reserved: [0; 7],
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed(
|
||||
|
|
|
@ -15,8 +15,6 @@ use crate::state::{
|
|||
token_owner_record::get_token_owner_record_data_for_proposal_owner,
|
||||
};
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
|
||||
/// Processes FinalizeVote instruction
|
||||
pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
|
|
@ -94,6 +94,7 @@ pub fn process_insert_transaction(
|
|||
executed_at: None,
|
||||
execution_status: TransactionExecutionStatus::None,
|
||||
proposal: *proposal_info.key,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
create_and_serialize_account_signed::<ProposalTransactionV2>(
|
||||
|
|
|
@ -20,8 +20,6 @@ use crate::{
|
|||
},
|
||||
};
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
|
||||
/// Processes RelinquishVote instruction
|
||||
pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||
let account_info_iter = &mut accounts.iter();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
clock::Clock,
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
//! Program state processor
|
||||
|
||||
use borsh::BorshSerialize;
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
entrypoint::ProgramResult,
|
||||
|
|
|
@ -10,22 +10,22 @@ pub enum GovernanceAccountType {
|
|||
Uninitialized,
|
||||
|
||||
/// Top level aggregation for governances with Community Token (and optional Council Token)
|
||||
Realm,
|
||||
RealmV1,
|
||||
|
||||
/// Token Owner Record for given governing token owner within a Realm
|
||||
TokenOwnerRecord,
|
||||
TokenOwnerRecordV1,
|
||||
|
||||
/// Governance account
|
||||
Governance,
|
||||
GovernanceV1,
|
||||
|
||||
/// Program Governance account
|
||||
ProgramGovernance,
|
||||
ProgramGovernanceV1,
|
||||
|
||||
/// Proposal account for Governance account. A single Governance account can have multiple Proposal accounts
|
||||
ProposalV1,
|
||||
|
||||
/// Proposal Signatory account
|
||||
SignatoryRecord,
|
||||
SignatoryRecordV1,
|
||||
|
||||
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
|
||||
VoteRecordV1,
|
||||
|
@ -34,12 +34,12 @@ pub enum GovernanceAccountType {
|
|||
ProposalInstructionV1,
|
||||
|
||||
/// Mint Governance account
|
||||
MintGovernance,
|
||||
MintGovernanceV1,
|
||||
|
||||
/// Token Governance account
|
||||
TokenGovernance,
|
||||
TokenGovernanceV1,
|
||||
|
||||
/// Realm config account
|
||||
/// Realm config account (introduced in V2)
|
||||
RealmConfig,
|
||||
|
||||
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
|
||||
|
@ -54,8 +54,40 @@ pub enum GovernanceAccountType {
|
|||
/// V2 adds support for multiple vote options
|
||||
ProposalV2,
|
||||
|
||||
/// Program metadata account. It stores information about the particular SPL-Governance program instance
|
||||
/// Program metadata account (introduced in V2)
|
||||
/// It stores information about the particular SPL-Governance program instance
|
||||
ProgramMetadata,
|
||||
|
||||
/// Top level aggregation for governances with Community Token (and optional Council Token)
|
||||
/// V2 adds the following fields:
|
||||
/// 1) use_community_voter_weight_addin and use_max_community_voter_weight_addin to RealmConfig
|
||||
/// 2) voting_proposal_count
|
||||
/// 3) extra reserved space reserved_v2
|
||||
RealmV2,
|
||||
|
||||
/// Token Owner Record for given governing token owner within a Realm
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
TokenOwnerRecordV2,
|
||||
|
||||
/// Governance account
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
GovernanceV2,
|
||||
|
||||
/// Program Governance account
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
ProgramGovernanceV2,
|
||||
|
||||
/// Mint Governance account
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
MintGovernanceV2,
|
||||
|
||||
/// Token Governance account
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
TokenGovernanceV2,
|
||||
|
||||
/// Proposal Signatory account
|
||||
/// V2 adds extra reserved space reserved_v2
|
||||
SignatoryRecordV2,
|
||||
}
|
||||
|
||||
impl Default for GovernanceAccountType {
|
||||
|
|
|
@ -1,19 +1,21 @@
|
|||
//! Governance Account
|
||||
use borsh::maybestd::io::Write;
|
||||
|
||||
use crate::{
|
||||
error::GovernanceError,
|
||||
state::{
|
||||
enums::{GovernanceAccountType, VoteThresholdPercentage, VoteTipping},
|
||||
legacy::{is_governance_v1_account_type, GovernanceV1},
|
||||
realm::assert_is_valid_realm,
|
||||
},
|
||||
};
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
||||
pubkey::Pubkey,
|
||||
account_info::AccountInfo, borsh::try_from_slice_unchecked, program_error::ProgramError,
|
||||
program_pack::IsInitialized, pubkey::Pubkey,
|
||||
};
|
||||
use spl_governance_tools::{
|
||||
account::{assert_is_valid_account2, get_account_data, AccountMaxSize},
|
||||
account::{assert_is_valid_account_of_types, get_account_data, AccountMaxSize},
|
||||
error::GovernanceToolsError,
|
||||
};
|
||||
|
||||
|
@ -49,7 +51,7 @@ pub struct GovernanceConfig {
|
|||
/// Governance Account
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct Governance {
|
||||
pub struct GovernanceV2 {
|
||||
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
|
@ -78,33 +80,43 @@ pub struct Governance {
|
|||
|
||||
/// The number of proposals in voting state in the Governance
|
||||
pub voting_proposal_count: u16,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 128],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for Governance {}
|
||||
impl AccountMaxSize for GovernanceV2 {}
|
||||
|
||||
impl IsInitialized for Governance {
|
||||
/// Checks if the given account type is one of the Governance account types
|
||||
pub fn is_governance_v2_account_type(account_type: &GovernanceAccountType) -> bool {
|
||||
*account_type == GovernanceAccountType::GovernanceV2
|
||||
|| *account_type == GovernanceAccountType::ProgramGovernanceV2
|
||||
|| *account_type == GovernanceAccountType::MintGovernanceV2
|
||||
|| *account_type == GovernanceAccountType::TokenGovernanceV2
|
||||
}
|
||||
|
||||
impl IsInitialized for GovernanceV2 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::Governance
|
||||
|| self.account_type == GovernanceAccountType::ProgramGovernance
|
||||
|| self.account_type == GovernanceAccountType::MintGovernance
|
||||
|| self.account_type == GovernanceAccountType::TokenGovernance
|
||||
is_governance_v2_account_type(&self.account_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl Governance {
|
||||
impl GovernanceV2 {
|
||||
/// Returns Governance PDA seeds
|
||||
pub fn get_governance_address_seeds(&self) -> Result<[&[u8]; 3], ProgramError> {
|
||||
let seeds = match self.account_type {
|
||||
GovernanceAccountType::Governance => {
|
||||
GovernanceAccountType::GovernanceV1 | GovernanceAccountType::GovernanceV2 => {
|
||||
get_governance_address_seeds(&self.realm, &self.governed_account)
|
||||
}
|
||||
GovernanceAccountType::ProgramGovernance => {
|
||||
GovernanceAccountType::ProgramGovernanceV1
|
||||
| GovernanceAccountType::ProgramGovernanceV2 => {
|
||||
get_program_governance_address_seeds(&self.realm, &self.governed_account)
|
||||
}
|
||||
GovernanceAccountType::MintGovernance => {
|
||||
GovernanceAccountType::MintGovernanceV1 | GovernanceAccountType::MintGovernanceV2 => {
|
||||
get_mint_governance_address_seeds(&self.realm, &self.governed_account)
|
||||
}
|
||||
GovernanceAccountType::TokenGovernance => {
|
||||
GovernanceAccountType::TokenGovernanceV1 | GovernanceAccountType::TokenGovernanceV2 => {
|
||||
get_token_governance_address_seeds(&self.realm, &self.governed_account)
|
||||
}
|
||||
_ => return Err(GovernanceToolsError::InvalidAccountType.into()),
|
||||
|
@ -112,14 +124,67 @@ impl Governance {
|
|||
|
||||
Ok(seeds)
|
||||
}
|
||||
|
||||
/// Serializes account into the target buffer
|
||||
pub fn serialize<W: Write>(self, writer: &mut W) -> Result<(), ProgramError> {
|
||||
if is_governance_v2_account_type(&self.account_type) {
|
||||
BorshSerialize::serialize(&self, writer)?
|
||||
} else if is_governance_v1_account_type(&self.account_type) {
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 128] {
|
||||
panic!("Extended data not supported by GovernanceV1")
|
||||
}
|
||||
|
||||
let governance_data_v1 = GovernanceV1 {
|
||||
account_type: self.account_type,
|
||||
realm: self.realm,
|
||||
governed_account: self.governed_account,
|
||||
proposals_count: self.proposals_count,
|
||||
config: self.config,
|
||||
reserved: self.reserved,
|
||||
voting_proposal_count: self.voting_proposal_count,
|
||||
};
|
||||
|
||||
BorshSerialize::serialize(&governance_data_v1, writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Deserializes Governance account and checks owner program
|
||||
pub fn get_governance_data(
|
||||
program_id: &Pubkey,
|
||||
governance_info: &AccountInfo,
|
||||
) -> Result<Governance, ProgramError> {
|
||||
get_account_data::<Governance>(program_id, governance_info)
|
||||
) -> Result<GovernanceV2, ProgramError> {
|
||||
if governance_info.data_is_empty() {
|
||||
return Err(GovernanceToolsError::AccountDoesNotExist.into());
|
||||
}
|
||||
|
||||
let account_type: GovernanceAccountType =
|
||||
try_from_slice_unchecked(&governance_info.data.borrow())?;
|
||||
|
||||
// If the account is V1 version then translate to V2
|
||||
if is_governance_v1_account_type(&account_type) {
|
||||
let governance_data_v1 = get_account_data::<GovernanceV1>(program_id, governance_info)?;
|
||||
|
||||
return Ok(GovernanceV2 {
|
||||
account_type,
|
||||
realm: governance_data_v1.realm,
|
||||
governed_account: governance_data_v1.governed_account,
|
||||
proposals_count: governance_data_v1.proposals_count,
|
||||
config: governance_data_v1.config,
|
||||
reserved: governance_data_v1.reserved,
|
||||
voting_proposal_count: governance_data_v1.voting_proposal_count,
|
||||
|
||||
// Add the extra reserved_v2 padding
|
||||
reserved_v2: [0; 128],
|
||||
});
|
||||
}
|
||||
|
||||
get_account_data::<GovernanceV2>(program_id, governance_info)
|
||||
}
|
||||
|
||||
/// Deserializes Governance account, checks owner program and asserts governance belongs to the given ream
|
||||
|
@ -127,7 +192,7 @@ pub fn get_governance_data_for_realm(
|
|||
program_id: &Pubkey,
|
||||
governance_info: &AccountInfo,
|
||||
realm: &Pubkey,
|
||||
) -> Result<Governance, ProgramError> {
|
||||
) -> Result<GovernanceV2, ProgramError> {
|
||||
let governance_data = get_governance_data(program_id, governance_info)?;
|
||||
|
||||
if governance_data.realm != *realm {
|
||||
|
@ -250,13 +315,17 @@ pub fn assert_is_valid_governance(
|
|||
program_id: &Pubkey,
|
||||
governance_info: &AccountInfo,
|
||||
) -> Result<(), ProgramError> {
|
||||
assert_is_valid_account2(
|
||||
assert_is_valid_account_of_types(
|
||||
governance_info,
|
||||
&[
|
||||
GovernanceAccountType::Governance,
|
||||
GovernanceAccountType::ProgramGovernance,
|
||||
GovernanceAccountType::TokenGovernance,
|
||||
GovernanceAccountType::MintGovernance,
|
||||
GovernanceAccountType::GovernanceV1,
|
||||
GovernanceAccountType::GovernanceV2,
|
||||
GovernanceAccountType::ProgramGovernanceV1,
|
||||
GovernanceAccountType::ProgramGovernanceV2,
|
||||
GovernanceAccountType::TokenGovernanceV1,
|
||||
GovernanceAccountType::TokenGovernanceV2,
|
||||
GovernanceAccountType::MintGovernanceV1,
|
||||
GovernanceAccountType::MintGovernanceV2,
|
||||
],
|
||||
program_id,
|
||||
)
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
|
||||
use crate::state::{
|
||||
enums::{
|
||||
GovernanceAccountType, InstructionExecutionFlags, MintMaxVoteWeightSource, ProposalState,
|
||||
GovernanceAccountType, InstructionExecutionFlags, ProposalState,
|
||||
TransactionExecutionStatus, VoteThresholdPercentage,
|
||||
},
|
||||
governance::GovernanceConfig,
|
||||
proposal_transaction::InstructionData,
|
||||
realm::RealmConfig,
|
||||
};
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::{
|
||||
|
@ -14,58 +16,6 @@ use solana_program::{
|
|||
pubkey::Pubkey,
|
||||
};
|
||||
|
||||
/// Realm Config instruction args
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct RealmConfigArgsV1 {
|
||||
/// Indicates whether council_mint should be used
|
||||
/// If yes then council_mint account must also be passed to the instruction
|
||||
pub use_council_mint: bool,
|
||||
|
||||
/// Min number of community tokens required to create a governance
|
||||
pub min_community_weight_to_create_governance: u64,
|
||||
|
||||
/// The source used for community mint max vote weight source
|
||||
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
|
||||
}
|
||||
|
||||
/// Instructions supported by the Governance program
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub enum GovernanceInstructionV1 {
|
||||
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
|
||||
CreateRealm {
|
||||
#[allow(dead_code)]
|
||||
/// UTF-8 encoded Governance Realm name
|
||||
name: String,
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Realm config args
|
||||
config_args: RealmConfigArgsV1,
|
||||
},
|
||||
|
||||
/// Deposits governing tokens (Community or Council) to Governance Realm and establishes your voter weight to be used for voting within the Realm
|
||||
DepositGoverningTokens {
|
||||
/// The amount to deposit into the realm
|
||||
#[allow(dead_code)]
|
||||
amount: u64,
|
||||
},
|
||||
}
|
||||
|
||||
/// Realm Config defining Realm parameters.
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct RealmConfigV1 {
|
||||
/// Reserved space for future versions
|
||||
pub reserved: [u8; 8],
|
||||
|
||||
/// Min number of community tokens required to create a governance
|
||||
pub min_community_weight_to_create_governance: u64,
|
||||
|
||||
/// The source used for community mint max vote weight source
|
||||
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
|
||||
|
||||
/// Optional council mint
|
||||
pub council_mint: Option<Pubkey>,
|
||||
}
|
||||
|
||||
/// Governance Realm Account
|
||||
/// Account PDA seeds" ['governance', name]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
|
@ -77,10 +27,15 @@ pub struct RealmV1 {
|
|||
pub community_mint: Pubkey,
|
||||
|
||||
/// Configuration of the Realm
|
||||
pub config: RealmConfigV1,
|
||||
pub config: RealmConfig,
|
||||
|
||||
/// Reserved space for future versions
|
||||
pub reserved: [u8; 8],
|
||||
pub reserved: [u8; 6],
|
||||
|
||||
/// The number of proposals in voting state in the Realm
|
||||
/// Note: This is field introduced in V2 but it took space from reserved
|
||||
/// and we have preserve it for V1 serialization roundtrip
|
||||
pub voting_proposal_count: u16,
|
||||
|
||||
/// Realm authority. The authority must sign transactions which update the realm config
|
||||
/// The authority should be transferred to Realm Governance to make the Realm self governed through proposals
|
||||
|
@ -92,7 +47,107 @@ pub struct RealmV1 {
|
|||
|
||||
impl IsInitialized for RealmV1 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::Realm
|
||||
self.account_type == GovernanceAccountType::RealmV1
|
||||
}
|
||||
}
|
||||
|
||||
/// Governance Token Owner Record
|
||||
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct TokenOwnerRecordV1 {
|
||||
/// Governance account type
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
/// The Realm the TokenOwnerRecord belongs to
|
||||
pub realm: Pubkey,
|
||||
|
||||
/// Governing Token Mint the TokenOwnerRecord holds deposit for
|
||||
pub governing_token_mint: Pubkey,
|
||||
|
||||
/// The owner (either single or multisig) of the deposited governing SPL Tokens
|
||||
/// This is who can authorize a withdrawal of the tokens
|
||||
pub governing_token_owner: Pubkey,
|
||||
|
||||
/// The amount of governing tokens deposited into the Realm
|
||||
/// This amount is the voter weight used when voting on proposals
|
||||
pub governing_token_deposit_amount: u64,
|
||||
|
||||
/// The number of votes cast by TokenOwner but not relinquished yet
|
||||
/// Every time a vote is cast this number is increased and it's always decreased when relinquishing a vote regardless of the vote state
|
||||
pub unrelinquished_votes_count: u32,
|
||||
|
||||
/// The total number of votes cast by the TokenOwner
|
||||
/// If TokenOwner withdraws vote while voting is still in progress total_votes_count is decreased and the vote doesn't count towards the total
|
||||
pub total_votes_count: u32,
|
||||
|
||||
/// The number of outstanding proposals the TokenOwner currently owns
|
||||
/// The count is increased when TokenOwner creates a proposal
|
||||
/// and decreased once it's either voted on (Succeeded or Defeated) or Cancelled
|
||||
/// By default it's restricted to 1 outstanding Proposal per token owner
|
||||
pub outstanding_proposal_count: u8,
|
||||
|
||||
/// Reserved space for future versions
|
||||
pub reserved: [u8; 7],
|
||||
|
||||
/// A single account that is allowed to operate governance with the deposited governing tokens
|
||||
/// It can be delegated to by the governing_token_owner or current governance_delegate
|
||||
pub governance_delegate: Option<Pubkey>,
|
||||
}
|
||||
|
||||
impl IsInitialized for TokenOwnerRecordV1 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::TokenOwnerRecordV1
|
||||
}
|
||||
}
|
||||
|
||||
/// Governance Account
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct GovernanceV1 {
|
||||
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
/// Governance Realm
|
||||
pub realm: Pubkey,
|
||||
|
||||
/// Account governed by this Governance and/or PDA identity seed
|
||||
/// It can be Program account, Mint account, Token account or any other account
|
||||
///
|
||||
/// Note: The account doesn't have to exist. In that case the field is only a PDA seed
|
||||
///
|
||||
/// Note: Setting governed_account doesn't give any authority over the governed account
|
||||
/// The relevant authorities for specific account types must still be transferred to the Governance PDA
|
||||
/// Ex: mint_authority/freeze_authority for a Mint account
|
||||
/// or upgrade_authority for a Program account should be transferred to the Governance PDA
|
||||
pub governed_account: Pubkey,
|
||||
|
||||
/// Running count of proposals
|
||||
pub proposals_count: u32,
|
||||
|
||||
/// Governance config
|
||||
pub config: GovernanceConfig,
|
||||
|
||||
/// Reserved space for future versions
|
||||
pub reserved: [u8; 6],
|
||||
|
||||
/// The number of proposals in voting state in the Governance
|
||||
/// Note: This is field introduced in V2 but it took space from reserved
|
||||
/// and we have preserve it for V1 serialization roundtrip
|
||||
pub voting_proposal_count: u16,
|
||||
}
|
||||
|
||||
/// Checks if the given account type is one of the Governance account types
|
||||
pub fn is_governance_v1_account_type(account_type: &GovernanceAccountType) -> bool {
|
||||
*account_type == GovernanceAccountType::GovernanceV1
|
||||
|| *account_type == GovernanceAccountType::ProgramGovernanceV1
|
||||
|| *account_type == GovernanceAccountType::MintGovernanceV1
|
||||
|| *account_type == GovernanceAccountType::TokenGovernanceV1
|
||||
}
|
||||
|
||||
impl IsInitialized for GovernanceV1 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
is_governance_v1_account_type(&self.account_type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -186,6 +241,29 @@ impl IsInitialized for ProposalV1 {
|
|||
}
|
||||
}
|
||||
|
||||
/// Account PDA seeds: ['governance', proposal, signatory]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct SignatoryRecordV1 {
|
||||
/// Governance account type
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
/// Proposal the signatory is assigned for
|
||||
pub proposal: Pubkey,
|
||||
|
||||
/// The account of the signatory who can sign off the proposal
|
||||
pub signatory: Pubkey,
|
||||
|
||||
/// Indicates whether the signatory signed off the proposal
|
||||
pub signed_off: bool,
|
||||
}
|
||||
|
||||
impl IsInitialized for SignatoryRecordV1 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::SignatoryRecordV1
|
||||
}
|
||||
}
|
||||
|
||||
/// Proposal instruction V1
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct ProposalInstructionV1 {
|
||||
|
|
|
@ -65,7 +65,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_max_size() {
|
||||
let program_metadata_data = ProgramMetadata {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
updated_at: 10,
|
||||
reserved: [0; 64],
|
||||
version: "111.122.155".to_string(),
|
||||
|
|
|
@ -29,7 +29,7 @@ use crate::{
|
|||
},
|
||||
governance::GovernanceConfig,
|
||||
proposal_transaction::ProposalTransactionV2,
|
||||
realm::Realm,
|
||||
realm::RealmV2,
|
||||
realm_config::get_realm_config_data_for_realm,
|
||||
vote_record::Vote,
|
||||
},
|
||||
|
@ -413,7 +413,7 @@ impl ProposalV2 {
|
|||
/// Calculates max voter weight for given mint supply and realm config
|
||||
fn get_max_voter_weight_from_mint_supply(
|
||||
&mut self,
|
||||
realm_data: &Realm,
|
||||
realm_data: &RealmV2,
|
||||
governing_token_mint_supply: u64,
|
||||
) -> Result<u64, ProgramError> {
|
||||
// max vote weight fraction is only used for community mint
|
||||
|
@ -464,7 +464,7 @@ impl ProposalV2 {
|
|||
governing_token_mint_info: &AccountInfo,
|
||||
account_info_iter: &mut Iter<AccountInfo>,
|
||||
realm: &Pubkey,
|
||||
realm_data: &Realm,
|
||||
realm_data: &RealmV2,
|
||||
) -> Result<u64, ProgramError> {
|
||||
// if the realm uses addin for max community voter weight then use the externally provided max weight
|
||||
if realm_data.config.use_max_community_voter_weight_addin
|
||||
|
@ -774,7 +774,7 @@ impl ProposalV2 {
|
|||
}
|
||||
|
||||
if self.options.len() != 1 {
|
||||
panic!("ProposalV1 doesn't multiple options")
|
||||
panic!("ProposalV1 doesn't support multiple options")
|
||||
}
|
||||
|
||||
let proposal_data_v1 = ProposalV1 {
|
||||
|
@ -1011,7 +1011,7 @@ mod test {
|
|||
|
||||
fn create_test_proposal() -> ProposalV2 {
|
||||
ProposalV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
governance: Pubkey::new_unique(),
|
||||
governing_token_mint: Pubkey::new_unique(),
|
||||
max_vote_weight: Some(10),
|
||||
|
@ -1087,9 +1087,9 @@ mod test {
|
|||
proposal
|
||||
}
|
||||
|
||||
fn create_test_realm() -> Realm {
|
||||
Realm {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
fn create_test_realm() -> RealmV2 {
|
||||
RealmV2 {
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
community_mint: Pubkey::new_unique(),
|
||||
reserved: [0; 6],
|
||||
|
||||
|
@ -1106,6 +1106,7 @@ mod test {
|
|||
min_community_weight_to_create_governance: 10,
|
||||
},
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -114,6 +114,10 @@ pub struct ProposalTransactionV2 {
|
|||
|
||||
/// Instruction execution status
|
||||
pub execution_status: TransactionExecutionStatus,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 8],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for ProposalTransactionV2 {
|
||||
|
@ -125,7 +129,7 @@ impl AccountMaxSize for ProposalTransactionV2 {
|
|||
.sum::<usize>()
|
||||
+ 4;
|
||||
|
||||
Some(instructions_size + 90)
|
||||
Some(instructions_size + 98)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,6 +150,12 @@ impl ProposalTransactionV2 {
|
|||
};
|
||||
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 8] {
|
||||
panic!("Extended data not supported by ProposalInstructionV1")
|
||||
}
|
||||
|
||||
let proposal_transaction_data_v1 = ProposalInstructionV1 {
|
||||
account_type: self.account_type,
|
||||
proposal: self.proposal,
|
||||
|
@ -217,6 +227,7 @@ pub fn get_proposal_transaction_data(
|
|||
instructions: vec![proposal_transaction_data_v1.instruction],
|
||||
executed_at: proposal_transaction_data_v1.executed_at,
|
||||
execution_status: proposal_transaction_data_v1.execution_status,
|
||||
reserved_v2: [0; 8],
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -277,6 +288,7 @@ mod test {
|
|||
instructions: create_test_instruction_data(),
|
||||
executed_at: Some(100),
|
||||
execution_status: TransactionExecutionStatus::Success,
|
||||
reserved_v2: [0; 8],
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,21 +1,26 @@
|
|||
//! Realm Account
|
||||
|
||||
use borsh::maybestd::io::Write;
|
||||
use std::slice::Iter;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
borsh::try_from_slice_unchecked,
|
||||
program_error::ProgramError,
|
||||
program_pack::IsInitialized,
|
||||
pubkey::Pubkey,
|
||||
};
|
||||
use spl_governance_addin_api::voter_weight::VoterWeightAction;
|
||||
use spl_governance_tools::account::{assert_is_valid_account, get_account_data, AccountMaxSize};
|
||||
use spl_governance_tools::account::{
|
||||
assert_is_valid_account_of_types, get_account_data, AccountMaxSize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
error::GovernanceError,
|
||||
state::{
|
||||
enums::{GovernanceAccountType, MintMaxVoteWeightSource},
|
||||
legacy::RealmV1,
|
||||
token_owner_record::get_token_owner_record_data_for_realm,
|
||||
},
|
||||
PROGRAM_AUTHORITY_SEED,
|
||||
|
@ -87,7 +92,7 @@ pub struct RealmConfig {
|
|||
/// Account PDA seeds" ['governance', name]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct Realm {
|
||||
pub struct RealmV2 {
|
||||
/// Governance account type
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
|
@ -109,21 +114,25 @@ pub struct Realm {
|
|||
|
||||
/// Governance Realm name
|
||||
pub name: String,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 128],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for Realm {
|
||||
impl AccountMaxSize for RealmV2 {
|
||||
fn get_max_size(&self) -> Option<usize> {
|
||||
Some(self.name.len() + 136)
|
||||
Some(self.name.len() + 264)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsInitialized for Realm {
|
||||
impl IsInitialized for RealmV2 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::Realm
|
||||
self.account_type == GovernanceAccountType::RealmV2
|
||||
}
|
||||
}
|
||||
|
||||
impl Realm {
|
||||
impl RealmV2 {
|
||||
/// Asserts the given mint is either Community or Council mint of the Realm
|
||||
pub fn assert_is_valid_governing_token_mint(
|
||||
&self,
|
||||
|
@ -215,6 +224,34 @@ impl Realm {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes account into the target buffer
|
||||
pub fn serialize<W: Write>(self, writer: &mut W) -> Result<(), ProgramError> {
|
||||
if self.account_type == GovernanceAccountType::RealmV2 {
|
||||
BorshSerialize::serialize(&self, writer)?
|
||||
} else if self.account_type == GovernanceAccountType::RealmV1 {
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 128] {
|
||||
panic!("Extended data not supported by RealmV1")
|
||||
}
|
||||
|
||||
let realm_data_v1 = RealmV1 {
|
||||
account_type: self.account_type,
|
||||
community_mint: self.community_mint,
|
||||
config: self.config,
|
||||
reserved: self.reserved,
|
||||
voting_proposal_count: self.voting_proposal_count,
|
||||
authority: self.authority,
|
||||
name: self.name,
|
||||
};
|
||||
|
||||
BorshSerialize::serialize(&realm_data_v1, writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks whether realm account exists, is initialized and owned by Governance program
|
||||
|
@ -222,15 +259,41 @@ pub fn assert_is_valid_realm(
|
|||
program_id: &Pubkey,
|
||||
realm_info: &AccountInfo,
|
||||
) -> Result<(), ProgramError> {
|
||||
assert_is_valid_account(realm_info, GovernanceAccountType::Realm, program_id)
|
||||
assert_is_valid_account_of_types(
|
||||
realm_info,
|
||||
&[
|
||||
GovernanceAccountType::RealmV1,
|
||||
GovernanceAccountType::RealmV2,
|
||||
],
|
||||
program_id,
|
||||
)
|
||||
}
|
||||
|
||||
/// Deserializes account and checks owner program
|
||||
pub fn get_realm_data(
|
||||
program_id: &Pubkey,
|
||||
realm_info: &AccountInfo,
|
||||
) -> Result<Realm, ProgramError> {
|
||||
get_account_data::<Realm>(program_id, realm_info)
|
||||
) -> Result<RealmV2, ProgramError> {
|
||||
let account_type: GovernanceAccountType = try_from_slice_unchecked(&realm_info.data.borrow())?;
|
||||
|
||||
// If the account is V1 version then translate to V2
|
||||
if account_type == GovernanceAccountType::ProposalV1 {
|
||||
let realm_data_v1 = get_account_data::<RealmV1>(program_id, realm_info)?;
|
||||
|
||||
return Ok(RealmV2 {
|
||||
account_type,
|
||||
community_mint: realm_data_v1.community_mint,
|
||||
config: realm_data_v1.config,
|
||||
reserved: realm_data_v1.reserved,
|
||||
voting_proposal_count: realm_data_v1.voting_proposal_count,
|
||||
authority: realm_data_v1.authority,
|
||||
name: realm_data_v1.name,
|
||||
// Add the extra reserved_v2 padding
|
||||
reserved_v2: [0; 128],
|
||||
});
|
||||
}
|
||||
|
||||
get_account_data::<RealmV2>(program_id, realm_info)
|
||||
}
|
||||
|
||||
/// Deserializes account and checks the given authority is Realm's authority
|
||||
|
@ -238,8 +301,8 @@ pub fn get_realm_data_for_authority(
|
|||
program_id: &Pubkey,
|
||||
realm_info: &AccountInfo,
|
||||
realm_authority: &Pubkey,
|
||||
) -> Result<Realm, ProgramError> {
|
||||
let realm_data = get_account_data::<Realm>(program_id, realm_info)?;
|
||||
) -> Result<RealmV2, ProgramError> {
|
||||
let realm_data = get_account_data::<RealmV2>(program_id, realm_info)?;
|
||||
|
||||
if realm_data.authority.is_none() {
|
||||
return Err(GovernanceError::RealmHasNoAuthority.into());
|
||||
|
@ -257,7 +320,7 @@ pub fn get_realm_data_for_governing_token_mint(
|
|||
program_id: &Pubkey,
|
||||
realm_info: &AccountInfo,
|
||||
governing_token_mint: &Pubkey,
|
||||
) -> Result<Realm, ProgramError> {
|
||||
) -> Result<RealmV2, ProgramError> {
|
||||
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||
|
||||
realm_data.assert_is_valid_governing_token_mint(governing_token_mint)?;
|
||||
|
@ -319,18 +382,15 @@ pub fn assert_valid_realm_config_args(config_args: &RealmConfigArgs) -> Result<(
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use crate::{
|
||||
instruction::GovernanceInstruction,
|
||||
state::legacy::{GovernanceInstructionV1, RealmConfigV1, RealmV1},
|
||||
};
|
||||
use crate::instruction::GovernanceInstruction;
|
||||
use solana_program::borsh::try_from_slice_unchecked;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_max_size() {
|
||||
let realm = Realm {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
let realm = RealmV2 {
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
community_mint: Pubkey::new_unique(),
|
||||
reserved: [0; 6],
|
||||
|
||||
|
@ -346,6 +406,7 @@ mod test {
|
|||
},
|
||||
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let size = realm.try_to_vec().unwrap().len();
|
||||
|
@ -353,36 +414,40 @@ mod test {
|
|||
assert_eq!(realm.get_max_size(), Some(size));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_v2_realm_account_from_v1() {
|
||||
// Arrange
|
||||
let realm_v1 = RealmV1 {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
community_mint: Pubkey::new_unique(),
|
||||
config: RealmConfigV1 {
|
||||
council_mint: Some(Pubkey::new_unique()),
|
||||
reserved: [0; 8],
|
||||
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::Absolute(100),
|
||||
min_community_weight_to_create_governance: 10,
|
||||
},
|
||||
reserved: [0; 8],
|
||||
authority: Some(Pubkey::new_unique()),
|
||||
name: "test-realm-v1".to_string(),
|
||||
};
|
||||
/// Realm Config instruction args
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct RealmConfigArgsV1 {
|
||||
/// Indicates whether council_mint should be used
|
||||
/// If yes then council_mint account must also be passed to the instruction
|
||||
pub use_council_mint: bool,
|
||||
|
||||
let mut realm_v1_data = vec![];
|
||||
realm_v1.serialize(&mut realm_v1_data).unwrap();
|
||||
/// Min number of community tokens required to create a governance
|
||||
pub min_community_weight_to_create_governance: u64,
|
||||
|
||||
// Act
|
||||
let realm_v2: Realm = try_from_slice_unchecked(&realm_v1_data).unwrap();
|
||||
/// The source used for community mint max vote weight source
|
||||
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
|
||||
}
|
||||
|
||||
// Assert
|
||||
assert!(!realm_v2.config.use_community_voter_weight_addin);
|
||||
assert_eq!(realm_v2.account_type, GovernanceAccountType::Realm);
|
||||
assert_eq!(
|
||||
realm_v2.config.min_community_weight_to_create_governance,
|
||||
realm_v1.config.min_community_weight_to_create_governance,
|
||||
);
|
||||
/// Instructions supported by the Governance program
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub enum GovernanceInstructionV1 {
|
||||
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
|
||||
CreateRealm {
|
||||
#[allow(dead_code)]
|
||||
/// UTF-8 encoded Governance Realm name
|
||||
name: String,
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Realm config args
|
||||
config_args: RealmConfigArgsV1,
|
||||
},
|
||||
|
||||
/// Deposits governing tokens (Community or Council) to Governance Realm and establishes your voter weight to be used for voting within the Realm
|
||||
DepositGoverningTokens {
|
||||
/// The amount to deposit into the realm
|
||||
#[allow(dead_code)]
|
||||
amount: u64,
|
||||
},
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -92,7 +92,7 @@ mod test {
|
|||
#[test]
|
||||
fn test_max_size() {
|
||||
let realm_config = RealmConfigAccount {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
realm: Pubkey::new_unique(),
|
||||
community_voter_weight_addin: Some(Pubkey::new_unique()),
|
||||
max_community_voter_weight_addin: Some(Pubkey::new_unique()),
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
//! Signatory Record
|
||||
|
||||
use borsh::maybestd::io::Write;
|
||||
|
||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::borsh::try_from_slice_unchecked;
|
||||
use solana_program::{
|
||||
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
||||
pubkey::Pubkey,
|
||||
|
@ -11,10 +14,12 @@ use crate::{error::GovernanceError, PROGRAM_AUTHORITY_SEED};
|
|||
|
||||
use crate::state::enums::GovernanceAccountType;
|
||||
|
||||
use super::legacy::SignatoryRecordV1;
|
||||
|
||||
/// Account PDA seeds: ['governance', proposal, signatory]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct SignatoryRecord {
|
||||
pub struct SignatoryRecordV2 {
|
||||
/// Governance account type
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
|
@ -26,17 +31,21 @@ pub struct SignatoryRecord {
|
|||
|
||||
/// Indicates whether the signatory signed off the proposal
|
||||
pub signed_off: bool,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 8],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for SignatoryRecord {}
|
||||
impl AccountMaxSize for SignatoryRecordV2 {}
|
||||
|
||||
impl IsInitialized for SignatoryRecord {
|
||||
impl IsInitialized for SignatoryRecordV2 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::SignatoryRecord
|
||||
self.account_type == GovernanceAccountType::SignatoryRecordV2
|
||||
}
|
||||
}
|
||||
|
||||
impl SignatoryRecord {
|
||||
impl SignatoryRecordV2 {
|
||||
/// Checks signatory hasn't signed off yet and is transaction signer
|
||||
pub fn assert_can_sign_off(&self, signatory_info: &AccountInfo) -> Result<(), ProgramError> {
|
||||
if self.signed_off {
|
||||
|
@ -58,6 +67,31 @@ impl SignatoryRecord {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Serializes account into the target buffer
|
||||
pub fn serialize<W: Write>(self, writer: &mut W) -> Result<(), ProgramError> {
|
||||
if self.account_type == GovernanceAccountType::SignatoryRecordV2 {
|
||||
BorshSerialize::serialize(&self, writer)?
|
||||
} else if self.account_type == GovernanceAccountType::SignatoryRecordV1 {
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 8] {
|
||||
panic!("Extended data not supported by SignatoryRecordV1")
|
||||
}
|
||||
|
||||
let signatory_record_data_v1 = SignatoryRecordV1 {
|
||||
account_type: self.account_type,
|
||||
proposal: self.proposal,
|
||||
signatory: self.signatory,
|
||||
signed_off: self.signed_off,
|
||||
};
|
||||
|
||||
BorshSerialize::serialize(&signatory_record_data_v1, writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns SignatoryRecord PDA seeds
|
||||
|
@ -89,8 +123,28 @@ pub fn get_signatory_record_address<'a>(
|
|||
pub fn get_signatory_record_data(
|
||||
program_id: &Pubkey,
|
||||
signatory_record_info: &AccountInfo,
|
||||
) -> Result<SignatoryRecord, ProgramError> {
|
||||
get_account_data::<SignatoryRecord>(program_id, signatory_record_info)
|
||||
) -> Result<SignatoryRecordV2, ProgramError> {
|
||||
let account_type: GovernanceAccountType =
|
||||
try_from_slice_unchecked(&signatory_record_info.data.borrow())?;
|
||||
|
||||
// If the account is V1 version then translate to V2
|
||||
if account_type == GovernanceAccountType::SignatoryRecordV1 {
|
||||
let signatory_record_data_v1 =
|
||||
get_account_data::<SignatoryRecordV1>(program_id, signatory_record_info)?;
|
||||
|
||||
return Ok(SignatoryRecordV2 {
|
||||
account_type,
|
||||
|
||||
proposal: signatory_record_data_v1.proposal,
|
||||
signatory: signatory_record_data_v1.signatory,
|
||||
signed_off: signatory_record_data_v1.signed_off,
|
||||
|
||||
// Add the extra reserved_v2 padding
|
||||
reserved_v2: [0; 8],
|
||||
});
|
||||
}
|
||||
|
||||
get_account_data::<SignatoryRecordV2>(program_id, signatory_record_info)
|
||||
}
|
||||
|
||||
/// Deserializes SignatoryRecord and validates its PDA
|
||||
|
@ -99,7 +153,7 @@ pub fn get_signatory_record_data_for_seeds(
|
|||
signatory_record_info: &AccountInfo,
|
||||
proposal: &Pubkey,
|
||||
signatory: &Pubkey,
|
||||
) -> Result<SignatoryRecord, ProgramError> {
|
||||
) -> Result<SignatoryRecordV2, ProgramError> {
|
||||
let (signatory_record_address, _) = Pubkey::find_program_address(
|
||||
&get_signatory_record_address_seeds(proposal, signatory),
|
||||
program_id,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Token Owner Record Account
|
||||
|
||||
use borsh::maybestd::io::Write;
|
||||
use std::slice::Iter;
|
||||
|
||||
use crate::{
|
||||
|
@ -8,8 +9,8 @@ use crate::{
|
|||
},
|
||||
error::GovernanceError,
|
||||
state::{
|
||||
enums::GovernanceAccountType, governance::GovernanceConfig, realm::Realm,
|
||||
realm_config::get_realm_config_data_for_realm,
|
||||
enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1,
|
||||
realm::RealmV2, realm_config::get_realm_config_data_for_realm,
|
||||
},
|
||||
PROGRAM_AUTHORITY_SEED,
|
||||
};
|
||||
|
@ -17,6 +18,7 @@ use crate::{
|
|||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||
use solana_program::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
borsh::try_from_slice_unchecked,
|
||||
program_error::ProgramError,
|
||||
program_pack::IsInitialized,
|
||||
pubkey::Pubkey,
|
||||
|
@ -28,7 +30,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize};
|
|||
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ]
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct TokenOwnerRecord {
|
||||
pub struct TokenOwnerRecordV2 {
|
||||
/// Governance account type
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
|
@ -66,21 +68,25 @@ pub struct TokenOwnerRecord {
|
|||
/// A single account that is allowed to operate governance with the deposited governing tokens
|
||||
/// It can be delegated to by the governing_token_owner or current governance_delegate
|
||||
pub governance_delegate: Option<Pubkey>,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 128],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for TokenOwnerRecord {
|
||||
impl AccountMaxSize for TokenOwnerRecordV2 {
|
||||
fn get_max_size(&self) -> Option<usize> {
|
||||
Some(154)
|
||||
Some(282)
|
||||
}
|
||||
}
|
||||
|
||||
impl IsInitialized for TokenOwnerRecord {
|
||||
impl IsInitialized for TokenOwnerRecordV2 {
|
||||
fn is_initialized(&self) -> bool {
|
||||
self.account_type == GovernanceAccountType::TokenOwnerRecord
|
||||
self.account_type == GovernanceAccountType::TokenOwnerRecordV2
|
||||
}
|
||||
}
|
||||
|
||||
impl TokenOwnerRecord {
|
||||
impl TokenOwnerRecordV2 {
|
||||
/// Checks whether the provided Governance Authority signed transaction
|
||||
pub fn assert_token_owner_or_delegate_is_signer(
|
||||
&self,
|
||||
|
@ -104,7 +110,7 @@ impl TokenOwnerRecord {
|
|||
/// Asserts TokenOwner has enough tokens to be allowed to create proposal and doesn't have any outstanding proposals
|
||||
pub fn assert_can_create_proposal(
|
||||
&self,
|
||||
realm_data: &Realm,
|
||||
realm_data: &RealmV2,
|
||||
config: &GovernanceConfig,
|
||||
voter_weight: u64,
|
||||
) -> Result<(), ProgramError> {
|
||||
|
@ -133,7 +139,7 @@ impl TokenOwnerRecord {
|
|||
/// Asserts TokenOwner has enough tokens to be allowed to create governance
|
||||
pub fn assert_can_create_governance(
|
||||
&self,
|
||||
realm_data: &Realm,
|
||||
realm_data: &RealmV2,
|
||||
voter_weight: u64,
|
||||
) -> Result<(), ProgramError> {
|
||||
let min_weight_to_create_governance =
|
||||
|
@ -188,7 +194,7 @@ impl TokenOwnerRecord {
|
|||
realm_config_info: &AccountInfo,
|
||||
account_info_iter: &mut Iter<AccountInfo>,
|
||||
realm: &Pubkey,
|
||||
realm_data: &Realm,
|
||||
realm_data: &RealmV2,
|
||||
weight_action: VoterWeightAction,
|
||||
weight_action_target: &Pubkey,
|
||||
) -> Result<u64, ProgramError> {
|
||||
|
@ -218,6 +224,37 @@ impl TokenOwnerRecord {
|
|||
Ok(self.governing_token_deposit_amount)
|
||||
}
|
||||
}
|
||||
|
||||
/// Serializes account into the target buffer
|
||||
pub fn serialize<W: Write>(self, writer: &mut W) -> Result<(), ProgramError> {
|
||||
if self.account_type == GovernanceAccountType::TokenOwnerRecordV2 {
|
||||
BorshSerialize::serialize(&self, writer)?
|
||||
} else if self.account_type == GovernanceAccountType::TokenOwnerRecordV1 {
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 128] {
|
||||
panic!("Extended data not supported by TokenOwnerRecordV1")
|
||||
}
|
||||
|
||||
let token_owner_record_data_v1 = TokenOwnerRecordV1 {
|
||||
account_type: self.account_type,
|
||||
realm: self.realm,
|
||||
governing_token_mint: self.governing_token_mint,
|
||||
governing_token_owner: self.governing_token_owner,
|
||||
governing_token_deposit_amount: self.governing_token_deposit_amount,
|
||||
unrelinquished_votes_count: self.unrelinquished_votes_count,
|
||||
total_votes_count: self.total_votes_count,
|
||||
outstanding_proposal_count: self.outstanding_proposal_count,
|
||||
reserved: self.reserved,
|
||||
governance_delegate: self.governance_delegate,
|
||||
};
|
||||
|
||||
BorshSerialize::serialize(&token_owner_record_data_v1, writer)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns TokenOwnerRecord PDA address
|
||||
|
@ -252,8 +289,35 @@ pub fn get_token_owner_record_address_seeds<'a>(
|
|||
pub fn get_token_owner_record_data(
|
||||
program_id: &Pubkey,
|
||||
token_owner_record_info: &AccountInfo,
|
||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
||||
get_account_data::<TokenOwnerRecord>(program_id, token_owner_record_info)
|
||||
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||
let account_type: GovernanceAccountType =
|
||||
try_from_slice_unchecked(&token_owner_record_info.data.borrow())?;
|
||||
|
||||
// If the account is V1 version then translate to V2
|
||||
if account_type == GovernanceAccountType::TokenOwnerRecordV1 {
|
||||
let token_owner_record_data_v1 =
|
||||
get_account_data::<TokenOwnerRecordV1>(program_id, token_owner_record_info)?;
|
||||
|
||||
return Ok(TokenOwnerRecordV2 {
|
||||
account_type,
|
||||
|
||||
realm: token_owner_record_data_v1.realm,
|
||||
governing_token_mint: token_owner_record_data_v1.governing_token_mint,
|
||||
governing_token_owner: token_owner_record_data_v1.governing_token_owner,
|
||||
governing_token_deposit_amount: token_owner_record_data_v1
|
||||
.governing_token_deposit_amount,
|
||||
unrelinquished_votes_count: token_owner_record_data_v1.unrelinquished_votes_count,
|
||||
total_votes_count: token_owner_record_data_v1.total_votes_count,
|
||||
outstanding_proposal_count: token_owner_record_data_v1.outstanding_proposal_count,
|
||||
reserved: token_owner_record_data_v1.reserved,
|
||||
governance_delegate: token_owner_record_data_v1.governance_delegate,
|
||||
|
||||
// Add the extra reserved_v2 padding
|
||||
reserved_v2: [0; 128],
|
||||
});
|
||||
}
|
||||
|
||||
get_account_data::<TokenOwnerRecordV2>(program_id, token_owner_record_info)
|
||||
}
|
||||
|
||||
/// Deserializes TokenOwnerRecord account and checks its PDA against the provided seeds
|
||||
|
@ -261,7 +325,7 @@ pub fn get_token_owner_record_data_for_seeds(
|
|||
program_id: &Pubkey,
|
||||
token_owner_record_info: &AccountInfo,
|
||||
token_owner_record_seeds: &[&[u8]],
|
||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
||||
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||
let (token_owner_record_address, _) =
|
||||
Pubkey::find_program_address(token_owner_record_seeds, program_id);
|
||||
|
||||
|
@ -277,7 +341,7 @@ pub fn get_token_owner_record_data_for_realm(
|
|||
program_id: &Pubkey,
|
||||
token_owner_record_info: &AccountInfo,
|
||||
realm: &Pubkey,
|
||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
||||
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||
let token_owner_record_data = get_token_owner_record_data(program_id, token_owner_record_info)?;
|
||||
|
||||
if token_owner_record_data.realm != *realm {
|
||||
|
@ -293,7 +357,7 @@ pub fn get_token_owner_record_data_for_realm_and_governing_mint(
|
|||
token_owner_record_info: &AccountInfo,
|
||||
realm: &Pubkey,
|
||||
governing_token_mint: &Pubkey,
|
||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
||||
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||
let token_owner_record_data =
|
||||
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm)?;
|
||||
|
||||
|
@ -309,7 +373,7 @@ pub fn get_token_owner_record_data_for_proposal_owner(
|
|||
program_id: &Pubkey,
|
||||
token_owner_record_info: &AccountInfo,
|
||||
proposal_owner: &Pubkey,
|
||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
||||
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||
if token_owner_record_info.key != proposal_owner {
|
||||
return Err(GovernanceError::InvalidProposalOwnerAccount.into());
|
||||
}
|
||||
|
@ -319,14 +383,14 @@ pub fn get_token_owner_record_data_for_proposal_owner(
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use solana_program::borsh::{get_packed_len, try_from_slice_unchecked};
|
||||
use solana_program::borsh::get_packed_len;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_max_size() {
|
||||
let token_owner_record = TokenOwnerRecord {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
let token_owner_record = TokenOwnerRecordV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
realm: Pubkey::new_unique(),
|
||||
governing_token_mint: Pubkey::new_unique(),
|
||||
governing_token_owner: Pubkey::new_unique(),
|
||||
|
@ -336,66 +400,11 @@ mod test {
|
|||
total_votes_count: 1,
|
||||
outstanding_proposal_count: 1,
|
||||
reserved: [0; 7],
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let size = get_packed_len::<TokenOwnerRecord>();
|
||||
let size = get_packed_len::<TokenOwnerRecordV2>();
|
||||
|
||||
assert_eq!(token_owner_record.get_max_size(), Some(size));
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||
pub struct TokenOwnerRecordV1 {
|
||||
pub account_type: GovernanceAccountType,
|
||||
|
||||
pub realm: Pubkey,
|
||||
|
||||
pub governing_token_mint: Pubkey,
|
||||
|
||||
pub governing_token_owner: Pubkey,
|
||||
|
||||
pub governing_token_deposit_amount: u64,
|
||||
|
||||
pub unrelinquished_votes_count: u32,
|
||||
|
||||
pub total_votes_count: u32,
|
||||
|
||||
pub reserved: [u8; 8],
|
||||
|
||||
pub governance_delegate: Option<Pubkey>,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deserialize_v1_0_account() {
|
||||
let token_owner_record_v1_0 = TokenOwnerRecordV1 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
realm: Pubkey::new_unique(),
|
||||
governing_token_mint: Pubkey::new_unique(),
|
||||
governing_token_owner: Pubkey::new_unique(),
|
||||
governing_token_deposit_amount: 10,
|
||||
unrelinquished_votes_count: 2,
|
||||
total_votes_count: 5,
|
||||
reserved: [0; 8],
|
||||
governance_delegate: Some(Pubkey::new_unique()),
|
||||
};
|
||||
|
||||
let mut token_owner_record_v1_0_data = vec![];
|
||||
token_owner_record_v1_0
|
||||
.serialize(&mut token_owner_record_v1_0_data)
|
||||
.unwrap();
|
||||
|
||||
let token_owner_record_v1_1_data: TokenOwnerRecord =
|
||||
try_from_slice_unchecked(&token_owner_record_v1_0_data).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
token_owner_record_v1_0.account_type,
|
||||
token_owner_record_v1_1_data.account_type
|
||||
);
|
||||
|
||||
assert_eq!(0, token_owner_record_v1_1_data.outstanding_proposal_count);
|
||||
|
||||
assert_eq!(
|
||||
token_owner_record_v1_0.governance_delegate,
|
||||
token_owner_record_v1_1_data.governance_delegate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,10 @@ pub struct VoteRecordV2 {
|
|||
|
||||
/// Voter's vote
|
||||
pub vote: Vote,
|
||||
|
||||
/// Reserved space for versions v2 and onwards
|
||||
/// Note: This space won't be available to v1 accounts until runtime supports resizing
|
||||
pub reserved_v2: [u8; 8],
|
||||
}
|
||||
|
||||
impl AccountMaxSize for VoteRecordV2 {}
|
||||
|
@ -107,6 +111,12 @@ impl VoteRecordV2 {
|
|||
BorshSerialize::serialize(&self, writer)?
|
||||
} else if self.account_type == GovernanceAccountType::VoteRecordV1 {
|
||||
// V1 account can't be resized and we have to translate it back to the original format
|
||||
|
||||
// If reserved_v2 is used it must be individually asses for v1 backward compatibility impact
|
||||
if self.reserved_v2 != [0; 8] {
|
||||
panic!("Extended data not supported by VoteRecordV1")
|
||||
}
|
||||
|
||||
let vote_weight = match &self.vote {
|
||||
Vote::Approve(_options) => VoteWeightV1::Yes(self.voter_weight),
|
||||
Vote::Deny => VoteWeightV1::No(self.voter_weight),
|
||||
|
@ -160,6 +170,7 @@ pub fn get_vote_record_data(
|
|||
is_relinquished: vote_record_data_v1.is_relinquished,
|
||||
voter_weight,
|
||||
vote,
|
||||
reserved_v2: [0; 8],
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||
use solana_sdk::signature::Keypair;
|
||||
use spl_governance::state::{
|
||||
governance::Governance, native_treasury::NativeTreasury, program_metadata::ProgramMetadata,
|
||||
proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::Realm,
|
||||
realm_config::RealmConfigAccount, signatory_record::SignatoryRecord,
|
||||
token_owner_record::TokenOwnerRecord, vote_record::VoteRecordV2,
|
||||
governance::GovernanceV2, native_treasury::NativeTreasury, program_metadata::ProgramMetadata,
|
||||
proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::RealmV2,
|
||||
realm_config::RealmConfigAccount, signatory_record::SignatoryRecordV2,
|
||||
token_owner_record::TokenOwnerRecordV2, vote_record::VoteRecordV2,
|
||||
};
|
||||
|
||||
use spl_governance_addin_api::{
|
||||
|
@ -20,7 +20,7 @@ pub trait AccountCookie {
|
|||
pub struct RealmCookie {
|
||||
pub address: Pubkey,
|
||||
|
||||
pub account: Realm,
|
||||
pub account: RealmV2,
|
||||
|
||||
pub community_mint_authority: Keypair,
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub struct RealmConfigCookie {
|
|||
pub struct TokenOwnerRecordCookie {
|
||||
pub address: Pubkey,
|
||||
|
||||
pub account: TokenOwnerRecord,
|
||||
pub account: TokenOwnerRecordV2,
|
||||
|
||||
pub token_source: Pubkey,
|
||||
|
||||
|
@ -131,7 +131,7 @@ impl AccountCookie for GovernedAccountCookie {
|
|||
#[derive(Debug)]
|
||||
pub struct GovernanceCookie {
|
||||
pub address: Pubkey,
|
||||
pub account: Governance,
|
||||
pub account: GovernanceV2,
|
||||
pub next_proposal_index: u32,
|
||||
}
|
||||
|
||||
|
@ -147,7 +147,7 @@ pub struct ProposalCookie {
|
|||
#[derive(Debug)]
|
||||
pub struct SignatoryRecordCookie {
|
||||
pub address: Pubkey,
|
||||
pub account: SignatoryRecord,
|
||||
pub account: SignatoryRecordV2,
|
||||
pub signatory: Keypair,
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ use spl_governance::{
|
|||
},
|
||||
governance::{
|
||||
get_governance_address, get_mint_governance_address, get_program_governance_address,
|
||||
get_token_governance_address, Governance, GovernanceConfig,
|
||||
get_token_governance_address, GovernanceConfig, GovernanceV2,
|
||||
},
|
||||
native_treasury::{get_native_treasury_address, NativeTreasury},
|
||||
program_metadata::{get_program_metadata_address, ProgramMetadata},
|
||||
|
@ -41,12 +41,12 @@ use spl_governance::{
|
|||
get_proposal_transaction_address, InstructionData, ProposalTransactionV2,
|
||||
},
|
||||
realm::{
|
||||
get_governing_token_holding_address, get_realm_address, Realm, RealmConfig,
|
||||
RealmConfigArgs, SetRealmAuthorityAction,
|
||||
get_governing_token_holding_address, get_realm_address, RealmConfig, RealmConfigArgs,
|
||||
RealmV2, SetRealmAuthorityAction,
|
||||
},
|
||||
realm_config::{get_realm_config_address, RealmConfigAccount},
|
||||
signatory_record::{get_signatory_record_address, SignatoryRecord},
|
||||
token_owner_record::{get_token_owner_record_address, TokenOwnerRecord},
|
||||
signatory_record::{get_signatory_record_address, SignatoryRecordV2},
|
||||
token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2},
|
||||
vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2},
|
||||
},
|
||||
tools::bpf_loader_upgradeable::get_program_data_address,
|
||||
|
@ -298,8 +298,8 @@ impl GovernanceProgramTest {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let account = Realm {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
let account = RealmV2 {
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
community_mint: community_token_mint_keypair.pubkey(),
|
||||
|
||||
name,
|
||||
|
@ -320,6 +320,7 @@ impl GovernanceProgramTest {
|
|||
use_max_community_voter_weight_addin: false,
|
||||
},
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some()
|
||||
|
@ -390,8 +391,8 @@ impl GovernanceProgramTest {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let account = Realm {
|
||||
account_type: GovernanceAccountType::Realm,
|
||||
let account = RealmV2 {
|
||||
account_type: GovernanceAccountType::RealmV2,
|
||||
community_mint: realm_cookie.account.community_mint,
|
||||
|
||||
name,
|
||||
|
@ -408,6 +409,7 @@ impl GovernanceProgramTest {
|
|||
use_max_community_voter_weight_addin: false,
|
||||
},
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let community_token_holding_address = get_governing_token_holding_address(
|
||||
|
@ -470,8 +472,8 @@ impl GovernanceProgramTest {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
let account = TokenOwnerRecord {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
let account = TokenOwnerRecordV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
realm: realm_cookie.address,
|
||||
governing_token_mint: realm_cookie.account.community_mint,
|
||||
governing_token_owner: token_owner.pubkey(),
|
||||
|
@ -481,6 +483,7 @@ impl GovernanceProgramTest {
|
|||
total_votes_count: 0,
|
||||
outstanding_proposal_count: 0,
|
||||
reserved: [0; 7],
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let token_owner_record_address = get_token_owner_record_address(
|
||||
|
@ -686,8 +689,8 @@ impl GovernanceProgramTest {
|
|||
&token_owner.pubkey(),
|
||||
);
|
||||
|
||||
let account = TokenOwnerRecord {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
||||
let account = TokenOwnerRecordV2 {
|
||||
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||
realm: *realm_address,
|
||||
governing_token_mint: *governing_mint,
|
||||
governing_token_owner: token_owner.pubkey(),
|
||||
|
@ -697,6 +700,7 @@ impl GovernanceProgramTest {
|
|||
total_votes_count: 0,
|
||||
outstanding_proposal_count: 0,
|
||||
reserved: [0; 7],
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string());
|
||||
|
@ -1236,14 +1240,15 @@ impl GovernanceProgramTest {
|
|||
governance_config.clone(),
|
||||
);
|
||||
|
||||
let account = Governance {
|
||||
account_type: GovernanceAccountType::Governance,
|
||||
let account = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::GovernanceV2,
|
||||
realm: realm_cookie.address,
|
||||
governed_account: governed_account_cookie.address,
|
||||
config: governance_config.clone(),
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let default_signers = &[create_authority];
|
||||
|
@ -1404,14 +1409,15 @@ impl GovernanceProgramTest {
|
|||
.process_transaction(&[create_program_governance_ix], Some(signers))
|
||||
.await?;
|
||||
|
||||
let account = Governance {
|
||||
account_type: GovernanceAccountType::ProgramGovernance,
|
||||
let account = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::ProgramGovernanceV2,
|
||||
realm: realm_cookie.address,
|
||||
governed_account: governed_program_cookie.address,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let program_governance_address = get_program_governance_address(
|
||||
|
@ -1527,14 +1533,15 @@ impl GovernanceProgramTest {
|
|||
.process_transaction(&[create_mint_governance_ix], Some(signers))
|
||||
.await?;
|
||||
|
||||
let account = Governance {
|
||||
account_type: GovernanceAccountType::MintGovernance,
|
||||
let account = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::MintGovernanceV2,
|
||||
realm: realm_cookie.address,
|
||||
governed_account: governed_mint_cookie.address,
|
||||
config: governance_config.clone(),
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let mint_governance_address = get_mint_governance_address(
|
||||
|
@ -1610,14 +1617,15 @@ impl GovernanceProgramTest {
|
|||
.process_transaction(&[create_token_governance_ix], Some(signers))
|
||||
.await?;
|
||||
|
||||
let account = Governance {
|
||||
account_type: GovernanceAccountType::TokenGovernance,
|
||||
let account = GovernanceV2 {
|
||||
account_type: GovernanceAccountType::TokenGovernanceV2,
|
||||
realm: realm_cookie.address,
|
||||
governed_account: governed_token_cookie.address,
|
||||
config,
|
||||
proposals_count: 0,
|
||||
reserved: [0; 6],
|
||||
voting_proposal_count: 0,
|
||||
reserved_v2: [0; 128],
|
||||
};
|
||||
|
||||
let token_governance_address = get_token_governance_address(
|
||||
|
@ -1857,11 +1865,12 @@ impl GovernanceProgramTest {
|
|||
&signatory.pubkey(),
|
||||
);
|
||||
|
||||
let signatory_record_data = SignatoryRecord {
|
||||
account_type: GovernanceAccountType::SignatoryRecord,
|
||||
let signatory_record_data = SignatoryRecordV2 {
|
||||
account_type: GovernanceAccountType::SignatoryRecordV2,
|
||||
proposal: proposal_cookie.address,
|
||||
signatory: signatory.pubkey(),
|
||||
signed_off: false,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
let signatory_record_cookie = SignatoryRecordCookie {
|
||||
|
@ -2154,6 +2163,7 @@ impl GovernanceProgramTest {
|
|||
vote,
|
||||
voter_weight: vote_amount,
|
||||
is_relinquished: false,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
let vote_record_cookie = VoteRecordCookie {
|
||||
|
@ -2441,6 +2451,7 @@ impl GovernanceProgramTest {
|
|||
executed_at: None,
|
||||
execution_status: TransactionExecutionStatus::None,
|
||||
proposal: proposal_cookie.address,
|
||||
reserved_v2: [0; 8],
|
||||
};
|
||||
|
||||
instruction.accounts = instruction
|
||||
|
@ -2531,9 +2542,9 @@ impl GovernanceProgramTest {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_token_owner_record_account(&mut self, address: &Pubkey) -> TokenOwnerRecord {
|
||||
pub async fn get_token_owner_record_account(&mut self, address: &Pubkey) -> TokenOwnerRecordV2 {
|
||||
self.bench
|
||||
.get_borsh_account::<TokenOwnerRecord>(address)
|
||||
.get_borsh_account::<TokenOwnerRecordV2>(address)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -2552,8 +2563,8 @@ impl GovernanceProgramTest {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> Realm {
|
||||
self.bench.get_borsh_account::<Realm>(realm_address).await
|
||||
pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> RealmV2 {
|
||||
self.bench.get_borsh_account::<RealmV2>(realm_address).await
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -2567,9 +2578,9 @@ impl GovernanceProgramTest {
|
|||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub async fn get_governance_account(&mut self, governance_address: &Pubkey) -> Governance {
|
||||
pub async fn get_governance_account(&mut self, governance_address: &Pubkey) -> GovernanceV2 {
|
||||
self.bench
|
||||
.get_borsh_account::<Governance>(governance_address)
|
||||
.get_borsh_account::<GovernanceV2>(governance_address)
|
||||
.await
|
||||
}
|
||||
|
||||
|
@ -2601,9 +2612,9 @@ impl GovernanceProgramTest {
|
|||
pub async fn get_signatory_record_account(
|
||||
&mut self,
|
||||
proposal_address: &Pubkey,
|
||||
) -> SignatoryRecord {
|
||||
) -> SignatoryRecordV2 {
|
||||
self.bench
|
||||
.get_borsh_account::<SignatoryRecord>(proposal_address)
|
||||
.get_borsh_account::<SignatoryRecordV2>(proposal_address)
|
||||
.await
|
||||
}
|
||||
|
||||
|
|
|
@ -224,16 +224,16 @@ pub fn get_account_data<T: BorshDeserialize + IsInitialized>(
|
|||
|
||||
/// Asserts the given account is not empty, owned by the given program and of the expected type
|
||||
/// Note: The function assumes the account type T is stored as the first element in the account data
|
||||
pub fn assert_is_valid_account<T: BorshDeserialize + PartialEq>(
|
||||
pub fn assert_is_valid_account_of_type<T: BorshDeserialize + PartialEq>(
|
||||
account_info: &AccountInfo,
|
||||
expected_account_type: T,
|
||||
owner_program_id: &Pubkey,
|
||||
) -> Result<(), ProgramError> {
|
||||
assert_is_valid_account2(account_info, &[expected_account_type], owner_program_id)
|
||||
assert_is_valid_account_of_types(account_info, &[expected_account_type], owner_program_id)
|
||||
}
|
||||
/// Asserts the given account is not empty, owned by the given program and one of the expected types
|
||||
/// Note: The function assumes the account type T is stored as the first element in the account data
|
||||
pub fn assert_is_valid_account2<T: BorshDeserialize + PartialEq>(
|
||||
pub fn assert_is_valid_account_of_types<T: BorshDeserialize + PartialEq>(
|
||||
account_info: &AccountInfo,
|
||||
expected_account_types: &[T],
|
||||
owner_program_id: &Pubkey,
|
||||
|
|
Loading…
Reference in New Issue