use crate::{
state::{
governance::{
get_account_governance_address, get_mint_governance_address,
get_program_governance_address, get_token_governance_address, GovernanceConfig,
},
proposal::get_proposal_address,
proposal_instruction::{get_proposal_instruction_address, InstructionData},
realm::{get_governing_token_holding_address, get_realm_address},
signatory_record::get_signatory_record_address,
token_owner_record::get_token_owner_record_address,
vote_record::get_vote_record_address,
},
tools::bpf_loader_upgradeable::get_program_data_address,
};
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use solana_program::{
bpf_loader_upgradeable,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
system_program, sysvar,
};
#[repr(C)]
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum Vote {
Yes,
No,
}
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[repr(C)]
#[allow(clippy::large_enum_variant)]
pub enum GovernanceInstruction {
CreateRealm {
#[allow(dead_code)]
name: String,
},
DepositGoverningTokens {},
WithdrawGoverningTokens {},
SetGovernanceDelegate {
#[allow(dead_code)]
new_governance_delegate: Option<Pubkey>,
},
CreateAccountGovernance {
#[allow(dead_code)]
config: GovernanceConfig,
},
CreateProgramGovernance {
#[allow(dead_code)]
config: GovernanceConfig,
#[allow(dead_code)]
transfer_upgrade_authority: bool,
},
CreateProposal {
#[allow(dead_code)]
name: String,
#[allow(dead_code)]
description_link: String,
#[allow(dead_code)]
governing_token_mint: Pubkey,
},
AddSignatory {
#[allow(dead_code)]
signatory: Pubkey,
},
RemoveSignatory {
#[allow(dead_code)]
signatory: Pubkey,
},
InsertInstruction {
#[allow(dead_code)]
index: u16,
#[allow(dead_code)]
hold_up_time: u32,
#[allow(dead_code)]
instruction: InstructionData,
},
RemoveInstruction,
CancelProposal,
SignOffProposal,
CastVote {
#[allow(dead_code)]
vote: Vote,
},
FinalizeVote {},
RelinquishVote,
ExecuteInstruction,
CreateMintGovernance {
#[allow(dead_code)]
config: GovernanceConfig,
#[allow(dead_code)]
transfer_mint_authority: bool,
},
CreateTokenGovernance {
#[allow(dead_code)]
config: GovernanceConfig,
#[allow(dead_code)]
transfer_token_owner: bool,
},
SetGovernanceConfig {
#[allow(dead_code)]
config: GovernanceConfig,
},
FlagInstructionError,
SetRealmAuthority {
#[allow(dead_code)]
new_realm_authority: Option<Pubkey>,
},
}
pub fn create_realm(
program_id: &Pubkey,
realm_authority: &Pubkey,
community_token_mint: &Pubkey,
payer: &Pubkey,
council_token_mint: Option<Pubkey>,
name: String,
) -> Instruction {
let realm_address = get_realm_address(program_id, &name);
let community_token_holding_address =
get_governing_token_holding_address(program_id, &realm_address, community_token_mint);
let mut accounts = vec![
AccountMeta::new(realm_address, false),
AccountMeta::new_readonly(*realm_authority, false),
AccountMeta::new_readonly(*community_token_mint, false),
AccountMeta::new(community_token_holding_address, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
if let Some(council_token_mint) = council_token_mint {
let council_token_holding_address =
get_governing_token_holding_address(program_id, &realm_address, &council_token_mint);
accounts.push(AccountMeta::new_readonly(council_token_mint, false));
accounts.push(AccountMeta::new(council_token_holding_address, false));
}
let instruction = GovernanceInstruction::CreateRealm { name };
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn deposit_governing_tokens(
program_id: &Pubkey,
realm: &Pubkey,
governing_token_source: &Pubkey,
governing_token_owner: &Pubkey,
governing_token_transfer_authority: &Pubkey,
payer: &Pubkey,
governing_token_mint: &Pubkey,
) -> Instruction {
let token_owner_record_address = get_token_owner_record_address(
program_id,
realm,
governing_token_mint,
governing_token_owner,
);
let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, false),
AccountMeta::new(*governing_token_source, false),
AccountMeta::new_readonly(*governing_token_owner, true),
AccountMeta::new_readonly(*governing_token_transfer_authority, true),
AccountMeta::new(token_owner_record_address, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::DepositGoverningTokens {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn withdraw_governing_tokens(
program_id: &Pubkey,
realm: &Pubkey,
governing_token_destination: &Pubkey,
governing_token_owner: &Pubkey,
governing_token_mint: &Pubkey,
) -> Instruction {
let token_owner_record_address = get_token_owner_record_address(
program_id,
realm,
governing_token_mint,
governing_token_owner,
);
let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, false),
AccountMeta::new(*governing_token_destination, false),
AccountMeta::new_readonly(*governing_token_owner, true),
AccountMeta::new(token_owner_record_address, false),
AccountMeta::new_readonly(spl_token::id(), false),
];
let instruction = GovernanceInstruction::WithdrawGoverningTokens {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn set_governance_delegate(
program_id: &Pubkey,
governance_authority: &Pubkey,
realm: &Pubkey,
governing_token_mint: &Pubkey,
governing_token_owner: &Pubkey,
new_governance_delegate: &Option<Pubkey>,
) -> Instruction {
let vote_record_address = get_token_owner_record_address(
program_id,
realm,
governing_token_mint,
governing_token_owner,
);
let accounts = vec![
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(vote_record_address, false),
];
let instruction = GovernanceInstruction::SetGovernanceDelegate {
new_governance_delegate: *new_governance_delegate,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn create_account_governance(
program_id: &Pubkey,
realm: &Pubkey,
governed_account: &Pubkey,
payer: &Pubkey,
config: GovernanceConfig,
) -> Instruction {
let account_governance_address =
get_account_governance_address(program_id, realm, governed_account);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(account_governance_address, false),
AccountMeta::new_readonly(*governed_account, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::CreateAccountGovernance { config };
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn create_program_governance(
program_id: &Pubkey,
realm: &Pubkey,
governed_program: &Pubkey,
governed_program_upgrade_authority: &Pubkey,
payer: &Pubkey,
config: GovernanceConfig,
transfer_upgrade_authority: bool,
) -> Instruction {
let program_governance_address =
get_program_governance_address(program_id, realm, governed_program);
let governed_program_data_address = get_program_data_address(governed_program);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(program_governance_address, false),
AccountMeta::new_readonly(*governed_program, false),
AccountMeta::new(governed_program_data_address, false),
AccountMeta::new_readonly(*governed_program_upgrade_authority, true),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(bpf_loader_upgradeable::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::CreateProgramGovernance {
config,
transfer_upgrade_authority,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn create_mint_governance(
program_id: &Pubkey,
realm: &Pubkey,
governed_mint: &Pubkey,
governed_mint_authority: &Pubkey,
payer: &Pubkey,
config: GovernanceConfig,
transfer_mint_authority: bool,
) -> Instruction {
let mint_governance_address = get_mint_governance_address(program_id, realm, governed_mint);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(mint_governance_address, false),
AccountMeta::new(*governed_mint, false),
AccountMeta::new_readonly(*governed_mint_authority, true),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::CreateMintGovernance {
config,
transfer_mint_authority,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn create_token_governance(
program_id: &Pubkey,
realm: &Pubkey,
governed_token: &Pubkey,
governed_token_owner: &Pubkey,
payer: &Pubkey,
config: GovernanceConfig,
transfer_token_owner: bool,
) -> Instruction {
let token_governance_address = get_token_governance_address(program_id, realm, governed_token);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(token_governance_address, false),
AccountMeta::new(*governed_token, false),
AccountMeta::new_readonly(*governed_token_owner, true),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::CreateTokenGovernance {
config,
transfer_token_owner,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn create_proposal(
program_id: &Pubkey,
governance: &Pubkey,
governing_token_owner_record: &Pubkey,
governance_authority: &Pubkey,
payer: &Pubkey,
realm: &Pubkey,
name: String,
description_link: String,
governing_token_mint: &Pubkey,
proposal_index: u32,
) -> Instruction {
let proposal_address = get_proposal_address(
program_id,
governance,
governing_token_mint,
&proposal_index.to_le_bytes(),
);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(proposal_address, false),
AccountMeta::new(*governance, false),
AccountMeta::new_readonly(*governing_token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::CreateProposal {
name,
description_link,
governing_token_mint: *governing_token_mint,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn add_signatory(
program_id: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
payer: &Pubkey,
signatory: &Pubkey,
) -> Instruction {
let signatory_record_address = get_signatory_record_address(program_id, proposal, signatory);
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(signatory_record_address, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::AddSignatory {
signatory: *signatory,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn remove_signatory(
program_id: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
signatory: &Pubkey,
beneficiary: &Pubkey,
) -> Instruction {
let signatory_record_address = get_signatory_record_address(program_id, proposal, signatory);
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(signatory_record_address, false),
AccountMeta::new(*beneficiary, false),
];
let instruction = GovernanceInstruction::RemoveSignatory {
signatory: *signatory,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn sign_off_proposal(
program_id: &Pubkey,
proposal: &Pubkey,
signatory: &Pubkey,
) -> Instruction {
let signatory_record_address = get_signatory_record_address(program_id, proposal, signatory);
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new(signatory_record_address, false),
AccountMeta::new_readonly(*signatory, true),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::SignOffProposal;
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn cast_vote(
program_id: &Pubkey,
governance: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
governing_token_mint: &Pubkey,
payer: &Pubkey,
vote: Vote,
) -> Instruction {
let vote_record_address = get_vote_record_address(program_id, proposal, token_owner_record);
let accounts = vec![
AccountMeta::new_readonly(*governance, false),
AccountMeta::new(*proposal, false),
AccountMeta::new(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(vote_record_address, false),
AccountMeta::new_readonly(*governing_token_mint, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::CastVote { vote };
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn finalize_vote(
program_id: &Pubkey,
governance: &Pubkey,
proposal: &Pubkey,
governing_token_mint: &Pubkey,
) -> Instruction {
let accounts = vec![
AccountMeta::new_readonly(*governance, false),
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*governing_token_mint, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::FinalizeVote {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn relinquish_vote(
program_id: &Pubkey,
governance: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governing_token_mint: &Pubkey,
governance_authority: Option<Pubkey>,
beneficiary: Option<Pubkey>,
) -> Instruction {
let vote_record_address = get_vote_record_address(program_id, proposal, token_owner_record);
let mut accounts = vec![
AccountMeta::new_readonly(*governance, false),
AccountMeta::new(*proposal, false),
AccountMeta::new(*token_owner_record, false),
AccountMeta::new(vote_record_address, false),
AccountMeta::new_readonly(*governing_token_mint, false),
];
if let Some(governance_authority) = governance_authority {
accounts.push(AccountMeta::new_readonly(governance_authority, true));
accounts.push(AccountMeta::new(beneficiary.unwrap(), false));
}
let instruction = GovernanceInstruction::RelinquishVote {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn cancel_proposal(
program_id: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::CancelProposal {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
#[allow(clippy::too_many_arguments)]
pub fn insert_instruction(
program_id: &Pubkey,
governance: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
payer: &Pubkey,
index: u16,
hold_up_time: u32,
instruction: InstructionData,
) -> Instruction {
let proposal_instruction_address =
get_proposal_instruction_address(program_id, proposal, &index.to_le_bytes());
let accounts = vec![
AccountMeta::new_readonly(*governance, false),
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(proposal_instruction_address, false),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];
let instruction = GovernanceInstruction::InsertInstruction {
index,
hold_up_time,
instruction,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn remove_instruction(
program_id: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
proposal_instruction: &Pubkey,
beneficiary: &Pubkey,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(*proposal_instruction, false),
AccountMeta::new(*beneficiary, false),
];
let instruction = GovernanceInstruction::RemoveInstruction {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn execute_instruction(
program_id: &Pubkey,
governance: &Pubkey,
proposal: &Pubkey,
proposal_instruction: &Pubkey,
instruction_program_id: &Pubkey,
instruction_accounts: &[AccountMeta],
) -> Instruction {
let mut accounts = vec![
AccountMeta::new_readonly(*governance, false),
AccountMeta::new(*proposal, false),
AccountMeta::new(*proposal_instruction, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
AccountMeta::new_readonly(*instruction_program_id, false),
];
accounts.extend_from_slice(instruction_accounts);
let instruction = GovernanceInstruction::ExecuteInstruction {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn set_governance_config(
program_id: &Pubkey,
governance: &Pubkey,
config: GovernanceConfig,
) -> Instruction {
let accounts = vec![AccountMeta::new(*governance, true)];
let instruction = GovernanceInstruction::SetGovernanceConfig { config };
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn flag_instruction_error(
program_id: &Pubkey,
proposal: &Pubkey,
token_owner_record: &Pubkey,
governance_authority: &Pubkey,
proposal_instruction: &Pubkey,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*proposal, false),
AccountMeta::new_readonly(*token_owner_record, false),
AccountMeta::new_readonly(*governance_authority, true),
AccountMeta::new(*proposal_instruction, false),
AccountMeta::new_readonly(sysvar::clock::id(), false),
];
let instruction = GovernanceInstruction::FlagInstructionError {};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
pub fn set_realm_authority(
program_id: &Pubkey,
realm: &Pubkey,
realm_authority: &Pubkey,
new_realm_authority: &Option<Pubkey>,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*realm, false),
AccountMeta::new_readonly(*realm_authority, true),
];
let instruction = GovernanceInstruction::SetRealmAuthority {
new_realm_authority: *new_realm_authority,
};
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}