use {
crate::{
error::GovernanceError,
state::{
enums::{GovernanceAccountType, InstructionExecutionFlags, ProposalState},
governance::get_governance_data_for_realm,
proposal::{
assert_valid_proposal_options, get_proposal_address_seeds, OptionVoteResult,
ProposalOption, ProposalV2, VoteType,
},
proposal_deposit::{get_proposal_deposit_address_seeds, ProposalDeposit},
realm::get_realm_data_for_governing_token_mint,
realm_config::get_realm_config_data_for_realm,
token_owner_record::get_token_owner_record_data_for_realm,
vote_record::VoteKind,
},
},
solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint::ProgramResult,
pubkey::Pubkey,
rent::Rent,
sysvar::Sysvar,
},
spl_governance_addin_api::voter_weight::VoterWeightAction,
spl_governance_tools::account::create_and_serialize_account_signed,
};
#[allow(clippy::too_many_arguments)]
pub fn process_create_proposal(
program_id: &Pubkey,
accounts: &[AccountInfo],
name: String,
description_link: String,
vote_type: VoteType,
options: Vec<String>,
use_deny_option: bool,
proposal_seed: Pubkey,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
let realm_info = next_account_info(account_info_iter)?; let proposal_info = next_account_info(account_info_iter)?; let governance_info = next_account_info(account_info_iter)?;
let proposal_owner_record_info = next_account_info(account_info_iter)?; let governing_token_mint_info = next_account_info(account_info_iter)?; let governance_authority_info = next_account_info(account_info_iter)?;
let payer_info = next_account_info(account_info_iter)?; let system_info = next_account_info(account_info_iter)?;
let rent = Rent::get()?;
let clock = Clock::get()?;
if !proposal_info.data_is_empty() {
return Err(GovernanceError::ProposalAlreadyExists.into());
}
let realm_data = get_realm_data_for_governing_token_mint(
program_id,
realm_info,
governing_token_mint_info.key,
)?;
let mut governance_data =
get_governance_data_for_realm(program_id, governance_info, realm_info.key)?;
governance_data.assert_governing_token_mint_can_vote(
&realm_data,
governing_token_mint_info.key,
&VoteKind::Electorate,
)?;
let mut proposal_owner_record_data = get_token_owner_record_data_for_realm(
program_id,
proposal_owner_record_info,
realm_info.key,
)?;
proposal_owner_record_data
.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
let realm_config_info = next_account_info(account_info_iter)?; let realm_config_data =
get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?;
let voter_weight = proposal_owner_record_data.resolve_voter_weight(
account_info_iter, &realm_data,
&realm_config_data,
VoterWeightAction::CreateProposal,
governance_info.key,
)?;
proposal_owner_record_data.assert_can_create_proposal(
&realm_data,
&governance_data.config,
voter_weight,
)?;
proposal_owner_record_data.outstanding_proposal_count = proposal_owner_record_data
.outstanding_proposal_count
.checked_add(1)
.unwrap();
proposal_owner_record_data.serialize(&mut proposal_owner_record_info.data.borrow_mut()[..])?;
assert_valid_proposal_options(&options, &vote_type)?;
let proposal_options: Vec<ProposalOption> = options
.iter()
.map(|o| ProposalOption {
label: o.to_string(),
vote_weight: 0,
vote_result: OptionVoteResult::None,
transactions_executed_count: 0,
transactions_count: 0,
transactions_next_index: 0,
})
.collect();
let deny_vote_weight = if use_deny_option { Some(0) } else { None };
let proposal_data = ProposalV2 {
account_type: GovernanceAccountType::ProposalV2,
governance: *governance_info.key,
governing_token_mint: *governing_token_mint_info.key,
state: ProposalState::Draft,
token_owner_record: *proposal_owner_record_info.key,
signatories_count: 0,
signatories_signed_off_count: 0,
name,
description_link,
start_voting_at: None,
draft_at: clock.unix_timestamp,
signing_off_at: None,
voting_at: None,
voting_at_slot: None,
voting_completed_at: None,
executing_at: None,
closed_at: None,
execution_flags: InstructionExecutionFlags::None,
vote_type,
options: proposal_options,
deny_vote_weight,
veto_vote_weight: 0,
abstain_vote_weight: None,
max_vote_weight: None,
max_voting_time: None,
vote_threshold: None,
reserved: [0; 64],
reserved1: 0,
};
create_and_serialize_account_signed::<ProposalV2>(
payer_info,
proposal_info,
&proposal_data,
&get_proposal_address_seeds(
governance_info.key,
governing_token_mint_info.key,
&proposal_seed,
),
program_id,
system_info,
&rent,
0,
)?;
governance_data.active_proposal_count = governance_data
.active_proposal_count
.checked_add(1)
.unwrap();
let proposal_deposit_amount = governance_data.get_proposal_deposit_amount();
if proposal_deposit_amount > 0 {
let proposal_deposit_info = next_account_info(account_info_iter)?; let proposal_deposit_data = ProposalDeposit {
account_type: GovernanceAccountType::ProposalDeposit,
proposal: *proposal_info.key,
deposit_payer: *payer_info.key,
reserved: [0; 64],
};
create_and_serialize_account_signed::<ProposalDeposit>(
payer_info,
proposal_deposit_info,
&proposal_deposit_data,
&get_proposal_deposit_address_seeds(proposal_info.key, payer_info.key),
program_id,
system_info,
&rent,
proposal_deposit_amount,
)?;
}
governance_data.serialize_as_governance_v2(governance_info, payer_info, system_info, &rent)?;
Ok(())
}