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,
|
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
|
/// Defines all GovernanceChat accounts types
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -67,7 +67,7 @@ pub fn assert_is_valid_chat_message(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
chat_message_info: &AccountInfo,
|
chat_message_info: &AccountInfo,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
assert_is_valid_account(
|
assert_is_valid_account_of_type(
|
||||||
chat_message_info,
|
chat_message_info,
|
||||||
GovernanceChatAccountType::ChatMessage,
|
GovernanceChatAccountType::ChatMessage,
|
||||||
program_id,
|
program_id,
|
||||||
|
|
|
@ -223,7 +223,7 @@ impl GovernanceChatProgramTest {
|
||||||
let create_governance_ix = create_governance(
|
let create_governance_ix = create_governance(
|
||||||
&self.governance_program_id,
|
&self.governance_program_id,
|
||||||
&realm_address,
|
&realm_address,
|
||||||
&governed_account_address,
|
Some(&governed_account_address),
|
||||||
&token_owner_record_address,
|
&token_owner_record_address,
|
||||||
&self.bench.payer.pubkey(),
|
&self.bench.payer.pubkey(),
|
||||||
&token_owner.pubkey(),
|
&token_owner.pubkey(),
|
||||||
|
|
|
@ -7,7 +7,7 @@ use solana_program::{
|
||||||
use spl_governance_addin_api::voter_weight::{VoterWeightAction, VoterWeightRecord};
|
use spl_governance_addin_api::voter_weight::{VoterWeightAction, VoterWeightRecord};
|
||||||
use spl_governance_tools::account::get_account_data;
|
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
|
/// Asserts the VoterWeightRecord hasn't expired and matches the given action and target if specified
|
||||||
pub fn assert_is_valid_voter_weight(
|
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(
|
pub fn get_voter_weight_record_data_for_token_owner_record(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
voter_weight_record_info: &AccountInfo,
|
voter_weight_record_info: &AccountInfo,
|
||||||
token_owner_record: &TokenOwnerRecord,
|
token_owner_record: &TokenOwnerRecordV2,
|
||||||
) -> Result<VoterWeightRecord, ProgramError> {
|
) -> Result<VoterWeightRecord, ProgramError> {
|
||||||
let voter_weight_record_data =
|
let voter_weight_record_data =
|
||||||
get_voter_weight_record_data(program_id, voter_weight_record_info)?;
|
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::{
|
use crate::state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
proposal::get_proposal_data,
|
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,
|
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)?;
|
token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
|
||||||
|
|
||||||
let signatory_record_data = SignatoryRecord {
|
let signatory_record_data = SignatoryRecordV2 {
|
||||||
account_type: GovernanceAccountType::SignatoryRecord,
|
account_type: GovernanceAccountType::SignatoryRecordV2,
|
||||||
proposal: *proposal_info.key,
|
proposal: *proposal_info.key,
|
||||||
signatory,
|
signatory,
|
||||||
signed_off: false,
|
signed_off: false,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<SignatoryRecord>(
|
create_and_serialize_account_signed::<SignatoryRecordV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
signatory_record_info,
|
signatory_record_info,
|
||||||
&signatory_record_data,
|
&signatory_record_data,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
|
|
|
@ -26,8 +26,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
|
|
||||||
/// Processes CastVote instruction
|
/// Processes CastVote instruction
|
||||||
pub fn process_cast_vote(
|
pub fn process_cast_vote(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
|
@ -176,6 +174,8 @@ pub fn process_cast_vote(
|
||||||
governance_data.serialize(&mut *governance_info.data.borrow_mut())?;
|
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
|
voter_token_owner_record_data
|
||||||
.serialize(&mut *voter_token_owner_record_info.data.borrow_mut())?;
|
.serialize(&mut *voter_token_owner_record_info.data.borrow_mut())?;
|
||||||
|
|
||||||
|
@ -185,10 +185,11 @@ pub fn process_cast_vote(
|
||||||
let vote_record_data = VoteRecordV2 {
|
let vote_record_data = VoteRecordV2 {
|
||||||
account_type: GovernanceAccountType::VoteRecordV2,
|
account_type: GovernanceAccountType::VoteRecordV2,
|
||||||
proposal: *proposal_info.key,
|
proposal: *proposal_info.key,
|
||||||
governing_token_owner: voter_token_owner_record_data.governing_token_owner,
|
governing_token_owner,
|
||||||
voter_weight,
|
voter_weight,
|
||||||
vote,
|
vote,
|
||||||
is_relinquished: false,
|
is_relinquished: false,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<VoteRecordV2>(
|
create_and_serialize_account_signed::<VoteRecordV2>(
|
||||||
|
|
|
@ -3,8 +3,8 @@
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
governance::{
|
governance::{
|
||||||
assert_valid_create_governance_args, get_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_governance_address_seeds, GovernanceConfig,
|
||||||
GovernanceConfig,
|
GovernanceV2,
|
||||||
},
|
},
|
||||||
realm::get_realm_data,
|
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
|
account_info_iter, // realm_config_info 7, voter_weight_record_info 8
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let governance_data = Governance {
|
let governance_data = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::Governance,
|
account_type: GovernanceAccountType::GovernanceV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governed_account: *governed_account_info.key,
|
governed_account: *governed_account_info.key,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<Governance>(
|
create_and_serialize_account_signed::<GovernanceV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
governance_info,
|
governance_info,
|
||||||
&governance_data,
|
&governance_data,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
||||||
state::{
|
state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
governance::{
|
governance::{
|
||||||
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_mint_governance_address_seeds,
|
||||||
GovernanceConfig,
|
GovernanceConfig, GovernanceV2,
|
||||||
},
|
},
|
||||||
realm::get_realm_data,
|
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
|
account_info_iter, // realm_config_info 9, voter_weight_record_info 10
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mint_governance_data = Governance {
|
let mint_governance_data = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::MintGovernance,
|
account_type: GovernanceAccountType::MintGovernanceV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governed_account: *governed_mint_info.key,
|
governed_account: *governed_mint_info.key,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<Governance>(
|
create_and_serialize_account_signed::<GovernanceV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
mint_governance_info,
|
mint_governance_info,
|
||||||
&mint_governance_data,
|
&mint_governance_data,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
state::governance::Governance,
|
state::governance::GovernanceV2,
|
||||||
state::{
|
state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
governance::{
|
governance::{
|
||||||
|
@ -63,17 +63,18 @@ pub fn process_create_program_governance(
|
||||||
account_info_iter, // realm_config_info 10, voter_weight_record_info 11
|
account_info_iter, // realm_config_info 10, voter_weight_record_info 11
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let program_governance_data = Governance {
|
let program_governance_data = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::ProgramGovernance,
|
account_type: GovernanceAccountType::ProgramGovernanceV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governed_account: *governed_program_info.key,
|
governed_account: *governed_program_info.key,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<Governance>(
|
create_and_serialize_account_signed::<GovernanceV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
program_governance_info,
|
program_governance_info,
|
||||||
&program_governance_data,
|
&program_governance_data,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
|
|
|
@ -15,7 +15,7 @@ use crate::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
realm::{
|
realm::{
|
||||||
assert_valid_realm_config_args, get_governing_token_holding_address_seeds,
|
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},
|
realm_config::{get_realm_config_address_seeds, RealmConfigAccount},
|
||||||
},
|
},
|
||||||
|
@ -125,8 +125,8 @@ pub fn process_create_realm(
|
||||||
)?;
|
)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let realm_data = Realm {
|
let realm_data = RealmV2 {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
community_mint: *governance_token_mint_info.key,
|
community_mint: *governance_token_mint_info.key,
|
||||||
|
|
||||||
name: name.clone(),
|
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,
|
use_max_community_voter_weight_addin: config_args.use_max_community_voter_weight_addin,
|
||||||
},
|
},
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<Realm>(
|
create_and_serialize_account_signed::<RealmV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
realm_info,
|
realm_info,
|
||||||
&realm_data,
|
&realm_data,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use crate::{
|
||||||
state::{
|
state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
governance::{
|
governance::{
|
||||||
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_token_governance_address_seeds,
|
||||||
GovernanceConfig,
|
GovernanceConfig, GovernanceV2,
|
||||||
},
|
},
|
||||||
realm::get_realm_data,
|
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
|
account_info_iter, // realm_config_info 9, voter_weight_record_info 10
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let token_governance_data = Governance {
|
let token_governance_data = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::TokenGovernance,
|
account_type: GovernanceAccountType::TokenGovernanceV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governed_account: *governed_token_info.key,
|
governed_account: *governed_token_info.key,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<Governance>(
|
create_and_serialize_account_signed::<GovernanceV2>(
|
||||||
payer_info,
|
payer_info,
|
||||||
token_governance_info,
|
token_governance_info,
|
||||||
&token_governance_data,
|
&token_governance_data,
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
state::{
|
state::{
|
||||||
enums::GovernanceAccountType,
|
enums::GovernanceAccountType,
|
||||||
realm::get_realm_data,
|
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());
|
return Err(GovernanceError::TokenOwnerRecordAlreadyExists.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let token_owner_record_data = TokenOwnerRecord {
|
let token_owner_record_data = TokenOwnerRecordV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governing_token_owner: *governing_token_owner_info.key,
|
governing_token_owner: *governing_token_owner_info.key,
|
||||||
governing_token_deposit_amount: 0,
|
governing_token_deposit_amount: 0,
|
||||||
|
@ -51,6 +51,7 @@ pub fn process_create_token_owner_record(
|
||||||
total_votes_count: 0,
|
total_votes_count: 0,
|
||||||
outstanding_proposal_count: 0,
|
outstanding_proposal_count: 0,
|
||||||
reserved: [0; 7],
|
reserved: [0; 7],
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed(
|
create_and_serialize_account_signed(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
@ -17,7 +16,7 @@ use crate::{
|
||||||
realm::get_realm_data,
|
realm::get_realm_data,
|
||||||
token_owner_record::{
|
token_owner_record::{
|
||||||
get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds,
|
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},
|
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());
|
return Err(GovernanceError::GoverningTokenOwnerMustSign.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let token_owner_record_data = TokenOwnerRecord {
|
let token_owner_record_data = TokenOwnerRecordV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
governing_token_owner: *governing_token_owner_info.key,
|
governing_token_owner: *governing_token_owner_info.key,
|
||||||
governing_token_deposit_amount: amount,
|
governing_token_deposit_amount: amount,
|
||||||
|
@ -90,6 +89,7 @@ pub fn process_deposit_governing_tokens(
|
||||||
total_votes_count: 0,
|
total_votes_count: 0,
|
||||||
outstanding_proposal_count: 0,
|
outstanding_proposal_count: 0,
|
||||||
reserved: [0; 7],
|
reserved: [0; 7],
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed(
|
create_and_serialize_account_signed(
|
||||||
|
|
|
@ -15,8 +15,6 @@ use crate::state::{
|
||||||
token_owner_record::get_token_owner_record_data_for_proposal_owner,
|
token_owner_record::get_token_owner_record_data_for_proposal_owner,
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
|
|
||||||
/// Processes FinalizeVote instruction
|
/// Processes FinalizeVote instruction
|
||||||
pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
|
|
@ -94,6 +94,7 @@ pub fn process_insert_transaction(
|
||||||
executed_at: None,
|
executed_at: None,
|
||||||
execution_status: TransactionExecutionStatus::None,
|
execution_status: TransactionExecutionStatus::None,
|
||||||
proposal: *proposal_info.key,
|
proposal: *proposal_info.key,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
create_and_serialize_account_signed::<ProposalTransactionV2>(
|
create_and_serialize_account_signed::<ProposalTransactionV2>(
|
||||||
|
|
|
@ -20,8 +20,6 @@ use crate::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
|
|
||||||
/// Processes RelinquishVote instruction
|
/// Processes RelinquishVote instruction
|
||||||
pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
|
||||||
let account_info_iter = &mut accounts.iter();
|
let account_info_iter = &mut accounts.iter();
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
clock::Clock,
|
clock::Clock,
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
//! Program state processor
|
//! Program state processor
|
||||||
|
|
||||||
use borsh::BorshSerialize;
|
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
entrypoint::ProgramResult,
|
entrypoint::ProgramResult,
|
||||||
|
|
|
@ -10,22 +10,22 @@ pub enum GovernanceAccountType {
|
||||||
Uninitialized,
|
Uninitialized,
|
||||||
|
|
||||||
/// Top level aggregation for governances with Community Token (and optional Council Token)
|
/// 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
|
/// Token Owner Record for given governing token owner within a Realm
|
||||||
TokenOwnerRecord,
|
TokenOwnerRecordV1,
|
||||||
|
|
||||||
/// Governance account
|
/// Governance account
|
||||||
Governance,
|
GovernanceV1,
|
||||||
|
|
||||||
/// Program Governance account
|
/// Program Governance account
|
||||||
ProgramGovernance,
|
ProgramGovernanceV1,
|
||||||
|
|
||||||
/// Proposal account for Governance account. A single Governance account can have multiple Proposal accounts
|
/// Proposal account for Governance account. A single Governance account can have multiple Proposal accounts
|
||||||
ProposalV1,
|
ProposalV1,
|
||||||
|
|
||||||
/// Proposal Signatory account
|
/// Proposal Signatory account
|
||||||
SignatoryRecord,
|
SignatoryRecordV1,
|
||||||
|
|
||||||
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
|
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
|
||||||
VoteRecordV1,
|
VoteRecordV1,
|
||||||
|
@ -34,12 +34,12 @@ pub enum GovernanceAccountType {
|
||||||
ProposalInstructionV1,
|
ProposalInstructionV1,
|
||||||
|
|
||||||
/// Mint Governance account
|
/// Mint Governance account
|
||||||
MintGovernance,
|
MintGovernanceV1,
|
||||||
|
|
||||||
/// Token Governance account
|
/// Token Governance account
|
||||||
TokenGovernance,
|
TokenGovernanceV1,
|
||||||
|
|
||||||
/// Realm config account
|
/// Realm config account (introduced in V2)
|
||||||
RealmConfig,
|
RealmConfig,
|
||||||
|
|
||||||
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
|
/// 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
|
/// V2 adds support for multiple vote options
|
||||||
ProposalV2,
|
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,
|
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 {
|
impl Default for GovernanceAccountType {
|
||||||
|
|
|
@ -1,19 +1,21 @@
|
||||||
//! Governance Account
|
//! Governance Account
|
||||||
|
use borsh::maybestd::io::Write;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::GovernanceError,
|
error::GovernanceError,
|
||||||
state::{
|
state::{
|
||||||
enums::{GovernanceAccountType, VoteThresholdPercentage, VoteTipping},
|
enums::{GovernanceAccountType, VoteThresholdPercentage, VoteTipping},
|
||||||
|
legacy::{is_governance_v1_account_type, GovernanceV1},
|
||||||
realm::assert_is_valid_realm,
|
realm::assert_is_valid_realm,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
account_info::AccountInfo, borsh::try_from_slice_unchecked, program_error::ProgramError,
|
||||||
pubkey::Pubkey,
|
program_pack::IsInitialized, pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use spl_governance_tools::{
|
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,
|
error::GovernanceToolsError,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,7 +51,7 @@ pub struct GovernanceConfig {
|
||||||
/// Governance Account
|
/// Governance Account
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct Governance {
|
pub struct GovernanceV2 {
|
||||||
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
|
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
|
||||||
pub account_type: GovernanceAccountType,
|
pub account_type: GovernanceAccountType,
|
||||||
|
|
||||||
|
@ -78,33 +80,43 @@ pub struct Governance {
|
||||||
|
|
||||||
/// The number of proposals in voting state in the Governance
|
/// The number of proposals in voting state in the Governance
|
||||||
pub voting_proposal_count: u16,
|
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 {
|
fn is_initialized(&self) -> bool {
|
||||||
self.account_type == GovernanceAccountType::Governance
|
is_governance_v2_account_type(&self.account_type)
|
||||||
|| self.account_type == GovernanceAccountType::ProgramGovernance
|
|
||||||
|| self.account_type == GovernanceAccountType::MintGovernance
|
|
||||||
|| self.account_type == GovernanceAccountType::TokenGovernance
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Governance {
|
impl GovernanceV2 {
|
||||||
/// Returns Governance PDA seeds
|
/// Returns Governance PDA seeds
|
||||||
pub fn get_governance_address_seeds(&self) -> Result<[&[u8]; 3], ProgramError> {
|
pub fn get_governance_address_seeds(&self) -> Result<[&[u8]; 3], ProgramError> {
|
||||||
let seeds = match self.account_type {
|
let seeds = match self.account_type {
|
||||||
GovernanceAccountType::Governance => {
|
GovernanceAccountType::GovernanceV1 | GovernanceAccountType::GovernanceV2 => {
|
||||||
get_governance_address_seeds(&self.realm, &self.governed_account)
|
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)
|
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)
|
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)
|
get_token_governance_address_seeds(&self.realm, &self.governed_account)
|
||||||
}
|
}
|
||||||
_ => return Err(GovernanceToolsError::InvalidAccountType.into()),
|
_ => return Err(GovernanceToolsError::InvalidAccountType.into()),
|
||||||
|
@ -112,14 +124,67 @@ impl Governance {
|
||||||
|
|
||||||
Ok(seeds)
|
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
|
/// Deserializes Governance account and checks owner program
|
||||||
pub fn get_governance_data(
|
pub fn get_governance_data(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
governance_info: &AccountInfo,
|
governance_info: &AccountInfo,
|
||||||
) -> Result<Governance, ProgramError> {
|
) -> Result<GovernanceV2, ProgramError> {
|
||||||
get_account_data::<Governance>(program_id, governance_info)
|
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
|
/// 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,
|
program_id: &Pubkey,
|
||||||
governance_info: &AccountInfo,
|
governance_info: &AccountInfo,
|
||||||
realm: &Pubkey,
|
realm: &Pubkey,
|
||||||
) -> Result<Governance, ProgramError> {
|
) -> Result<GovernanceV2, ProgramError> {
|
||||||
let governance_data = get_governance_data(program_id, governance_info)?;
|
let governance_data = get_governance_data(program_id, governance_info)?;
|
||||||
|
|
||||||
if governance_data.realm != *realm {
|
if governance_data.realm != *realm {
|
||||||
|
@ -250,13 +315,17 @@ pub fn assert_is_valid_governance(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
governance_info: &AccountInfo,
|
governance_info: &AccountInfo,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
assert_is_valid_account2(
|
assert_is_valid_account_of_types(
|
||||||
governance_info,
|
governance_info,
|
||||||
&[
|
&[
|
||||||
GovernanceAccountType::Governance,
|
GovernanceAccountType::GovernanceV1,
|
||||||
GovernanceAccountType::ProgramGovernance,
|
GovernanceAccountType::GovernanceV2,
|
||||||
GovernanceAccountType::TokenGovernance,
|
GovernanceAccountType::ProgramGovernanceV1,
|
||||||
GovernanceAccountType::MintGovernance,
|
GovernanceAccountType::ProgramGovernanceV2,
|
||||||
|
GovernanceAccountType::TokenGovernanceV1,
|
||||||
|
GovernanceAccountType::TokenGovernanceV2,
|
||||||
|
GovernanceAccountType::MintGovernanceV1,
|
||||||
|
GovernanceAccountType::MintGovernanceV2,
|
||||||
],
|
],
|
||||||
program_id,
|
program_id,
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
use crate::state::{
|
use crate::state::{
|
||||||
enums::{
|
enums::{
|
||||||
GovernanceAccountType, InstructionExecutionFlags, MintMaxVoteWeightSource, ProposalState,
|
GovernanceAccountType, InstructionExecutionFlags, ProposalState,
|
||||||
TransactionExecutionStatus, VoteThresholdPercentage,
|
TransactionExecutionStatus, VoteThresholdPercentage,
|
||||||
},
|
},
|
||||||
|
governance::GovernanceConfig,
|
||||||
proposal_transaction::InstructionData,
|
proposal_transaction::InstructionData,
|
||||||
|
realm::RealmConfig,
|
||||||
};
|
};
|
||||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
|
@ -14,58 +16,6 @@ use solana_program::{
|
||||||
pubkey::Pubkey,
|
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
|
/// Governance Realm Account
|
||||||
/// Account PDA seeds" ['governance', name]
|
/// Account PDA seeds" ['governance', name]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
|
@ -77,10 +27,15 @@ pub struct RealmV1 {
|
||||||
pub community_mint: Pubkey,
|
pub community_mint: Pubkey,
|
||||||
|
|
||||||
/// Configuration of the Realm
|
/// Configuration of the Realm
|
||||||
pub config: RealmConfigV1,
|
pub config: RealmConfig,
|
||||||
|
|
||||||
/// Reserved space for future versions
|
/// 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
|
/// 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
|
/// 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 {
|
impl IsInitialized for RealmV1 {
|
||||||
fn is_initialized(&self) -> bool {
|
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
|
/// Proposal instruction V1
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct ProposalInstructionV1 {
|
pub struct ProposalInstructionV1 {
|
||||||
|
|
|
@ -65,7 +65,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_size() {
|
fn test_max_size() {
|
||||||
let program_metadata_data = ProgramMetadata {
|
let program_metadata_data = ProgramMetadata {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
updated_at: 10,
|
updated_at: 10,
|
||||||
reserved: [0; 64],
|
reserved: [0; 64],
|
||||||
version: "111.122.155".to_string(),
|
version: "111.122.155".to_string(),
|
||||||
|
|
|
@ -29,7 +29,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
governance::GovernanceConfig,
|
governance::GovernanceConfig,
|
||||||
proposal_transaction::ProposalTransactionV2,
|
proposal_transaction::ProposalTransactionV2,
|
||||||
realm::Realm,
|
realm::RealmV2,
|
||||||
realm_config::get_realm_config_data_for_realm,
|
realm_config::get_realm_config_data_for_realm,
|
||||||
vote_record::Vote,
|
vote_record::Vote,
|
||||||
},
|
},
|
||||||
|
@ -413,7 +413,7 @@ impl ProposalV2 {
|
||||||
/// Calculates max voter weight for given mint supply and realm config
|
/// Calculates max voter weight for given mint supply and realm config
|
||||||
fn get_max_voter_weight_from_mint_supply(
|
fn get_max_voter_weight_from_mint_supply(
|
||||||
&mut self,
|
&mut self,
|
||||||
realm_data: &Realm,
|
realm_data: &RealmV2,
|
||||||
governing_token_mint_supply: u64,
|
governing_token_mint_supply: u64,
|
||||||
) -> Result<u64, ProgramError> {
|
) -> Result<u64, ProgramError> {
|
||||||
// max vote weight fraction is only used for community mint
|
// max vote weight fraction is only used for community mint
|
||||||
|
@ -464,7 +464,7 @@ impl ProposalV2 {
|
||||||
governing_token_mint_info: &AccountInfo,
|
governing_token_mint_info: &AccountInfo,
|
||||||
account_info_iter: &mut Iter<AccountInfo>,
|
account_info_iter: &mut Iter<AccountInfo>,
|
||||||
realm: &Pubkey,
|
realm: &Pubkey,
|
||||||
realm_data: &Realm,
|
realm_data: &RealmV2,
|
||||||
) -> Result<u64, ProgramError> {
|
) -> Result<u64, ProgramError> {
|
||||||
// if the realm uses addin for max community voter weight then use the externally provided max weight
|
// 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
|
if realm_data.config.use_max_community_voter_weight_addin
|
||||||
|
@ -774,7 +774,7 @@ impl ProposalV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.options.len() != 1 {
|
if self.options.len() != 1 {
|
||||||
panic!("ProposalV1 doesn't multiple options")
|
panic!("ProposalV1 doesn't support multiple options")
|
||||||
}
|
}
|
||||||
|
|
||||||
let proposal_data_v1 = ProposalV1 {
|
let proposal_data_v1 = ProposalV1 {
|
||||||
|
@ -1011,7 +1011,7 @@ mod test {
|
||||||
|
|
||||||
fn create_test_proposal() -> ProposalV2 {
|
fn create_test_proposal() -> ProposalV2 {
|
||||||
ProposalV2 {
|
ProposalV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
governance: Pubkey::new_unique(),
|
governance: Pubkey::new_unique(),
|
||||||
governing_token_mint: Pubkey::new_unique(),
|
governing_token_mint: Pubkey::new_unique(),
|
||||||
max_vote_weight: Some(10),
|
max_vote_weight: Some(10),
|
||||||
|
@ -1087,9 +1087,9 @@ mod test {
|
||||||
proposal
|
proposal
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_test_realm() -> Realm {
|
fn create_test_realm() -> RealmV2 {
|
||||||
Realm {
|
RealmV2 {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
community_mint: Pubkey::new_unique(),
|
community_mint: Pubkey::new_unique(),
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
|
|
||||||
|
@ -1106,6 +1106,7 @@ mod test {
|
||||||
min_community_weight_to_create_governance: 10,
|
min_community_weight_to_create_governance: 10,
|
||||||
},
|
},
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -114,6 +114,10 @@ pub struct ProposalTransactionV2 {
|
||||||
|
|
||||||
/// Instruction execution status
|
/// Instruction execution status
|
||||||
pub execution_status: TransactionExecutionStatus,
|
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 {
|
impl AccountMaxSize for ProposalTransactionV2 {
|
||||||
|
@ -125,7 +129,7 @@ impl AccountMaxSize for ProposalTransactionV2 {
|
||||||
.sum::<usize>()
|
.sum::<usize>()
|
||||||
+ 4;
|
+ 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
|
// 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 {
|
let proposal_transaction_data_v1 = ProposalInstructionV1 {
|
||||||
account_type: self.account_type,
|
account_type: self.account_type,
|
||||||
proposal: self.proposal,
|
proposal: self.proposal,
|
||||||
|
@ -217,6 +227,7 @@ pub fn get_proposal_transaction_data(
|
||||||
instructions: vec![proposal_transaction_data_v1.instruction],
|
instructions: vec![proposal_transaction_data_v1.instruction],
|
||||||
executed_at: proposal_transaction_data_v1.executed_at,
|
executed_at: proposal_transaction_data_v1.executed_at,
|
||||||
execution_status: proposal_transaction_data_v1.execution_status,
|
execution_status: proposal_transaction_data_v1.execution_status,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,6 +288,7 @@ mod test {
|
||||||
instructions: create_test_instruction_data(),
|
instructions: create_test_instruction_data(),
|
||||||
executed_at: Some(100),
|
executed_at: Some(100),
|
||||||
execution_status: TransactionExecutionStatus::Success,
|
execution_status: TransactionExecutionStatus::Success,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,26 @@
|
||||||
//! Realm Account
|
//! Realm Account
|
||||||
|
|
||||||
|
use borsh::maybestd::io::Write;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
|
|
||||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
|
borsh::try_from_slice_unchecked,
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
program_pack::IsInitialized,
|
program_pack::IsInitialized,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
};
|
};
|
||||||
use spl_governance_addin_api::voter_weight::VoterWeightAction;
|
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::{
|
use crate::{
|
||||||
error::GovernanceError,
|
error::GovernanceError,
|
||||||
state::{
|
state::{
|
||||||
enums::{GovernanceAccountType, MintMaxVoteWeightSource},
|
enums::{GovernanceAccountType, MintMaxVoteWeightSource},
|
||||||
|
legacy::RealmV1,
|
||||||
token_owner_record::get_token_owner_record_data_for_realm,
|
token_owner_record::get_token_owner_record_data_for_realm,
|
||||||
},
|
},
|
||||||
PROGRAM_AUTHORITY_SEED,
|
PROGRAM_AUTHORITY_SEED,
|
||||||
|
@ -87,7 +92,7 @@ pub struct RealmConfig {
|
||||||
/// Account PDA seeds" ['governance', name]
|
/// Account PDA seeds" ['governance', name]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct Realm {
|
pub struct RealmV2 {
|
||||||
/// Governance account type
|
/// Governance account type
|
||||||
pub account_type: GovernanceAccountType,
|
pub account_type: GovernanceAccountType,
|
||||||
|
|
||||||
|
@ -109,21 +114,25 @@ pub struct Realm {
|
||||||
|
|
||||||
/// Governance Realm name
|
/// Governance Realm name
|
||||||
pub name: String,
|
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> {
|
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 {
|
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
|
/// Asserts the given mint is either Community or Council mint of the Realm
|
||||||
pub fn assert_is_valid_governing_token_mint(
|
pub fn assert_is_valid_governing_token_mint(
|
||||||
&self,
|
&self,
|
||||||
|
@ -215,6 +224,34 @@ impl Realm {
|
||||||
|
|
||||||
Ok(())
|
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
|
/// 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,
|
program_id: &Pubkey,
|
||||||
realm_info: &AccountInfo,
|
realm_info: &AccountInfo,
|
||||||
) -> Result<(), ProgramError> {
|
) -> 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
|
/// Deserializes account and checks owner program
|
||||||
pub fn get_realm_data(
|
pub fn get_realm_data(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
realm_info: &AccountInfo,
|
realm_info: &AccountInfo,
|
||||||
) -> Result<Realm, ProgramError> {
|
) -> Result<RealmV2, ProgramError> {
|
||||||
get_account_data::<Realm>(program_id, realm_info)
|
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
|
/// 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,
|
program_id: &Pubkey,
|
||||||
realm_info: &AccountInfo,
|
realm_info: &AccountInfo,
|
||||||
realm_authority: &Pubkey,
|
realm_authority: &Pubkey,
|
||||||
) -> Result<Realm, ProgramError> {
|
) -> Result<RealmV2, ProgramError> {
|
||||||
let realm_data = get_account_data::<Realm>(program_id, realm_info)?;
|
let realm_data = get_account_data::<RealmV2>(program_id, realm_info)?;
|
||||||
|
|
||||||
if realm_data.authority.is_none() {
|
if realm_data.authority.is_none() {
|
||||||
return Err(GovernanceError::RealmHasNoAuthority.into());
|
return Err(GovernanceError::RealmHasNoAuthority.into());
|
||||||
|
@ -257,7 +320,7 @@ pub fn get_realm_data_for_governing_token_mint(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
realm_info: &AccountInfo,
|
realm_info: &AccountInfo,
|
||||||
governing_token_mint: &Pubkey,
|
governing_token_mint: &Pubkey,
|
||||||
) -> Result<Realm, ProgramError> {
|
) -> Result<RealmV2, ProgramError> {
|
||||||
let realm_data = get_realm_data(program_id, realm_info)?;
|
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||||
|
|
||||||
realm_data.assert_is_valid_governing_token_mint(governing_token_mint)?;
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use crate::{
|
use crate::instruction::GovernanceInstruction;
|
||||||
instruction::GovernanceInstruction,
|
|
||||||
state::legacy::{GovernanceInstructionV1, RealmConfigV1, RealmV1},
|
|
||||||
};
|
|
||||||
use solana_program::borsh::try_from_slice_unchecked;
|
use solana_program::borsh::try_from_slice_unchecked;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_size() {
|
fn test_max_size() {
|
||||||
let realm = Realm {
|
let realm = RealmV2 {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
community_mint: Pubkey::new_unique(),
|
community_mint: Pubkey::new_unique(),
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
|
|
||||||
|
@ -346,6 +406,7 @@ mod test {
|
||||||
},
|
},
|
||||||
|
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let size = realm.try_to_vec().unwrap().len();
|
let size = realm.try_to_vec().unwrap().len();
|
||||||
|
@ -353,36 +414,40 @@ mod test {
|
||||||
assert_eq!(realm.get_max_size(), Some(size));
|
assert_eq!(realm.get_max_size(), Some(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
/// Realm Config instruction args
|
||||||
fn test_deserialize_v2_realm_account_from_v1() {
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
// Arrange
|
pub struct RealmConfigArgsV1 {
|
||||||
let realm_v1 = RealmV1 {
|
/// Indicates whether council_mint should be used
|
||||||
account_type: GovernanceAccountType::Realm,
|
/// If yes then council_mint account must also be passed to the instruction
|
||||||
community_mint: Pubkey::new_unique(),
|
pub use_council_mint: bool,
|
||||||
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(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut realm_v1_data = vec![];
|
/// Min number of community tokens required to create a governance
|
||||||
realm_v1.serialize(&mut realm_v1_data).unwrap();
|
pub min_community_weight_to_create_governance: u64,
|
||||||
|
|
||||||
// Act
|
/// The source used for community mint max vote weight source
|
||||||
let realm_v2: Realm = try_from_slice_unchecked(&realm_v1_data).unwrap();
|
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
|
||||||
|
}
|
||||||
|
|
||||||
// Assert
|
/// Instructions supported by the Governance program
|
||||||
assert!(!realm_v2.config.use_community_voter_weight_addin);
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
assert_eq!(realm_v2.account_type, GovernanceAccountType::Realm);
|
pub enum GovernanceInstructionV1 {
|
||||||
assert_eq!(
|
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
|
||||||
realm_v2.config.min_community_weight_to_create_governance,
|
CreateRealm {
|
||||||
realm_v1.config.min_community_weight_to_create_governance,
|
#[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]
|
#[test]
|
||||||
|
|
|
@ -92,7 +92,7 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_size() {
|
fn test_max_size() {
|
||||||
let realm_config = RealmConfigAccount {
|
let realm_config = RealmConfigAccount {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
realm: Pubkey::new_unique(),
|
realm: Pubkey::new_unique(),
|
||||||
community_voter_weight_addin: Some(Pubkey::new_unique()),
|
community_voter_weight_addin: Some(Pubkey::new_unique()),
|
||||||
max_community_voter_weight_addin: Some(Pubkey::new_unique()),
|
max_community_voter_weight_addin: Some(Pubkey::new_unique()),
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
//! Signatory Record
|
//! Signatory Record
|
||||||
|
|
||||||
|
use borsh::maybestd::io::Write;
|
||||||
|
|
||||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
|
use solana_program::borsh::try_from_slice_unchecked;
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -11,10 +14,12 @@ use crate::{error::GovernanceError, PROGRAM_AUTHORITY_SEED};
|
||||||
|
|
||||||
use crate::state::enums::GovernanceAccountType;
|
use crate::state::enums::GovernanceAccountType;
|
||||||
|
|
||||||
|
use super::legacy::SignatoryRecordV1;
|
||||||
|
|
||||||
/// Account PDA seeds: ['governance', proposal, signatory]
|
/// Account PDA seeds: ['governance', proposal, signatory]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct SignatoryRecord {
|
pub struct SignatoryRecordV2 {
|
||||||
/// Governance account type
|
/// Governance account type
|
||||||
pub account_type: GovernanceAccountType,
|
pub account_type: GovernanceAccountType,
|
||||||
|
|
||||||
|
@ -26,17 +31,21 @@ pub struct SignatoryRecord {
|
||||||
|
|
||||||
/// Indicates whether the signatory signed off the proposal
|
/// Indicates whether the signatory signed off the proposal
|
||||||
pub signed_off: bool,
|
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 {
|
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
|
/// Checks signatory hasn't signed off yet and is transaction signer
|
||||||
pub fn assert_can_sign_off(&self, signatory_info: &AccountInfo) -> Result<(), ProgramError> {
|
pub fn assert_can_sign_off(&self, signatory_info: &AccountInfo) -> Result<(), ProgramError> {
|
||||||
if self.signed_off {
|
if self.signed_off {
|
||||||
|
@ -58,6 +67,31 @@ impl SignatoryRecord {
|
||||||
|
|
||||||
Ok(())
|
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
|
/// Returns SignatoryRecord PDA seeds
|
||||||
|
@ -89,8 +123,28 @@ pub fn get_signatory_record_address<'a>(
|
||||||
pub fn get_signatory_record_data(
|
pub fn get_signatory_record_data(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
signatory_record_info: &AccountInfo,
|
signatory_record_info: &AccountInfo,
|
||||||
) -> Result<SignatoryRecord, ProgramError> {
|
) -> Result<SignatoryRecordV2, ProgramError> {
|
||||||
get_account_data::<SignatoryRecord>(program_id, signatory_record_info)
|
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
|
/// Deserializes SignatoryRecord and validates its PDA
|
||||||
|
@ -99,7 +153,7 @@ pub fn get_signatory_record_data_for_seeds(
|
||||||
signatory_record_info: &AccountInfo,
|
signatory_record_info: &AccountInfo,
|
||||||
proposal: &Pubkey,
|
proposal: &Pubkey,
|
||||||
signatory: &Pubkey,
|
signatory: &Pubkey,
|
||||||
) -> Result<SignatoryRecord, ProgramError> {
|
) -> Result<SignatoryRecordV2, ProgramError> {
|
||||||
let (signatory_record_address, _) = Pubkey::find_program_address(
|
let (signatory_record_address, _) = Pubkey::find_program_address(
|
||||||
&get_signatory_record_address_seeds(proposal, signatory),
|
&get_signatory_record_address_seeds(proposal, signatory),
|
||||||
program_id,
|
program_id,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
//! Token Owner Record Account
|
//! Token Owner Record Account
|
||||||
|
|
||||||
|
use borsh::maybestd::io::Write;
|
||||||
use std::slice::Iter;
|
use std::slice::Iter;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -8,8 +9,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
error::GovernanceError,
|
error::GovernanceError,
|
||||||
state::{
|
state::{
|
||||||
enums::GovernanceAccountType, governance::GovernanceConfig, realm::Realm,
|
enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1,
|
||||||
realm_config::get_realm_config_data_for_realm,
|
realm::RealmV2, realm_config::get_realm_config_data_for_realm,
|
||||||
},
|
},
|
||||||
PROGRAM_AUTHORITY_SEED,
|
PROGRAM_AUTHORITY_SEED,
|
||||||
};
|
};
|
||||||
|
@ -17,6 +18,7 @@ use crate::{
|
||||||
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
|
||||||
use solana_program::{
|
use solana_program::{
|
||||||
account_info::{next_account_info, AccountInfo},
|
account_info::{next_account_info, AccountInfo},
|
||||||
|
borsh::try_from_slice_unchecked,
|
||||||
program_error::ProgramError,
|
program_error::ProgramError,
|
||||||
program_pack::IsInitialized,
|
program_pack::IsInitialized,
|
||||||
pubkey::Pubkey,
|
pubkey::Pubkey,
|
||||||
|
@ -28,7 +30,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize};
|
||||||
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ]
|
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
|
||||||
pub struct TokenOwnerRecord {
|
pub struct TokenOwnerRecordV2 {
|
||||||
/// Governance account type
|
/// Governance account type
|
||||||
pub account_type: GovernanceAccountType,
|
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
|
/// 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
|
/// It can be delegated to by the governing_token_owner or current governance_delegate
|
||||||
pub governance_delegate: Option<Pubkey>,
|
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> {
|
fn get_max_size(&self) -> Option<usize> {
|
||||||
Some(154)
|
Some(282)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IsInitialized for TokenOwnerRecord {
|
impl IsInitialized for TokenOwnerRecordV2 {
|
||||||
fn is_initialized(&self) -> bool {
|
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
|
/// Checks whether the provided Governance Authority signed transaction
|
||||||
pub fn assert_token_owner_or_delegate_is_signer(
|
pub fn assert_token_owner_or_delegate_is_signer(
|
||||||
&self,
|
&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
|
/// Asserts TokenOwner has enough tokens to be allowed to create proposal and doesn't have any outstanding proposals
|
||||||
pub fn assert_can_create_proposal(
|
pub fn assert_can_create_proposal(
|
||||||
&self,
|
&self,
|
||||||
realm_data: &Realm,
|
realm_data: &RealmV2,
|
||||||
config: &GovernanceConfig,
|
config: &GovernanceConfig,
|
||||||
voter_weight: u64,
|
voter_weight: u64,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
|
@ -133,7 +139,7 @@ impl TokenOwnerRecord {
|
||||||
/// Asserts TokenOwner has enough tokens to be allowed to create governance
|
/// Asserts TokenOwner has enough tokens to be allowed to create governance
|
||||||
pub fn assert_can_create_governance(
|
pub fn assert_can_create_governance(
|
||||||
&self,
|
&self,
|
||||||
realm_data: &Realm,
|
realm_data: &RealmV2,
|
||||||
voter_weight: u64,
|
voter_weight: u64,
|
||||||
) -> Result<(), ProgramError> {
|
) -> Result<(), ProgramError> {
|
||||||
let min_weight_to_create_governance =
|
let min_weight_to_create_governance =
|
||||||
|
@ -188,7 +194,7 @@ impl TokenOwnerRecord {
|
||||||
realm_config_info: &AccountInfo,
|
realm_config_info: &AccountInfo,
|
||||||
account_info_iter: &mut Iter<AccountInfo>,
|
account_info_iter: &mut Iter<AccountInfo>,
|
||||||
realm: &Pubkey,
|
realm: &Pubkey,
|
||||||
realm_data: &Realm,
|
realm_data: &RealmV2,
|
||||||
weight_action: VoterWeightAction,
|
weight_action: VoterWeightAction,
|
||||||
weight_action_target: &Pubkey,
|
weight_action_target: &Pubkey,
|
||||||
) -> Result<u64, ProgramError> {
|
) -> Result<u64, ProgramError> {
|
||||||
|
@ -218,6 +224,37 @@ impl TokenOwnerRecord {
|
||||||
Ok(self.governing_token_deposit_amount)
|
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
|
/// Returns TokenOwnerRecord PDA address
|
||||||
|
@ -252,8 +289,35 @@ pub fn get_token_owner_record_address_seeds<'a>(
|
||||||
pub fn get_token_owner_record_data(
|
pub fn get_token_owner_record_data(
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
token_owner_record_info: &AccountInfo,
|
token_owner_record_info: &AccountInfo,
|
||||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||||
get_account_data::<TokenOwnerRecord>(program_id, token_owner_record_info)
|
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
|
/// 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,
|
program_id: &Pubkey,
|
||||||
token_owner_record_info: &AccountInfo,
|
token_owner_record_info: &AccountInfo,
|
||||||
token_owner_record_seeds: &[&[u8]],
|
token_owner_record_seeds: &[&[u8]],
|
||||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||||
let (token_owner_record_address, _) =
|
let (token_owner_record_address, _) =
|
||||||
Pubkey::find_program_address(token_owner_record_seeds, program_id);
|
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,
|
program_id: &Pubkey,
|
||||||
token_owner_record_info: &AccountInfo,
|
token_owner_record_info: &AccountInfo,
|
||||||
realm: &Pubkey,
|
realm: &Pubkey,
|
||||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||||
let token_owner_record_data = get_token_owner_record_data(program_id, token_owner_record_info)?;
|
let token_owner_record_data = get_token_owner_record_data(program_id, token_owner_record_info)?;
|
||||||
|
|
||||||
if token_owner_record_data.realm != *realm {
|
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,
|
token_owner_record_info: &AccountInfo,
|
||||||
realm: &Pubkey,
|
realm: &Pubkey,
|
||||||
governing_token_mint: &Pubkey,
|
governing_token_mint: &Pubkey,
|
||||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||||
let token_owner_record_data =
|
let token_owner_record_data =
|
||||||
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm)?;
|
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,
|
program_id: &Pubkey,
|
||||||
token_owner_record_info: &AccountInfo,
|
token_owner_record_info: &AccountInfo,
|
||||||
proposal_owner: &Pubkey,
|
proposal_owner: &Pubkey,
|
||||||
) -> Result<TokenOwnerRecord, ProgramError> {
|
) -> Result<TokenOwnerRecordV2, ProgramError> {
|
||||||
if token_owner_record_info.key != proposal_owner {
|
if token_owner_record_info.key != proposal_owner {
|
||||||
return Err(GovernanceError::InvalidProposalOwnerAccount.into());
|
return Err(GovernanceError::InvalidProposalOwnerAccount.into());
|
||||||
}
|
}
|
||||||
|
@ -319,14 +383,14 @@ pub fn get_token_owner_record_data_for_proposal_owner(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use solana_program::borsh::{get_packed_len, try_from_slice_unchecked};
|
use solana_program::borsh::get_packed_len;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_max_size() {
|
fn test_max_size() {
|
||||||
let token_owner_record = TokenOwnerRecord {
|
let token_owner_record = TokenOwnerRecordV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
realm: Pubkey::new_unique(),
|
realm: Pubkey::new_unique(),
|
||||||
governing_token_mint: Pubkey::new_unique(),
|
governing_token_mint: Pubkey::new_unique(),
|
||||||
governing_token_owner: Pubkey::new_unique(),
|
governing_token_owner: Pubkey::new_unique(),
|
||||||
|
@ -336,66 +400,11 @@ mod test {
|
||||||
total_votes_count: 1,
|
total_votes_count: 1,
|
||||||
outstanding_proposal_count: 1,
|
outstanding_proposal_count: 1,
|
||||||
reserved: [0; 7],
|
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));
|
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
|
/// Voter's vote
|
||||||
pub vote: 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 {}
|
impl AccountMaxSize for VoteRecordV2 {}
|
||||||
|
@ -107,6 +111,12 @@ impl VoteRecordV2 {
|
||||||
BorshSerialize::serialize(&self, writer)?
|
BorshSerialize::serialize(&self, writer)?
|
||||||
} else if self.account_type == GovernanceAccountType::VoteRecordV1 {
|
} else if self.account_type == GovernanceAccountType::VoteRecordV1 {
|
||||||
// V1 account can't be resized and we have to translate it back to the original format
|
// 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 {
|
let vote_weight = match &self.vote {
|
||||||
Vote::Approve(_options) => VoteWeightV1::Yes(self.voter_weight),
|
Vote::Approve(_options) => VoteWeightV1::Yes(self.voter_weight),
|
||||||
Vote::Deny => VoteWeightV1::No(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,
|
is_relinquished: vote_record_data_v1.is_relinquished,
|
||||||
voter_weight,
|
voter_weight,
|
||||||
vote,
|
vote,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
use solana_program::{instruction::Instruction, pubkey::Pubkey};
|
||||||
use solana_sdk::signature::Keypair;
|
use solana_sdk::signature::Keypair;
|
||||||
use spl_governance::state::{
|
use spl_governance::state::{
|
||||||
governance::Governance, native_treasury::NativeTreasury, program_metadata::ProgramMetadata,
|
governance::GovernanceV2, native_treasury::NativeTreasury, program_metadata::ProgramMetadata,
|
||||||
proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::Realm,
|
proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::RealmV2,
|
||||||
realm_config::RealmConfigAccount, signatory_record::SignatoryRecord,
|
realm_config::RealmConfigAccount, signatory_record::SignatoryRecordV2,
|
||||||
token_owner_record::TokenOwnerRecord, vote_record::VoteRecordV2,
|
token_owner_record::TokenOwnerRecordV2, vote_record::VoteRecordV2,
|
||||||
};
|
};
|
||||||
|
|
||||||
use spl_governance_addin_api::{
|
use spl_governance_addin_api::{
|
||||||
|
@ -20,7 +20,7 @@ pub trait AccountCookie {
|
||||||
pub struct RealmCookie {
|
pub struct RealmCookie {
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
|
|
||||||
pub account: Realm,
|
pub account: RealmV2,
|
||||||
|
|
||||||
pub community_mint_authority: Keypair,
|
pub community_mint_authority: Keypair,
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ pub struct RealmConfigCookie {
|
||||||
pub struct TokenOwnerRecordCookie {
|
pub struct TokenOwnerRecordCookie {
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
|
|
||||||
pub account: TokenOwnerRecord,
|
pub account: TokenOwnerRecordV2,
|
||||||
|
|
||||||
pub token_source: Pubkey,
|
pub token_source: Pubkey,
|
||||||
|
|
||||||
|
@ -131,7 +131,7 @@ impl AccountCookie for GovernedAccountCookie {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct GovernanceCookie {
|
pub struct GovernanceCookie {
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
pub account: Governance,
|
pub account: GovernanceV2,
|
||||||
pub next_proposal_index: u32,
|
pub next_proposal_index: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ pub struct ProposalCookie {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignatoryRecordCookie {
|
pub struct SignatoryRecordCookie {
|
||||||
pub address: Pubkey,
|
pub address: Pubkey,
|
||||||
pub account: SignatoryRecord,
|
pub account: SignatoryRecordV2,
|
||||||
pub signatory: Keypair,
|
pub signatory: Keypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ use spl_governance::{
|
||||||
},
|
},
|
||||||
governance::{
|
governance::{
|
||||||
get_governance_address, get_mint_governance_address, get_program_governance_address,
|
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},
|
native_treasury::{get_native_treasury_address, NativeTreasury},
|
||||||
program_metadata::{get_program_metadata_address, ProgramMetadata},
|
program_metadata::{get_program_metadata_address, ProgramMetadata},
|
||||||
|
@ -41,12 +41,12 @@ use spl_governance::{
|
||||||
get_proposal_transaction_address, InstructionData, ProposalTransactionV2,
|
get_proposal_transaction_address, InstructionData, ProposalTransactionV2,
|
||||||
},
|
},
|
||||||
realm::{
|
realm::{
|
||||||
get_governing_token_holding_address, get_realm_address, Realm, RealmConfig,
|
get_governing_token_holding_address, get_realm_address, RealmConfig, RealmConfigArgs,
|
||||||
RealmConfigArgs, SetRealmAuthorityAction,
|
RealmV2, SetRealmAuthorityAction,
|
||||||
},
|
},
|
||||||
realm_config::{get_realm_config_address, RealmConfigAccount},
|
realm_config::{get_realm_config_address, RealmConfigAccount},
|
||||||
signatory_record::{get_signatory_record_address, SignatoryRecord},
|
signatory_record::{get_signatory_record_address, SignatoryRecordV2},
|
||||||
token_owner_record::{get_token_owner_record_address, TokenOwnerRecord},
|
token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2},
|
||||||
vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2},
|
vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2},
|
||||||
},
|
},
|
||||||
tools::bpf_loader_upgradeable::get_program_data_address,
|
tools::bpf_loader_upgradeable::get_program_data_address,
|
||||||
|
@ -298,8 +298,8 @@ impl GovernanceProgramTest {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let account = Realm {
|
let account = RealmV2 {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
community_mint: community_token_mint_keypair.pubkey(),
|
community_mint: community_token_mint_keypair.pubkey(),
|
||||||
|
|
||||||
name,
|
name,
|
||||||
|
@ -320,6 +320,7 @@ impl GovernanceProgramTest {
|
||||||
use_max_community_voter_weight_addin: false,
|
use_max_community_voter_weight_addin: false,
|
||||||
},
|
},
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some()
|
let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some()
|
||||||
|
@ -390,8 +391,8 @@ impl GovernanceProgramTest {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let account = Realm {
|
let account = RealmV2 {
|
||||||
account_type: GovernanceAccountType::Realm,
|
account_type: GovernanceAccountType::RealmV2,
|
||||||
community_mint: realm_cookie.account.community_mint,
|
community_mint: realm_cookie.account.community_mint,
|
||||||
|
|
||||||
name,
|
name,
|
||||||
|
@ -408,6 +409,7 @@ impl GovernanceProgramTest {
|
||||||
use_max_community_voter_weight_addin: false,
|
use_max_community_voter_weight_addin: false,
|
||||||
},
|
},
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let community_token_holding_address = get_governing_token_holding_address(
|
let community_token_holding_address = get_governing_token_holding_address(
|
||||||
|
@ -470,8 +472,8 @@ impl GovernanceProgramTest {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let account = TokenOwnerRecord {
|
let account = TokenOwnerRecordV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
realm: realm_cookie.address,
|
realm: realm_cookie.address,
|
||||||
governing_token_mint: realm_cookie.account.community_mint,
|
governing_token_mint: realm_cookie.account.community_mint,
|
||||||
governing_token_owner: token_owner.pubkey(),
|
governing_token_owner: token_owner.pubkey(),
|
||||||
|
@ -481,6 +483,7 @@ impl GovernanceProgramTest {
|
||||||
total_votes_count: 0,
|
total_votes_count: 0,
|
||||||
outstanding_proposal_count: 0,
|
outstanding_proposal_count: 0,
|
||||||
reserved: [0; 7],
|
reserved: [0; 7],
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let token_owner_record_address = get_token_owner_record_address(
|
let token_owner_record_address = get_token_owner_record_address(
|
||||||
|
@ -686,8 +689,8 @@ impl GovernanceProgramTest {
|
||||||
&token_owner.pubkey(),
|
&token_owner.pubkey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let account = TokenOwnerRecord {
|
let account = TokenOwnerRecordV2 {
|
||||||
account_type: GovernanceAccountType::TokenOwnerRecord,
|
account_type: GovernanceAccountType::TokenOwnerRecordV2,
|
||||||
realm: *realm_address,
|
realm: *realm_address,
|
||||||
governing_token_mint: *governing_mint,
|
governing_token_mint: *governing_mint,
|
||||||
governing_token_owner: token_owner.pubkey(),
|
governing_token_owner: token_owner.pubkey(),
|
||||||
|
@ -697,6 +700,7 @@ impl GovernanceProgramTest {
|
||||||
total_votes_count: 0,
|
total_votes_count: 0,
|
||||||
outstanding_proposal_count: 0,
|
outstanding_proposal_count: 0,
|
||||||
reserved: [0; 7],
|
reserved: [0; 7],
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string());
|
let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string());
|
||||||
|
@ -1236,14 +1240,15 @@ impl GovernanceProgramTest {
|
||||||
governance_config.clone(),
|
governance_config.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let account = Governance {
|
let account = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::Governance,
|
account_type: GovernanceAccountType::GovernanceV2,
|
||||||
realm: realm_cookie.address,
|
realm: realm_cookie.address,
|
||||||
governed_account: governed_account_cookie.address,
|
governed_account: governed_account_cookie.address,
|
||||||
config: governance_config.clone(),
|
config: governance_config.clone(),
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let default_signers = &[create_authority];
|
let default_signers = &[create_authority];
|
||||||
|
@ -1404,14 +1409,15 @@ impl GovernanceProgramTest {
|
||||||
.process_transaction(&[create_program_governance_ix], Some(signers))
|
.process_transaction(&[create_program_governance_ix], Some(signers))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let account = Governance {
|
let account = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::ProgramGovernance,
|
account_type: GovernanceAccountType::ProgramGovernanceV2,
|
||||||
realm: realm_cookie.address,
|
realm: realm_cookie.address,
|
||||||
governed_account: governed_program_cookie.address,
|
governed_account: governed_program_cookie.address,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let program_governance_address = get_program_governance_address(
|
let program_governance_address = get_program_governance_address(
|
||||||
|
@ -1527,14 +1533,15 @@ impl GovernanceProgramTest {
|
||||||
.process_transaction(&[create_mint_governance_ix], Some(signers))
|
.process_transaction(&[create_mint_governance_ix], Some(signers))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let account = Governance {
|
let account = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::MintGovernance,
|
account_type: GovernanceAccountType::MintGovernanceV2,
|
||||||
realm: realm_cookie.address,
|
realm: realm_cookie.address,
|
||||||
governed_account: governed_mint_cookie.address,
|
governed_account: governed_mint_cookie.address,
|
||||||
config: governance_config.clone(),
|
config: governance_config.clone(),
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mint_governance_address = get_mint_governance_address(
|
let mint_governance_address = get_mint_governance_address(
|
||||||
|
@ -1610,14 +1617,15 @@ impl GovernanceProgramTest {
|
||||||
.process_transaction(&[create_token_governance_ix], Some(signers))
|
.process_transaction(&[create_token_governance_ix], Some(signers))
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let account = Governance {
|
let account = GovernanceV2 {
|
||||||
account_type: GovernanceAccountType::TokenGovernance,
|
account_type: GovernanceAccountType::TokenGovernanceV2,
|
||||||
realm: realm_cookie.address,
|
realm: realm_cookie.address,
|
||||||
governed_account: governed_token_cookie.address,
|
governed_account: governed_token_cookie.address,
|
||||||
config,
|
config,
|
||||||
proposals_count: 0,
|
proposals_count: 0,
|
||||||
reserved: [0; 6],
|
reserved: [0; 6],
|
||||||
voting_proposal_count: 0,
|
voting_proposal_count: 0,
|
||||||
|
reserved_v2: [0; 128],
|
||||||
};
|
};
|
||||||
|
|
||||||
let token_governance_address = get_token_governance_address(
|
let token_governance_address = get_token_governance_address(
|
||||||
|
@ -1857,11 +1865,12 @@ impl GovernanceProgramTest {
|
||||||
&signatory.pubkey(),
|
&signatory.pubkey(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let signatory_record_data = SignatoryRecord {
|
let signatory_record_data = SignatoryRecordV2 {
|
||||||
account_type: GovernanceAccountType::SignatoryRecord,
|
account_type: GovernanceAccountType::SignatoryRecordV2,
|
||||||
proposal: proposal_cookie.address,
|
proposal: proposal_cookie.address,
|
||||||
signatory: signatory.pubkey(),
|
signatory: signatory.pubkey(),
|
||||||
signed_off: false,
|
signed_off: false,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
let signatory_record_cookie = SignatoryRecordCookie {
|
let signatory_record_cookie = SignatoryRecordCookie {
|
||||||
|
@ -2154,6 +2163,7 @@ impl GovernanceProgramTest {
|
||||||
vote,
|
vote,
|
||||||
voter_weight: vote_amount,
|
voter_weight: vote_amount,
|
||||||
is_relinquished: false,
|
is_relinquished: false,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
let vote_record_cookie = VoteRecordCookie {
|
let vote_record_cookie = VoteRecordCookie {
|
||||||
|
@ -2441,6 +2451,7 @@ impl GovernanceProgramTest {
|
||||||
executed_at: None,
|
executed_at: None,
|
||||||
execution_status: TransactionExecutionStatus::None,
|
execution_status: TransactionExecutionStatus::None,
|
||||||
proposal: proposal_cookie.address,
|
proposal: proposal_cookie.address,
|
||||||
|
reserved_v2: [0; 8],
|
||||||
};
|
};
|
||||||
|
|
||||||
instruction.accounts = instruction
|
instruction.accounts = instruction
|
||||||
|
@ -2531,9 +2542,9 @@ impl GovernanceProgramTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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
|
self.bench
|
||||||
.get_borsh_account::<TokenOwnerRecord>(address)
|
.get_borsh_account::<TokenOwnerRecordV2>(address)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2552,8 +2563,8 @@ impl GovernanceProgramTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> Realm {
|
pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> RealmV2 {
|
||||||
self.bench.get_borsh_account::<Realm>(realm_address).await
|
self.bench.get_borsh_account::<RealmV2>(realm_address).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
|
@ -2567,9 +2578,9 @@ impl GovernanceProgramTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[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
|
self.bench
|
||||||
.get_borsh_account::<Governance>(governance_address)
|
.get_borsh_account::<GovernanceV2>(governance_address)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2601,9 +2612,9 @@ impl GovernanceProgramTest {
|
||||||
pub async fn get_signatory_record_account(
|
pub async fn get_signatory_record_account(
|
||||||
&mut self,
|
&mut self,
|
||||||
proposal_address: &Pubkey,
|
proposal_address: &Pubkey,
|
||||||
) -> SignatoryRecord {
|
) -> SignatoryRecordV2 {
|
||||||
self.bench
|
self.bench
|
||||||
.get_borsh_account::<SignatoryRecord>(proposal_address)
|
.get_borsh_account::<SignatoryRecordV2>(proposal_address)
|
||||||
.await
|
.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
|
/// 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
|
/// 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,
|
account_info: &AccountInfo,
|
||||||
expected_account_type: T,
|
expected_account_type: T,
|
||||||
owner_program_id: &Pubkey,
|
owner_program_id: &Pubkey,
|
||||||
) -> Result<(), ProgramError> {
|
) -> 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
|
/// 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
|
/// 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,
|
account_info: &AccountInfo,
|
||||||
expected_account_types: &[T],
|
expected_account_types: &[T],
|
||||||
owner_program_id: &Pubkey,
|
owner_program_id: &Pubkey,
|
||||||
|
|
Loading…
Reference in New Issue