diff --git a/governance/addin-api/src/max_voter_weight.rs b/governance/addin-api/src/max_voter_weight.rs index 6d34a0b7..5e14fa6b 100644 --- a/governance/addin-api/src/max_voter_weight.rs +++ b/governance/addin-api/src/max_voter_weight.rs @@ -6,7 +6,7 @@ use spl_governance_tools::account::AccountMaxSize; /// MaxVoterWeightRecord account /// The account is used as an api interface to provide max voting power to the governance program from external addin contracts -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct MaxVoterWeightRecord { /// VoterWeightRecord discriminator sha256("account:MaxVoterWeightRecord")[..8] /// Note: The discriminator size must match the addin implementing program discriminator size diff --git a/governance/addin-api/src/voter_weight.rs b/governance/addin-api/src/voter_weight.rs index c5b2549e..ecadafce 100644 --- a/governance/addin-api/src/voter_weight.rs +++ b/governance/addin-api/src/voter_weight.rs @@ -5,7 +5,7 @@ use solana_program::{clock::Slot, program_pack::IsInitialized, pubkey::Pubkey}; use spl_governance_tools::account::AccountMaxSize; /// The governance action VoterWeight is evaluated for -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoterWeightAction { /// Cast vote for a proposal. Target: Proposal CastVote, @@ -26,7 +26,7 @@ pub enum VoterWeightAction { /// VoterWeightRecord account /// The account is used as an api interface to provide voting power to the governance program from external addin contracts -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct VoterWeightRecord { /// VoterWeightRecord discriminator sha256("account:VoterWeightRecord")[..8] /// Note: The discriminator size must match the addin implementing program discriminator size diff --git a/governance/addin-mock/program/src/instruction.rs b/governance/addin-mock/program/src/instruction.rs index fe4d78d2..b31b7a0f 100644 --- a/governance/addin-mock/program/src/instruction.rs +++ b/governance/addin-mock/program/src/instruction.rs @@ -11,7 +11,7 @@ use spl_governance_addin_api::voter_weight::VoterWeightAction; /// Instructions supported by the VoterWeight addin program /// This program is a mock program used by spl-governance for testing and not real addin -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] #[allow(clippy::large_enum_variant)] pub enum VoterWeightAddinInstruction { /// Sets up VoterWeightRecord owned by the program diff --git a/governance/chat/program/src/instruction.rs b/governance/chat/program/src/instruction.rs index 0c798f36..e0391231 100644 --- a/governance/chat/program/src/instruction.rs +++ b/governance/chat/program/src/instruction.rs @@ -11,7 +11,7 @@ use spl_governance::instruction::with_realm_config_accounts; use crate::state::MessageBody; /// Instructions supported by the GovernanceChat program -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] #[allow(clippy::large_enum_variant)] pub enum GovernanceChatInstruction { /// Posts a message with a comment for a Proposal diff --git a/governance/chat/program/src/processor.rs b/governance/chat/program/src/processor.rs index 5ece332e..208d4ea1 100644 --- a/governance/chat/program/src/processor.rs +++ b/governance/chat/program/src/processor.rs @@ -18,7 +18,8 @@ use solana_program::{ }; use spl_governance::state::{ governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance, - realm::get_realm_data, token_owner_record::get_token_owner_record_data_for_realm, + realm::get_realm_data, realm_config::get_realm_config_data_for_realm, + token_owner_record::get_token_owner_record_data_for_realm, }; use spl_governance_addin_api::voter_weight::VoterWeightAction; use spl_governance_tools::account::create_and_serialize_account; @@ -92,14 +93,15 @@ pub fn process_post_message( governance_info.key, )?; - let realm_config_info = next_account_info(account_info_iter)?; //10 + let realm_config_info = next_account_info(account_info_iter)?; // 10 + + let realm_config_data = + get_realm_config_data_for_realm(governance_program_id, realm_config_info, realm_info.key)?; let voter_weight = token_owner_record_data.resolve_voter_weight( - governance_program_id, - realm_config_info, - account_info_iter, // 11 - realm_info.key, + account_info_iter, // voter_weight_record *11 &realm_data, + &realm_config_data, VoterWeightAction::CommentProposal, proposal_info.key, )?; diff --git a/governance/chat/program/src/state.rs b/governance/chat/program/src/state.rs index 9379271b..957465f9 100644 --- a/governance/chat/program/src/state.rs +++ b/governance/chat/program/src/state.rs @@ -8,7 +8,7 @@ use solana_program::{ use spl_governance_tools::account::{assert_is_valid_account_of_type, AccountMaxSize}; /// Defines all GovernanceChat accounts types -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum GovernanceChatAccountType { /// Default uninitialized account state Uninitialized, @@ -18,7 +18,7 @@ pub enum GovernanceChatAccountType { } /// Chat message body -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum MessageBody { /// Text message encoded as utf-8 string Text(String), @@ -29,7 +29,7 @@ pub enum MessageBody { } /// Chat message -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ChatMessage { /// Account type pub account_type: GovernanceChatAccountType, diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index 29b7039e..2e96512b 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -13,7 +13,8 @@ use spl_governance::{ enums::{MintMaxVoteWeightSource, VoteThreshold}, governance::{get_governance_address, GovernanceConfig}, proposal::{get_proposal_address, VoteType}, - realm::get_realm_address, + realm::{get_realm_address, GoverningTokenConfigAccountArgs}, + realm_config::GoverningTokenType, token_owner_record::get_token_owner_record_address, }, }; @@ -108,13 +109,19 @@ impl GovernanceChatProgramTest { let realm_authority = Keypair::new(); + let community_token_config_args = GoverningTokenConfigAccountArgs { + voter_weight_addin: self.voter_weight_addin_id, + max_voter_weight_addin: None, + token_type: GoverningTokenType::default(), + }; + let create_realm_ix = create_realm( &self.governance_program_id, &realm_authority.pubkey(), &governing_token_mint_keypair.pubkey(), &self.bench.payer.pubkey(), None, - self.voter_weight_addin_id, + Some(community_token_config_args), None, name.clone(), 1, diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index 3bbf79b1..fb9ae30e 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -261,23 +261,23 @@ pub enum GovernanceError { /// Governance PDA must sign #[error("Governance PDA must sign")] - GovernancePdaMustSign, + GovernancePdaMustSign, // 561 /// Transaction already flagged with error #[error("Transaction already flagged with error")] - TransactionAlreadyFlaggedWithError, + TransactionAlreadyFlaggedWithError, // 562 /// Invalid Realm for Governance #[error("Invalid Realm for Governance")] - InvalidRealmForGovernance, + InvalidRealmForGovernance, // 563 /// Invalid Authority for Realm #[error("Invalid Authority for Realm")] - InvalidAuthorityForRealm, + InvalidAuthorityForRealm, // 564 /// Realm has no authority #[error("Realm has no authority")] - RealmHasNoAuthority, + RealmHasNoAuthority, // 565 /// Realm authority must sign #[error("Realm authority must sign")] @@ -285,7 +285,7 @@ pub enum GovernanceError { /// Invalid governing token holding account #[error("Invalid governing token holding account")] - InvalidGoverningTokenHoldingAccount, + InvalidGoverningTokenHoldingAccount, // 567 /// Realm council mint change is not supported #[error("Realm council mint change is not supported")] @@ -406,6 +406,34 @@ pub enum GovernanceError { /// Cannot Relinquish in Finalizing state #[error("Cannot Relinquish in Finalizing state")] CannotRelinquishInFinalizingState, + + /// Invalid RealmConfig account address + #[error("Invalid RealmConfig account address")] + InvalidRealmConfigAddress, + + /// Cannot deposit dormant tokens + #[error("Cannot deposit dormant tokens")] + CannotDepositDormantTokens, // 599 + + /// Cannot withdraw membership tokens + #[error("Cannot withdraw membership tokens")] + CannotWithdrawMembershipTokens, // 600 + + /// Cannot revoke GoverningTokens + #[error("Cannot revoke GoverningTokens")] + CannotRevokeGoverningTokens, // 601 + + /// Invalid Revoke amount + #[error("Invalid Revoke amount")] + InvalidRevokeAmount, // 602 + + /// Invalid GoverningToken source + #[error("Invalid GoverningToken source")] + InvalidGoverningTokenSource, // 603 + + /// Cannot change community TokenType to Memebership + #[error("Cannot change community TokenType to Memebership")] + CannotChangeCommunityTokenTypeToMemebership, // 604 } impl PrintProgramError for GovernanceError { diff --git a/governance/program/src/instruction.rs b/governance/program/src/instruction.rs index 72bd717e..eb8beadf 100644 --- a/governance/program/src/instruction.rs +++ b/governance/program/src/instruction.rs @@ -11,8 +11,11 @@ use crate::{ program_metadata::get_program_metadata_address, proposal::{get_proposal_address, VoteType}, proposal_transaction::{get_proposal_transaction_address, InstructionData}, - realm::SetRealmAuthorityAction, - realm::{get_governing_token_holding_address, get_realm_address, RealmConfigArgs}, + realm::{ + get_governing_token_holding_address, get_realm_address, + GoverningTokenConfigAccountArgs, RealmConfigArgs, + }, + realm::{GoverningTokenConfigArgs, SetRealmAuthorityAction}, realm_config::get_realm_config_address, signatory_record::get_signatory_record_address, token_owner_record::get_token_owner_record_address, @@ -29,7 +32,7 @@ use solana_program::{ }; /// Instructions supported by the Governance program -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] #[allow(clippy::large_enum_variant)] pub enum GovernanceInstruction { /// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint @@ -48,9 +51,13 @@ pub enum GovernanceInstruction { /// 9. `[writable]` Council Token Holding account - optional unless council is used. PDA seeds: ['governance',realm,council_mint] /// The account will be created with the Realm PDA as its owner - /// 10. `[]` Optional Community Voter Weight Addin Program Id - /// 11. `[]` Optional Max Community Voter Weight Addin Program Id - /// 12. `[writable]` Optional RealmConfig account. PDA seeds: ['realm-config', realm] + /// 10. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm] + + /// 11. `[]` Optional Community Voter Weight Addin Program Id + /// 12. `[]` Optional Max Community Voter Weight Addin Program Id + /// + /// 13. `[]` Optional Council Voter Weight Addin Program Id + /// 14. `[]` Optional Max Council Voter Weight Addin Program Id CreateRealm { #[allow(dead_code)] /// UTF-8 encoded Governance Realm name @@ -65,16 +72,18 @@ pub enum GovernanceInstruction { /// Note: If subsequent (top up) deposit is made and there are active votes for the Voter then the vote weights won't be updated automatically /// It can be done by relinquishing votes on active Proposals and voting again with the new weight /// - /// 0. `[]` Governance Realm account + /// 0. `[]` Realm account /// 1. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint] - /// 2. `[writable]` Governing Token Source account. All tokens from the account will be transferred to the Holding account + /// 2. `[writable]` Governing Token Source account. It can be either spl-token TokenAccount or MintAccount + /// Tokens will be transferred or minted to the Holding account /// 3. `[signer]` Governing Token Owner account - /// 4. `[signer]` Governing Token Transfer authority - /// 5. `[writable]` Token Owner Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] + /// 4. `[signer]` Governing Token Source account authority + /// It should be owner for TokenAccount and mint_auhtority for MintAccount + /// 5. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] /// 6. `[signer]` Payer /// 7. `[]` System - /// 8. `[]` SPL Token - /// 9. `[]` Sysvar Rent + /// 8. `[]` SPL Token program + /// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] DepositGoverningTokens { /// The amount to deposit into the realm #[allow(dead_code)] @@ -85,12 +94,13 @@ pub enum GovernanceInstruction { /// Note: It's only possible to withdraw tokens if the Voter doesn't have any outstanding active votes /// If there are any outstanding votes then they must be relinquished before tokens could be withdrawn /// - /// 0. `[]` Governance Realm account + /// 0. `[]` Realm account /// 1. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint] /// 2. `[writable]` Governing Token Destination account. All tokens will be transferred to this account /// 3. `[signer]` Governing Token Owner account - /// 4. `[writable]` Token Owner Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] - /// 5. `[]` SPL Token + /// 4. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] + /// 5. `[]` SPL Token program + /// 6. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] WithdrawGoverningTokens {}, /// Sets Governance Delegate for the given Realm and Governing Token Mint (Community or Council) @@ -117,7 +127,7 @@ pub enum GovernanceInstruction { /// 5. `[]` System program /// 6. `[]` Sysvar Rent /// 7. `[signer]` Governance authority - /// 8. `[]` Realm Config + /// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 9. `[]` Optional Voter Weight Record CreateGovernance { /// Governance config @@ -138,7 +148,7 @@ pub enum GovernanceInstruction { /// 8. `[]` System program /// 9. `[]` Sysvar Rent /// 10. `[signer]` Governance authority - /// 11. `[]` Realm Config + /// 11. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 12. `[]` Optional Voter Weight Record CreateProgramGovernance { /// Governance config @@ -162,7 +172,7 @@ pub enum GovernanceInstruction { /// 5. `[signer]` Governance Authority (Token Owner or Governance Delegate) /// 6. `[signer]` Payer /// 7. `[]` System program - /// 8. `[]` Realm Config + /// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 9. `[]` Optional Voter Weight Record CreateProposal { #[allow(dead_code)] @@ -295,7 +305,7 @@ pub enum GovernanceInstruction { /// Note: In the current version only Council veto is supported /// 8. `[signer]` Payer /// 9. `[]` System program - /// 10. `[]` Realm Config + /// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 11. `[]` Optional Voter Weight Record /// 12. `[]` Optional Max Voter Weight Record CastVote { @@ -311,7 +321,7 @@ pub enum GovernanceInstruction { /// 2. `[writable]` Proposal account /// 3. `[writable]` TokenOwnerRecord of the Proposal owner /// 4. `[]` Governing Token Mint - /// 5. `[]` Realm Config + /// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 6. `[]` Optional Max Voter Weight Record FinalizeVote {}, @@ -355,7 +365,7 @@ pub enum GovernanceInstruction { /// 7. `[]` System program /// 8. `[]` Sysvar Rent /// 8. `[signer]` Governance authority - /// 9. `[]` Realm Config + /// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 10. `[]` Optional Voter Weight Record CreateMintGovernance { #[allow(dead_code)] @@ -381,7 +391,7 @@ pub enum GovernanceInstruction { /// 7. `[]` System program /// 8. `[]` Sysvar Rent /// 9. `[signer]` Governance authority - /// 10. `[]` Realm Config + /// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 11. `[]` Optional Voter Weight Record CreateTokenGovernance { #[allow(dead_code)] @@ -438,10 +448,14 @@ pub enum GovernanceInstruction { /// The account will be created with the Realm PDA as its owner /// 4. `[]` System /// 5. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm] - + /// /// 6. `[]` Optional Community Voter Weight Addin Program Id /// 7. `[]` Optional Max Community Voter Weight Addin Program Id - /// 8. `[signer]` Optional Payer + /// + /// 8. `[]` Optional Council Voter Weight Addin Program Id + /// 9. `[]` Optional Max Council Voter Weight Addin Program Id + /// + /// 10. `[signer]` Optional Payer. Required if RealmConfig doesn't exist and needs to be created SetRealmConfig { #[allow(dead_code)] /// Realm config args @@ -475,6 +489,22 @@ pub enum GovernanceInstruction { /// 2. `[signer]` Payer /// 3. `[]` System CreateNativeTreasury, + + /// Revokes (burns) membership governing tokens for the given TokenOwnerRecord and hence takes away governance power from the TokenOwner + /// Note: If there are active votes for the TokenOwner then the vote weights won't be updated automatically + /// + /// 0. `[]` Realm account + /// 1. `[signer]` Realm authority + /// 2. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint] + /// 3. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] + /// 4. `[writable]` GoverningTokenMint + /// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm] + /// 6. `[]` SPL Token program + RevokeGoverningTokens { + /// The amount to revoke + #[allow(dead_code)] + amount: u64, + }, } /// Creates CreateRealm instruction @@ -486,8 +516,9 @@ pub fn create_realm( community_token_mint: &Pubkey, payer: &Pubkey, council_token_mint: Option, - community_voter_weight_addin: Option, - max_community_voter_weight_addin: Option, + // Accounts Args + community_token_config_args: Option, + council_token_config_args: Option, // Args name: String, min_community_weight_to_create_governance: u64, @@ -519,40 +550,22 @@ pub fn create_realm( false }; - let use_community_voter_weight_addin = - if let Some(community_voter_weight_addin) = community_voter_weight_addin { - accounts.push(AccountMeta::new_readonly( - community_voter_weight_addin, - false, - )); - true - } else { - false - }; + let realm_config_address = get_realm_config_address(program_id, &realm_address); + accounts.push(AccountMeta::new(realm_config_address, false)); - let use_max_community_voter_weight_addin = - if let Some(max_community_voter_weight_addin) = max_community_voter_weight_addin { - accounts.push(AccountMeta::new_readonly( - max_community_voter_weight_addin, - false, - )); - true - } else { - false - }; + let community_token_config_args = + with_governing_token_config_args(&mut accounts, community_token_config_args); - if use_community_voter_weight_addin || use_max_community_voter_weight_addin { - let realm_config_address = get_realm_config_address(program_id, &realm_address); - accounts.push(AccountMeta::new(realm_config_address, false)); - } + let council_token_config_args = + with_governing_token_config_args(&mut accounts, council_token_config_args); let instruction = GovernanceInstruction::CreateRealm { config_args: RealmConfigArgs { use_council_mint, min_community_weight_to_create_governance, community_mint_max_vote_weight_source, - use_community_voter_weight_addin, - use_max_community_voter_weight_addin, + community_token_config_args, + council_token_config_args, }, name, }; @@ -572,7 +585,7 @@ pub fn deposit_governing_tokens( realm: &Pubkey, governing_token_source: &Pubkey, governing_token_owner: &Pubkey, - governing_token_transfer_authority: &Pubkey, + governing_token_source_authority: &Pubkey, payer: &Pubkey, // Args amount: u64, @@ -588,16 +601,19 @@ pub fn deposit_governing_tokens( let governing_token_holding_address = get_governing_token_holding_address(program_id, realm, governing_token_mint); + let realm_config_address = get_realm_config_address(program_id, realm); + 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_readonly(*governing_token_source_authority, true), AccountMeta::new(token_owner_record_address, false), AccountMeta::new(*payer, true), AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(realm_config_address, false), ]; let instruction = GovernanceInstruction::DepositGoverningTokens { amount }; @@ -629,6 +645,8 @@ pub fn withdraw_governing_tokens( let governing_token_holding_address = get_governing_token_holding_address(program_id, realm, governing_token_mint); + let realm_config_address = get_realm_config_address(program_id, realm); + let accounts = vec![ AccountMeta::new_readonly(*realm, false), AccountMeta::new(governing_token_holding_address, false), @@ -636,6 +654,7 @@ pub fn withdraw_governing_tokens( AccountMeta::new_readonly(*governing_token_owner, true), AccountMeta::new(token_owner_record_address, false), AccountMeta::new_readonly(spl_token::id(), false), + AccountMeta::new_readonly(realm_config_address, false), ]; let instruction = GovernanceInstruction::WithdrawGoverningTokens {}; @@ -1355,8 +1374,9 @@ pub fn set_realm_config( realm_authority: &Pubkey, council_token_mint: Option, payer: &Pubkey, - community_voter_weight_addin: Option, - max_community_voter_weight_addin: Option, + // Accounts Args + community_token_config_args: Option, + council_token_config_args: Option, // Args min_community_weight_to_create_governance: u64, community_mint_max_vote_weight_source: MintMaxVoteWeightSource, @@ -1384,39 +1404,21 @@ pub fn set_realm_config( let realm_config_address = get_realm_config_address(program_id, realm); accounts.push(AccountMeta::new(realm_config_address, false)); - let use_community_voter_weight_addin = - if let Some(community_voter_weight_addin) = community_voter_weight_addin { - accounts.push(AccountMeta::new_readonly( - community_voter_weight_addin, - false, - )); - true - } else { - false - }; + let community_token_config_args = + with_governing_token_config_args(&mut accounts, community_token_config_args); - let use_max_community_voter_weight_addin = - if let Some(max_community_voter_weight_addin) = max_community_voter_weight_addin { - accounts.push(AccountMeta::new_readonly( - max_community_voter_weight_addin, - false, - )); - true - } else { - false - }; + let council_token_config_args = + with_governing_token_config_args(&mut accounts, council_token_config_args); - if use_community_voter_weight_addin || use_max_community_voter_weight_addin { - accounts.push(AccountMeta::new(*payer, true)); - } + accounts.push(AccountMeta::new(*payer, true)); let instruction = GovernanceInstruction::SetRealmConfig { config_args: RealmConfigArgs { use_council_mint, min_community_weight_to_create_governance, community_mint_max_vote_weight_source, - use_community_voter_weight_addin, - use_max_community_voter_weight_addin, + community_token_config_args, + council_token_config_args, }, }; @@ -1536,3 +1538,77 @@ pub fn create_native_treasury( data: instruction.try_to_vec().unwrap(), } } + +/// Creates RevokeGoverningTokens instruction +#[allow(clippy::too_many_arguments)] +pub fn revoke_governing_tokens( + program_id: &Pubkey, + // Accounts + realm: &Pubkey, + realm_authority: &Pubkey, + governing_token_owner: &Pubkey, + governing_token_mint: &Pubkey, + // Args + amount: u64, +) -> 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 realm_config_address = get_realm_config_address(program_id, realm); + + let accounts = vec![ + AccountMeta::new_readonly(*realm, false), + AccountMeta::new_readonly(*realm_authority, true), + AccountMeta::new(governing_token_holding_address, false), + AccountMeta::new(token_owner_record_address, false), + AccountMeta::new(*governing_token_mint, false), + AccountMeta::new_readonly(realm_config_address, false), + AccountMeta::new_readonly(spl_token::id(), false), + ]; + + let instruction = GovernanceInstruction::RevokeGoverningTokens { amount }; + + Instruction { + program_id: *program_id, + accounts, + data: instruction.try_to_vec().unwrap(), + } +} + +/// Adds accounts specified by GoverningTokenConfigAccountArgs +/// and returns GoverningTokenConfigArgs +pub fn with_governing_token_config_args( + accounts: &mut Vec, + governing_token_config_args: Option, +) -> GoverningTokenConfigArgs { + let governing_token_config_args = governing_token_config_args.unwrap_or_default(); + + let use_voter_weight_addin = + if let Some(voter_weight_addin) = governing_token_config_args.voter_weight_addin { + accounts.push(AccountMeta::new_readonly(voter_weight_addin, false)); + true + } else { + false + }; + + let use_max_voter_weight_addin = + if let Some(max_voter_weight_addin) = governing_token_config_args.max_voter_weight_addin { + accounts.push(AccountMeta::new_readonly(max_voter_weight_addin, false)); + true + } else { + false + }; + + GoverningTokenConfigArgs { + use_voter_weight_addin, + use_max_voter_weight_addin, + token_type: governing_token_config_args.token_type, + } +} diff --git a/governance/program/src/processor/mod.rs b/governance/program/src/processor/mod.rs index e8011875..a4baf1f9 100644 --- a/governance/program/src/processor/mod.rs +++ b/governance/program/src/processor/mod.rs @@ -19,6 +19,7 @@ mod process_insert_transaction; mod process_relinquish_vote; mod process_remove_signatory; mod process_remove_transaction; +mod process_revoke_governing_tokens; mod process_set_governance_config; mod process_set_governance_delegate; mod process_set_realm_authority; @@ -48,6 +49,7 @@ use process_insert_transaction::*; use process_relinquish_vote::*; use process_remove_signatory::*; use process_remove_transaction::*; +use process_revoke_governing_tokens::*; use process_set_governance_config::*; use process_set_governance_delegate::*; use process_set_realm_authority::*; @@ -213,5 +215,9 @@ pub fn process_instruction( GovernanceInstruction::CreateNativeTreasury {} => { process_create_native_treasury(program_id, accounts) } + + GovernanceInstruction::RevokeGoverningTokens { amount } => { + process_revoke_governing_tokens(program_id, accounts, amount) + } } } diff --git a/governance/program/src/processor/process_cast_vote.rs b/governance/program/src/processor/process_cast_vote.rs index aec2e1e8..eedfd49a 100644 --- a/governance/program/src/processor/process_cast_vote.rs +++ b/governance/program/src/processor/process_cast_vote.rs @@ -18,6 +18,7 @@ use crate::{ governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance_and_governing_mint, 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_proposal_owner, get_token_owner_record_data_for_realm_and_governing_mint, @@ -104,17 +105,14 @@ pub fn process_cast_vote( .checked_add(1) .unwrap(); - // Note: When both voter_weight and max_voter_weight addins are used the realm_config will be deserialized twice in resolve_voter_weight() and resolve_max_voter_weight() - // It can't be deserialized eagerly because some realms won't have the config if they don't use any of the advanced options - // This extra deserialisation should be acceptable to keep things simple and encapsulated. - let realm_config_info = next_account_info(account_info_iter)?; //9 + let realm_config_info = next_account_info(account_info_iter)?; // 9 + let realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; let voter_weight = voter_token_owner_record_data.resolve_voter_weight( - program_id, - realm_config_info, - account_info_iter, // voter_weight_record 10 - realm_info.key, + account_info_iter, // voter_weight_record *10 &realm_data, + &realm_config_data, VoterWeightAction::CastVote, proposal_info.key, )?; @@ -152,12 +150,11 @@ pub fn process_cast_vote( } let max_voter_weight = proposal_data.resolve_max_voter_weight( - program_id, - realm_config_info, - vote_governing_token_mint_info, account_info_iter, // max_voter_weight_record 11 realm_info.key, &realm_data, + &realm_config_data, + vote_governing_token_mint_info, &vote_kind, )?; diff --git a/governance/program/src/processor/process_create_proposal.rs b/governance/program/src/processor/process_create_proposal.rs index f135fba0..325b5286 100644 --- a/governance/program/src/processor/process_create_proposal.rs +++ b/governance/program/src/processor/process_create_proposal.rs @@ -21,6 +21,7 @@ use crate::{ ProposalOption, ProposalV2, VoteType, }, 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, }, @@ -82,13 +83,13 @@ pub fn process_create_proposal( .assert_token_owner_or_delegate_is_signer(governance_authority_info)?; let realm_config_info = next_account_info(account_info_iter)?; // 10 + 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( - program_id, - realm_config_info, - account_info_iter, - realm_info.key, + account_info_iter, // voter_weight_record *11 &realm_data, + &realm_config_data, VoterWeightAction::CreateProposal, governance_info.key, )?; diff --git a/governance/program/src/processor/process_create_realm.rs b/governance/program/src/processor/process_create_realm.rs index 03d212b9..93ccb037 100644 --- a/governance/program/src/processor/process_create_realm.rs +++ b/governance/program/src/processor/process_create_realm.rs @@ -17,7 +17,10 @@ use crate::{ assert_valid_realm_config_args, get_governing_token_holding_address_seeds, get_realm_address_seeds, RealmConfig, RealmConfigArgs, RealmV2, }, - realm_config::{get_realm_config_address_seeds, RealmConfigAccount}, + realm_config::{ + get_realm_config_address_seeds, resolve_governing_token_config, RealmConfigAccount, + Reserved110, + }, }, tools::spl_token::create_spl_token_account_signed, }; @@ -27,7 +30,7 @@ pub fn process_create_realm( program_id: &Pubkey, accounts: &[AccountInfo], name: String, - config_args: RealmConfigArgs, + realm_config_args: RealmConfigArgs, ) -> ProgramResult { let account_info_iter = &mut accounts.iter(); @@ -46,8 +49,9 @@ pub fn process_create_realm( return Err(GovernanceError::RealmAlreadyExists.into()); } - assert_valid_realm_config_args(&config_args)?; + assert_valid_realm_config_args(&realm_config_args)?; + // Create Community token holding account create_spl_token_account_signed( payer_info, governance_token_holding_info, @@ -61,7 +65,8 @@ pub fn process_create_realm( rent, )?; - let council_token_mint_address = if config_args.use_council_mint { + // Create Council token holding account + let council_token_mint_address = if realm_config_args.use_council_mint { let council_token_mint_info = next_account_info(account_info_iter)?; // 8 let council_token_holding_info = next_account_info(account_info_iter)?; // 9 @@ -83,48 +88,40 @@ pub fn process_create_realm( None }; - // Setup config for addins + // Create and serialzie RealmConfig + let realm_config_info = next_account_info(account_info_iter)?; // 10 - let community_voter_weight_addin = if config_args.use_community_voter_weight_addin { - let community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 10 - Some(*community_voter_weight_addin_info.key) - } else { - None + // 11, 12 + let community_token_config = resolve_governing_token_config( + account_info_iter, + &realm_config_args.community_token_config_args, + )?; + + // 13, 14 + let council_token_config = resolve_governing_token_config( + account_info_iter, + &realm_config_args.council_token_config_args, + )?; + + let realm_config_data = RealmConfigAccount { + account_type: GovernanceAccountType::RealmConfig, + realm: *realm_info.key, + community_token_config, + council_token_config, + reserved: Reserved110::default(), }; - let max_community_voter_weight_addin = if config_args.use_max_community_voter_weight_addin { - let max_community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 11 - Some(*max_community_voter_weight_addin_info.key) - } else { - None - }; - - if config_args.use_community_voter_weight_addin - || config_args.use_max_community_voter_weight_addin - { - let realm_config_info = next_account_info(account_info_iter)?; // 12 - - let realm_config_data = RealmConfigAccount { - account_type: GovernanceAccountType::RealmConfig, - realm: *realm_info.key, - community_voter_weight_addin, - max_community_voter_weight_addin, - council_voter_weight_addin: None, - council_max_vote_weight_addin: None, - reserved: [0; 128], - }; - - create_and_serialize_account_signed::( - payer_info, - realm_config_info, - &realm_config_data, - &get_realm_config_address_seeds(realm_info.key), - program_id, - system_info, - rent, - )?; - } + create_and_serialize_account_signed::( + payer_info, + realm_config_info, + &realm_config_data, + &get_realm_config_address_seeds(realm_info.key), + program_id, + system_info, + rent, + )?; + // Create and serialize Realm let realm_data = RealmV2 { account_type: GovernanceAccountType::RealmV2, community_mint: *governance_token_mint_info.key, @@ -135,12 +132,12 @@ pub fn process_create_realm( config: RealmConfig { council_mint: council_token_mint_address, reserved: [0; 6], - community_mint_max_vote_weight_source: config_args + community_mint_max_vote_weight_source: realm_config_args .community_mint_max_vote_weight_source, - min_community_weight_to_create_governance: config_args + min_community_weight_to_create_governance: realm_config_args .min_community_weight_to_create_governance, - use_community_voter_weight_addin: config_args.use_community_voter_weight_addin, - use_max_community_voter_weight_addin: config_args.use_max_community_voter_weight_addin, + legacy1: 0, + legacy2: 0, }, voting_proposal_count: 0, reserved_v2: [0; 128], diff --git a/governance/program/src/processor/process_deposit_governing_tokens.rs b/governance/program/src/processor/process_deposit_governing_tokens.rs index 4d428f07..436ec1ac 100644 --- a/governance/program/src/processor/process_deposit_governing_tokens.rs +++ b/governance/program/src/processor/process_deposit_governing_tokens.rs @@ -14,12 +14,16 @@ use crate::{ state::{ enums::GovernanceAccountType, realm::get_realm_data, + realm_config::get_realm_config_data_for_realm, token_owner_record::{ get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds, TokenOwnerRecordV2, }, }, - tools::spl_token::{get_spl_token_mint, get_spl_token_owner, transfer_spl_tokens}, + tools::spl_token::{ + get_spl_token_mint, is_spl_token_account, is_spl_token_mint, mint_spl_tokens_to, + transfer_spl_tokens, + }, }; /// Processes DepositGoverningTokens instruction @@ -34,11 +38,12 @@ pub fn process_deposit_governing_tokens( let governing_token_holding_info = next_account_info(account_info_iter)?; // 1 let governing_token_source_info = next_account_info(account_info_iter)?; // 2 let governing_token_owner_info = next_account_info(account_info_iter)?; // 3 - let governing_token_transfer_authority_info = next_account_info(account_info_iter)?; // 4 + let governing_token_source_authority_info = next_account_info(account_info_iter)?; // 4 let token_owner_record_info = next_account_info(account_info_iter)?; // 5 let payer_info = next_account_info(account_info_iter)?; // 6 let system_info = next_account_info(account_info_iter)?; // 7 let spl_token_info = next_account_info(account_info_iter)?; // 8 + let realm_config_info = next_account_info(account_info_iter)?; // 9 let rent = Rent::get()?; @@ -52,13 +57,32 @@ pub fn process_deposit_governing_tokens( governing_token_holding_info.key, )?; - transfer_spl_tokens( - governing_token_source_info, - governing_token_holding_info, - governing_token_transfer_authority_info, - amount, - spl_token_info, - )?; + let realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; + + realm_config_data.assert_can_deposit_governing_token(&realm_data, &governing_token_mint)?; + + if is_spl_token_account(governing_token_source_info) { + // If the source is spl-token token account then transfer tokens from it + transfer_spl_tokens( + governing_token_source_info, + governing_token_holding_info, + governing_token_source_authority_info, + amount, + spl_token_info, + )?; + } else if is_spl_token_mint(governing_token_source_info) { + // If it's a mint then mint the tokens + mint_spl_tokens_to( + governing_token_source_info, + governing_token_holding_info, + governing_token_source_authority_info, + amount, + spl_token_info, + )?; + } else { + return Err(GovernanceError::InvalidGoverningTokenSource.into()); + } let token_owner_record_address_seeds = get_token_owner_record_address_seeds( realm_info.key, @@ -68,11 +92,7 @@ pub fn process_deposit_governing_tokens( if token_owner_record_info.data_is_empty() { // Deposited tokens can only be withdrawn by the owner so let's make sure the owner signed the transaction - let governing_token_owner = get_spl_token_owner(governing_token_source_info)?; - - if !(governing_token_owner == *governing_token_owner_info.key - && governing_token_owner_info.is_signer) - { + if !governing_token_owner_info.is_signer { return Err(GovernanceError::GoverningTokenOwnerMustSign.into()); } diff --git a/governance/program/src/processor/process_finalize_vote.rs b/governance/program/src/processor/process_finalize_vote.rs index 0075654c..5d79130f 100644 --- a/governance/program/src/processor/process_finalize_vote.rs +++ b/governance/program/src/processor/process_finalize_vote.rs @@ -11,7 +11,7 @@ use solana_program::{ use crate::state::{ governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance_and_governing_mint, - realm::get_realm_data_for_governing_token_mint, + 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_proposal_owner, vote_record::VoteKind, }; @@ -43,15 +43,16 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P governing_token_mint_info.key, )?; - let realm_config_info = next_account_info(account_info_iter)?; // 5 + let realm_config_info = next_account_info(account_info_iter)?; //5 + let realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; let max_voter_weight = proposal_data.resolve_max_voter_weight( - program_id, - realm_config_info, - governing_token_mint_info, account_info_iter, // *6 realm_info.key, &realm_data, + &realm_config_data, + governing_token_mint_info, &VoteKind::Electorate, )?; diff --git a/governance/program/src/processor/process_revoke_governing_tokens.rs b/governance/program/src/processor/process_revoke_governing_tokens.rs new file mode 100644 index 00000000..de517c5f --- /dev/null +++ b/governance/program/src/processor/process_revoke_governing_tokens.rs @@ -0,0 +1,82 @@ +//! Program state processor + +use solana_program::{ + account_info::{next_account_info, AccountInfo}, + entrypoint::ProgramResult, + pubkey::Pubkey, +}; + +use crate::{ + error::GovernanceError, + state::{ + realm::{get_realm_address_seeds, get_realm_data_for_authority}, + realm_config::get_realm_config_data_for_realm, + token_owner_record::get_token_owner_record_data_for_realm_and_governing_mint, + }, + tools::spl_token::burn_spl_tokens_signed, +}; + +/// Processes RevokeGoverningTokens instruction +pub fn process_revoke_governing_tokens( + program_id: &Pubkey, + accounts: &[AccountInfo], + amount: u64, +) -> ProgramResult { + let account_info_iter = &mut accounts.iter(); + + let realm_info = next_account_info(account_info_iter)?; // 0 + let realm_authority_info = next_account_info(account_info_iter)?; // 1 + + let governing_token_holding_info = next_account_info(account_info_iter)?; // 2 + let token_owner_record_info = next_account_info(account_info_iter)?; // 3 + let governing_token_mint_info = next_account_info(account_info_iter)?; // 4 + let realm_config_info = next_account_info(account_info_iter)?; // 5 + + let spl_token_info = next_account_info(account_info_iter)?; // 6 + + let realm_data = + get_realm_data_for_authority(program_id, realm_info, realm_authority_info.key)?; + + if !realm_authority_info.is_signer { + return Err(GovernanceError::RealmAuthorityMustSign.into()); + } + + realm_data.assert_is_valid_governing_token_mint_and_holding( + program_id, + realm_info.key, + governing_token_mint_info.key, + governing_token_holding_info.key, + )?; + + let realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; + + realm_config_data + .assert_can_revoke_governing_token(&realm_data, governing_token_mint_info.key)?; + + let mut token_owner_record_data = get_token_owner_record_data_for_realm_and_governing_mint( + program_id, + token_owner_record_info, + realm_info.key, + governing_token_mint_info.key, + )?; + + token_owner_record_data.governing_token_deposit_amount = token_owner_record_data + .governing_token_deposit_amount + .checked_sub(amount) + .ok_or(GovernanceError::InvalidRevokeAmount)?; + + token_owner_record_data.serialize(&mut *token_owner_record_info.data.borrow_mut())?; + + burn_spl_tokens_signed( + governing_token_holding_info, + governing_token_mint_info, + realm_info, + &get_realm_address_seeds(&realm_data.name), + program_id, + amount, + spl_token_info, + )?; + + Ok(()) +} diff --git a/governance/program/src/processor/process_set_realm_config.rs b/governance/program/src/processor/process_set_realm_config.rs index 3706f8e0..cdd899fd 100644 --- a/governance/program/src/processor/process_set_realm_config.rs +++ b/governance/program/src/processor/process_set_realm_config.rs @@ -13,10 +13,10 @@ use spl_governance_tools::account::create_and_serialize_account_signed; use crate::{ error::GovernanceError, state::{ - enums::GovernanceAccountType, realm::{assert_valid_realm_config_args, get_realm_data_for_authority, RealmConfigArgs}, realm_config::{ - get_realm_config_address_seeds, get_realm_config_data_for_realm, RealmConfigAccount, + get_realm_config_address_seeds, get_realm_config_data_for_realm, + resolve_governing_token_config, RealmConfigAccount, }, }, }; @@ -75,87 +75,60 @@ pub fn process_set_realm_config( } let system_info = next_account_info(account_info_iter)?; // 4 + let realm_config_info = next_account_info(account_info_iter)?; // 5 + let mut realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; - // Setup config for addins + realm_config_data.assert_can_change_config(&realm_config_args)?; - let community_voter_weight_addin = if realm_config_args.use_community_voter_weight_addin { - let community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 6 - Some(*community_voter_weight_addin_info.key) - } else { - None - }; + // Setup configs for tokens (plugins and token types) - let max_community_voter_weight_addin = if realm_config_args.use_max_community_voter_weight_addin - { - let max_community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 7 - Some(*max_community_voter_weight_addin_info.key) - } else { - None - }; + // 6, 7 + let community_token_config = resolve_governing_token_config( + account_info_iter, + &realm_config_args.community_token_config_args, + )?; + + // 8, 9 + let council_token_config = resolve_governing_token_config( + account_info_iter, + &realm_config_args.council_token_config_args, + )?; + + realm_config_data.community_token_config = community_token_config; + realm_config_data.council_token_config = council_token_config; + + // Update or create RealmConfigAccount + if realm_config_info.data_is_empty() { + // For older Realms (pre v3) RealmConfigAccount might not exist yet and we have to create it - // If any of the addins is needed then update or create (if doesn't exist yet) RealmConfigAccount - let update_realm_config = if realm_config_args.use_community_voter_weight_addin - || realm_config_args.use_max_community_voter_weight_addin - { // We need the payer to pay for the new account if it's created - let payer_info = next_account_info(account_info_iter)?; // 8 + let payer_info = next_account_info(account_info_iter)?; // 10 + let rent = Rent::get()?; - // If RealmConfigAccount doesn't exist yet then create it - if realm_config_info.data_is_empty() { - let realm_config_data = RealmConfigAccount { - account_type: GovernanceAccountType::RealmConfig, - realm: *realm_info.key, - community_voter_weight_addin, - max_community_voter_weight_addin, - council_voter_weight_addin: None, - council_max_vote_weight_addin: None, - reserved: [0; 128], - }; - - let rent = Rent::get()?; - - create_and_serialize_account_signed::( - payer_info, - realm_config_info, - &realm_config_data, - &get_realm_config_address_seeds(realm_info.key), - program_id, - system_info, - &rent, - )?; - false // RealmConfigAccount didn't exist and was created - } else { - true // RealmConfigAccount existed before and needs to be updated - } + create_and_serialize_account_signed::( + payer_info, + realm_config_info, + &realm_config_data, + &get_realm_config_address_seeds(realm_info.key), + program_id, + system_info, + &rent, + )?; } else { - // True: If RealmConfigAccount existed before we have to update it to remove the addins which are not used any longer - // False: We don't want to setup the addins and RealmConfigAccount didn't exist before - realm_data.config.use_community_voter_weight_addin - || realm_data.config.use_max_community_voter_weight_addin - }; - - if update_realm_config { - let mut realm_config_data = - get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; - - realm_config_data.community_voter_weight_addin = community_voter_weight_addin; - realm_config_data.max_community_voter_weight_addin = max_community_voter_weight_addin; - realm_config_data.serialize(&mut *realm_config_info.data.borrow_mut())?; } + // Update RealmConfig (Realm.config field) realm_data.config.community_mint_max_vote_weight_source = realm_config_args.community_mint_max_vote_weight_source; realm_data.config.min_community_weight_to_create_governance = realm_config_args.min_community_weight_to_create_governance; - realm_data.config.use_community_voter_weight_addin = - realm_config_args.use_community_voter_weight_addin; - - realm_data.config.use_max_community_voter_weight_addin = - realm_config_args.use_max_community_voter_weight_addin; + realm_data.config.legacy1 = 0; + realm_data.config.legacy2 = 0; realm_data.serialize(&mut *realm_info.data.borrow_mut())?; diff --git a/governance/program/src/processor/process_withdraw_governing_tokens.rs b/governance/program/src/processor/process_withdraw_governing_tokens.rs index fca2d792..202978a8 100644 --- a/governance/program/src/processor/process_withdraw_governing_tokens.rs +++ b/governance/program/src/processor/process_withdraw_governing_tokens.rs @@ -10,6 +10,7 @@ use crate::{ error::GovernanceError, state::{ realm::{get_realm_address_seeds, get_realm_data}, + realm_config::get_realm_config_data_for_realm, token_owner_record::{ get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds, }, @@ -30,6 +31,7 @@ pub fn process_withdraw_governing_tokens( let governing_token_owner_info = next_account_info(account_info_iter)?; // 3 let token_owner_record_info = next_account_info(account_info_iter)?; // 4 let spl_token_info = next_account_info(account_info_iter)?; // 5 + let realm_config_info = next_account_info(account_info_iter)?; // 6 if !governing_token_owner_info.is_signer { return Err(GovernanceError::GoverningTokenOwnerMustSign.into()); @@ -45,6 +47,11 @@ pub fn process_withdraw_governing_tokens( governing_token_holding_info.key, )?; + let realm_config_data = + get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?; + + realm_config_data.assert_can_withdraw_governing_token(&realm_data, &governing_token_mint)?; + let token_owner_record_address_seeds = get_token_owner_record_address_seeds( realm_info.key, &governing_token_mint, diff --git a/governance/program/src/state/enums.rs b/governance/program/src/state/enums.rs index e0811a16..85a0d7b2 100644 --- a/governance/program/src/state/enums.rs +++ b/governance/program/src/state/enums.rs @@ -3,7 +3,7 @@ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; /// Defines all Governance accounts types -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum GovernanceAccountType { /// Default uninitialized account state Uninitialized, @@ -96,7 +96,7 @@ impl Default for GovernanceAccountType { } /// What state a Proposal is in -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum ProposalState { /// Draft - Proposal enters Draft state when it's created Draft, @@ -141,7 +141,7 @@ impl Default for ProposalState { /// The type of the vote threshold used to resolve a vote on a Proposal /// /// Note: In the current version only YesVotePercentage and Disabled thresholds are supported -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoteThreshold { /// Voting threshold of Yes votes in % required to tip the vote (Approval Quorum) /// It's the percentage of tokens out of the entire pool of governance tokens eligible to vote @@ -173,7 +173,7 @@ pub enum VoteThreshold { /// The type of vote tipping to use on a Proposal. /// /// Vote tipping means that under some conditions voting will complete early. -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoteTipping { /// Tip when there is no way for another option to win and the vote threshold /// has been reached. This ignores voters withdrawing their votes. @@ -192,7 +192,7 @@ pub enum VoteTipping { } /// The status of instruction execution -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum TransactionExecutionStatus { /// Transaction was not executed yet None, @@ -205,7 +205,7 @@ pub enum TransactionExecutionStatus { } /// Transaction execution flags defining how instructions are executed for a Proposal -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum InstructionExecutionFlags { /// No execution flags are specified /// Instructions can be executed individually, in any order, as soon as they hold_up time expires @@ -224,7 +224,7 @@ pub enum InstructionExecutionFlags { /// The source of max vote weight used for voting /// Values below 100% mint supply can be used when the governing token is fully minted but not distributed yet -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum MintMaxVoteWeightSource { /// Fraction (10^10 precision) of the governing mint supply is used as max vote weight /// The default is 100% (10^10) to use all available mint supply for voting diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 6a7af6be..693f19f7 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -21,7 +21,7 @@ use spl_governance_tools::{ }; /// Governance config -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct GovernanceConfig { /// The type of the vote threshold used for community vote /// Note: In the current version only YesVotePercentage and Disabled thresholds are supported @@ -56,7 +56,7 @@ pub struct GovernanceConfig { } /// Governance Account -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct GovernanceV2 { /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance pub account_type: GovernanceAccountType, diff --git a/governance/program/src/state/legacy.rs b/governance/program/src/state/legacy.rs index bff860a9..19f87d6c 100644 --- a/governance/program/src/state/legacy.rs +++ b/governance/program/src/state/legacy.rs @@ -18,7 +18,7 @@ use solana_program::{ /// Governance Realm Account /// Account PDA seeds" ['governance', name] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct RealmV1 { /// Governance account type pub account_type: GovernanceAccountType, @@ -53,7 +53,7 @@ impl IsInitialized for RealmV1 { /// Governance Token Owner Record /// Account PDA seeds: ['governance', realm, token_mint, token_owner ] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct TokenOwnerRecordV1 { /// Governance account type pub account_type: GovernanceAccountType, @@ -101,7 +101,7 @@ impl IsInitialized for TokenOwnerRecordV1 { } /// Governance Account -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct GovernanceV1 { /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance pub account_type: GovernanceAccountType, @@ -171,7 +171,7 @@ impl IsInitialized for GovernanceV1 { } /// Governance Proposal -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalV1 { /// Governance account type pub account_type: GovernanceAccountType, @@ -260,7 +260,7 @@ impl IsInitialized for ProposalV1 { } /// Account PDA seeds: ['governance', proposal, signatory] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct SignatoryRecordV1 { /// Governance account type pub account_type: GovernanceAccountType, @@ -282,7 +282,7 @@ impl IsInitialized for SignatoryRecordV1 { } /// Proposal instruction V1 -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalInstructionV1 { /// Governance Account type pub account_type: GovernanceAccountType, @@ -315,7 +315,7 @@ impl IsInitialized for ProposalInstructionV1 { } /// Vote with number of votes -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoteWeightV1 { /// Yes vote Yes(u64), @@ -325,7 +325,7 @@ pub enum VoteWeightV1 { } /// Proposal VoteRecord -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct VoteRecordV1 { /// Governance account type pub account_type: GovernanceAccountType, diff --git a/governance/program/src/state/native_treasury.rs b/governance/program/src/state/native_treasury.rs index ec313d76..88729554 100644 --- a/governance/program/src/state/native_treasury.rs +++ b/governance/program/src/state/native_treasury.rs @@ -6,7 +6,7 @@ use spl_governance_tools::account::AccountMaxSize; /// Treasury account /// The account has no data and can be used as a payer for instruction signed by Governance PDAs or as a native SOL treasury -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct NativeTreasury {} impl AccountMaxSize for NativeTreasury { diff --git a/governance/program/src/state/program_metadata.rs b/governance/program/src/state/program_metadata.rs index 1c2fbf0e..786b0010 100644 --- a/governance/program/src/state/program_metadata.rs +++ b/governance/program/src/state/program_metadata.rs @@ -10,7 +10,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize}; use crate::state::enums::GovernanceAccountType; /// Program metadata account. It stores information about the particular SPL-Governance program instance -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProgramMetadata { /// Governance account type pub account_type: GovernanceAccountType, diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 6a72ec14..5cafa047 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -30,7 +30,6 @@ use crate::{ governance::GovernanceConfig, proposal_transaction::ProposalTransactionV2, realm::RealmV2, - realm_config::get_realm_config_data_for_realm, vote_record::Vote, vote_record::VoteKind, }, @@ -38,8 +37,10 @@ use crate::{ }; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use crate::state::realm_config::RealmConfigAccount; + /// Proposal option vote result -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum OptionVoteResult { /// Vote on the option is not resolved yet None, @@ -52,7 +53,7 @@ pub enum OptionVoteResult { } /// Proposal Option -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalOption { /// Option label pub label: String, @@ -74,7 +75,7 @@ pub struct ProposalOption { } /// Proposal vote type -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoteType { /// Single choice vote with mutually exclusive choices /// In the SingeChoice mode there can ever be a single winner @@ -103,7 +104,7 @@ pub enum VoteType { } /// Governance Proposal -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -472,30 +473,28 @@ impl ProposalV2 { max_voter_weight.max(total_vote_weight) } - /// Resolves max voter weight + /// Resolves max voter weight using either 1) voting governing_token_mint supply or 2) max voter weight if configured for the token mint #[allow(clippy::too_many_arguments)] pub fn resolve_max_voter_weight( &mut self, - program_id: &Pubkey, - realm_config_info: &AccountInfo, - vote_governing_token_mint_info: &AccountInfo, account_info_iter: &mut Iter, realm: &Pubkey, realm_data: &RealmV2, + realm_config_data: &RealmConfigAccount, + vote_governing_token_mint_info: &AccountInfo, vote_kind: &VoteKind, ) -> Result { - // 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 - && realm_data.community_mint == *vote_governing_token_mint_info.key + // if the Realm is configured to use max voter weight for the given voting governing_token_mint then use the externally provided max_voter_weight + // instead of the supply based max + if let Some(max_voter_weight_addin) = realm_config_data + .get_token_config(realm_data, vote_governing_token_mint_info.key)? + .max_voter_weight_addin { - let realm_config_data = - get_realm_config_data_for_realm(program_id, realm_config_info, realm)?; - let max_voter_weight_record_info = next_account_info(account_info_iter)?; let max_voter_weight_record_data = get_max_voter_weight_record_data_for_realm_and_governing_token_mint( - &realm_config_data.max_community_voter_weight_addin.unwrap(), + &max_voter_weight_addin, max_voter_weight_record_info, realm, vote_governing_token_mint_info.key, @@ -1166,8 +1165,8 @@ mod test { config: RealmConfig { council_mint: Some(Pubkey::new_unique()), reserved: [0; 6], - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, + legacy1: 0, + legacy2: 0, community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, diff --git a/governance/program/src/state/proposal_transaction.rs b/governance/program/src/state/proposal_transaction.rs index dcba1f6c..f1d4d5a4 100644 --- a/governance/program/src/state/proposal_transaction.rs +++ b/governance/program/src/state/proposal_transaction.rs @@ -25,7 +25,7 @@ use solana_program::{ use spl_governance_tools::account::{get_account_data, AccountMaxSize}; /// InstructionData wrapper. It can be removed once Borsh serialization for Instruction is supported in the SDK -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct InstructionData { /// Pubkey of the instruction processor that executes this instruction pub program_id: Pubkey, @@ -36,7 +36,7 @@ pub struct InstructionData { } /// Account metadata used to define Instructions -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct AccountMetaData { /// An account's public key pub pubkey: Pubkey, @@ -83,7 +83,7 @@ impl From<&InstructionData> for Instruction { } /// Account for an instruction to be executed for Proposal -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalTransactionV2 { /// Governance Account type pub account_type: GovernanceAccountType, diff --git a/governance/program/src/state/realm.rs b/governance/program/src/state/realm.rs index ff498d5d..7ee9e1d6 100644 --- a/governance/program/src/state/realm.rs +++ b/governance/program/src/state/realm.rs @@ -21,14 +21,17 @@ use crate::{ state::{ enums::{GovernanceAccountType, MintMaxVoteWeightSource}, legacy::RealmV1, + realm_config::GoverningTokenType, token_owner_record::get_token_owner_record_data_for_realm, vote_record::VoteKind, }, PROGRAM_AUTHORITY_SEED, }; +use crate::state::realm_config::get_realm_config_data_for_realm; + /// Realm Config instruction args -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct RealmConfigArgs { /// Indicates whether council_mint should be used /// If yes then council_mint account must also be passed to the instruction @@ -40,17 +43,45 @@ pub struct RealmConfigArgs { /// The source used for community mint max vote weight source pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, - /// Indicates whether an external addin program should be used to provide community voters weights - /// If yes then the voters weight program account must be passed to the instruction - pub use_community_voter_weight_addin: bool, + /// Community token config args + pub community_token_config_args: GoverningTokenConfigArgs, - /// Indicates whether an external addin program should be used to provide max voters weight for the community mint + /// Council token config args + pub council_token_config_args: GoverningTokenConfigArgs, +} + +/// Realm Config instruction args +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema, Default)] +pub struct GoverningTokenConfigArgs { + /// Indicates whether an external addin program should be used to provide voters weights + /// If yes then the voters weight program account must be passed to the instruction + pub use_voter_weight_addin: bool, + + /// Indicates whether an external addin program should be used to provide max voters weight for the token /// If yes then the max voter weight program account must be passed to the instruction - pub use_max_community_voter_weight_addin: bool, + pub use_max_voter_weight_addin: bool, + + /// Governing token type defines how the token is used for governance + pub token_type: GoverningTokenType, +} + +/// Realm Config instruction args with account parametres +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema, Default)] +pub struct GoverningTokenConfigAccountArgs { + /// Specifies an external plugin program which should be used to provide voters weights + /// for the given goventing token + pub voter_weight_addin: Option, + + /// Specifies an external an external plugin program should be used to provide max voters weight + /// for the given goventing token + pub max_voter_weight_addin: Option, + + /// Governing token type defines how the token is used for governance power + pub token_type: GoverningTokenType, } /// SetRealmAuthority instruction action -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum SetRealmAuthorityAction { /// Sets realm authority without any checks /// Uncheck option allows to set the realm authority to non governance accounts @@ -66,13 +97,17 @@ pub enum SetRealmAuthorityAction { } /// Realm Config defining Realm parameters. -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct RealmConfig { - /// Indicates whether an external addin program should be used to provide voters weights for the community mint - pub use_community_voter_weight_addin: bool, + /// Legacy field introdcued and used in V2 as use_community_voter_weight_addin: bool + /// If the field is going to be reused in future version it must be taken under consideration + /// that for some Realms it might be already set to 1 + pub legacy1: u8, - /// Indicates whether an external addin program should be used to provide max voter weight for the community mint - pub use_max_community_voter_weight_addin: bool, + /// Legacy field introdcued and used in V2 as use_max_community_voter_weight_addin: bool + /// If the field is going to be reused in future version it must be taken under consideration + /// that for some Realms it might be already set to 1 + pub legacy2: u8, /// Reserved space for future versions pub reserved: [u8; 6], @@ -89,7 +124,7 @@ pub struct RealmConfig { /// Governance Realm Account /// Account PDA seeds" ['governance', name] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct RealmV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -251,13 +286,13 @@ impl RealmV2 { token_owner_record_data.assert_token_owner_or_delegate_is_signer(create_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)?; let voter_weight = token_owner_record_data.resolve_voter_weight( - program_id, - realm_config_info, account_info_iter, - realm, self, + &realm_config_data, VoterWeightAction::CreateGovernance, realm, )?; @@ -399,8 +434,10 @@ pub fn get_governing_token_holding_address( } /// Asserts given realm config args are correct -pub fn assert_valid_realm_config_args(config_args: &RealmConfigArgs) -> Result<(), ProgramError> { - match config_args.community_mint_max_vote_weight_source { +pub fn assert_valid_realm_config_args( + realm_config_args: &RealmConfigArgs, +) -> Result<(), ProgramError> { + match realm_config_args.community_mint_max_vote_weight_source { MintMaxVoteWeightSource::SupplyFraction(fraction) => { if !(1..=MintMaxVoteWeightSource::SUPPLY_FRACTION_BASE).contains(&fraction) { return Err(GovernanceError::InvalidMaxVoteWeightSupplyFraction.into()); @@ -433,8 +470,8 @@ mod test { name: "test-realm".to_string(), config: RealmConfig { council_mint: Some(Pubkey::new_unique()), - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, + legacy1: 0, + legacy2: 0, reserved: [0; 6], community_mint_max_vote_weight_source: MintMaxVoteWeightSource::Absolute(100), min_community_weight_to_create_governance: 10, @@ -450,7 +487,7 @@ mod test { } /// Realm Config instruction args - #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] + #[derive(Clone, Debug, PartialEq, Eq, 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 @@ -464,7 +501,7 @@ mod test { } /// Instructions supported by the Governance program - #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] + #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum GovernanceInstructionV1 { /// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint CreateRealm { @@ -495,8 +532,8 @@ mod test { min_community_weight_to_create_governance: 100, community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, + community_token_config_args: GoverningTokenConfigArgs::default(), + council_token_config_args: GoverningTokenConfigArgs::default(), }, }; diff --git a/governance/program/src/state/realm_config.rs b/governance/program/src/state/realm_config.rs index 3b6bec58..e8e7ab8b 100644 --- a/governance/program/src/state/realm_config.rs +++ b/governance/program/src/state/realm_config.rs @@ -1,4 +1,7 @@ //! RealmConfig account +use std::slice::Iter; + +use solana_program::account_info::next_account_info; use solana_program::{ account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, @@ -10,9 +13,90 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize}; use crate::{error::GovernanceError, state::enums::GovernanceAccountType}; +use crate::state::realm::GoverningTokenConfigArgs; + +use crate::state::realm::{RealmConfigArgs, RealmV2}; + +/// The type of the governing token defines: +/// 1) Who retains the authority over deposited tokens +/// 2) Which token instructions Deposit, Withdraw and Revoke (burn) are allowed +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub enum GoverningTokenType { + /// Liquid token is a token which is fully liquid and the token owner retains full authority over it + /// Deposit - Yes + /// Withdraw - Yes + /// Revoke - No, Realm authority cannot revoke liquid tokens + Liquid, + + /// Membership token is a token controlled by Realm authority + /// Deposit - Yes, membership tokens can be deposited to gain governance power + /// The membership tokens are conventionally minted into the holding account to keep them out of members possesion + /// Withdraw - No, after membership tokens are deposited they are no longer transferable and can't be withdrawn + /// Revoke - Yes, Realm authority can Revoke (burn) membership tokens + Membership, + + /// Dormant token is a token which is only a placeholder and its deposits are not accepted and not used for governance power within the Realm + /// + /// The Dormant token type is used when only a single voting population is operational. For example a Multisig starter DAO uses Council only + /// and sets Community as Dormant to indicate its not utilised for any governance power. + /// Once the starter DAO decides to decentralise then it can change the Community token to Liquid + /// + /// Note: When an external voter weight plugin which takes deposits of the token is used then the type should be set to Dormant + /// to make the intention explicit + /// + /// Deposit - No, dormant tokens can't be deposited into the Realm + /// Withdraw - Yes, tokens can still be withdrawn from Realm to support scenario where the config is changed while some tokens are still deposited + /// Revoke - No, Realm authority cannot revoke dormant tokens + Dormant, +} + +#[allow(clippy::derivable_impls)] +impl Default for GoverningTokenType { + fn default() -> Self { + GoverningTokenType::Liquid + } +} + +/// GoverningTokenConfig specifies configuration for Realm governing token (Community or Council) +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema, Default)] +pub struct GoverningTokenConfig { + /// Plugin providing voter weights for the governing token + pub voter_weight_addin: Option, + + /// Plugin providing max voter weight for the governing token + pub max_voter_weight_addin: Option, + + /// Governing token type + pub token_type: GoverningTokenType, + + /// Reserved space for future versions + pub reserved: [u8; 8], +} + +/// Reserved 110 bytes +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct Reserved110 { + /// Reserved 64 bytes + pub reserved64: [u8; 64], + /// Reserved 32 bytes + pub reserved32: [u8; 32], + /// Reserved 4 bytes + pub reserved14: [u8; 14], +} + +impl Default for Reserved110 { + fn default() -> Self { + Self { + reserved64: [0; 64], + reserved32: [0; 32], + reserved14: [0; 14], + } + } +} + /// RealmConfig account /// The account is an optional extension to RealmConfig stored on Realm account -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct RealmConfigAccount { /// Governance account type pub account_type: GovernanceAccountType, @@ -20,23 +104,14 @@ pub struct RealmConfigAccount { /// The realm the config belong to pub realm: Pubkey, - /// Addin providing voter weights for community token - pub community_voter_weight_addin: Option, + /// Community token config + pub community_token_config: GoverningTokenConfig, - /// Addin providing max vote weight for community token - /// Note: This field is not implemented in the current version - pub max_community_voter_weight_addin: Option, - - /// Addin providing voter weights for council token - /// Note: This field is not implemented in the current version - pub council_voter_weight_addin: Option, - - /// Addin providing max vote weight for council token - /// Note: This field is not implemented in the current version - pub council_max_vote_weight_addin: Option, + /// Council token config + pub council_token_config: GoverningTokenConfig, /// Reserved - pub reserved: [u8; 128], + pub reserved: Reserved110, } impl AccountMaxSize for RealmConfigAccount { @@ -51,6 +126,98 @@ impl IsInitialized for RealmConfigAccount { } } +impl RealmConfigAccount { + /// Returns GoverningTokenConfig for the given governing_token_mint + pub fn get_token_config( + &self, + realm_data: &RealmV2, + governing_token_mint: &Pubkey, + ) -> Result<&GoverningTokenConfig, ProgramError> { + let token_config = if *governing_token_mint == realm_data.community_mint { + &self.community_token_config + } else if Some(*governing_token_mint) == realm_data.config.council_mint { + &self.council_token_config + } else { + return Err(GovernanceError::InvalidGoverningTokenMint.into()); + }; + + Ok(token_config) + } + + /// Assertes the given governing token can be revoked + pub fn assert_can_revoke_governing_token( + &self, + realm_data: &RealmV2, + governing_token_mint: &Pubkey, + ) -> Result<(), ProgramError> { + let governing_token_type = &self + .get_token_config(realm_data, governing_token_mint)? + .token_type; + + match governing_token_type { + GoverningTokenType::Membership => Ok(()), + GoverningTokenType::Liquid | GoverningTokenType::Dormant => { + Err(GovernanceError::CannotRevokeGoverningTokens.into()) + } + } + } + + /// Assertes the given governing token can be deposited + pub fn assert_can_deposit_governing_token( + &self, + realm_data: &RealmV2, + governing_token_mint: &Pubkey, + ) -> Result<(), ProgramError> { + let governing_token_type = &self + .get_token_config(realm_data, governing_token_mint)? + .token_type; + + match governing_token_type { + GoverningTokenType::Membership | GoverningTokenType::Liquid => Ok(()), + // Note: Preventing deposits of the Dormant type tokens is not a direct security concern + // It only makes the intention of not using deposited tokens as governnace power stronger + GoverningTokenType::Dormant => Err(GovernanceError::CannotDepositDormantTokens.into()), + } + } + + /// Assertes the given governing token can be withdrawn + pub fn assert_can_withdraw_governing_token( + &self, + realm_data: &RealmV2, + governing_token_mint: &Pubkey, + ) -> Result<(), ProgramError> { + let governing_token_type = &self + .get_token_config(realm_data, governing_token_mint)? + .token_type; + + match governing_token_type { + GoverningTokenType::Dormant | GoverningTokenType::Liquid => Ok(()), + GoverningTokenType::Membership => { + Err(GovernanceError::CannotWithdrawMembershipTokens.into()) + } + } + } + + /// Asserts the given RealmConfigArgs represent a valid Realm configuraiton change + pub fn assert_can_change_config( + &self, + realm_config_args: &RealmConfigArgs, + ) -> Result<(), ProgramError> { + // Existing community token type can't be changed to Membership because it would + // give the Realm authority the right to burn members tokens which should not be the case because the tokens belong to the members + // On the other had for the Council token it's acceptable and in fact desired change becuase council tokens denote memebership + // which should be controled by the Realm + if self.community_token_config.token_type != GoverningTokenType::Membership + && realm_config_args.community_token_config_args.token_type + == GoverningTokenType::Membership + { + return Err(GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into()); + } + + Ok(()) + } +} + /// Deserializes RealmConfig account and checks owner program pub fn get_realm_config_data( program_id: &Pubkey, @@ -59,17 +226,39 @@ pub fn get_realm_config_data( get_account_data::(program_id, realm_config_info) } -/// Deserializes RealmConfig account and checks the owner program and the Realm it belongs to +/// If the account exists then desrialises it into RealmConfigAccount struct and checks the owner program and the Realm it belongs to +/// If the accoutn doesn't exist then it checks its address is derived from the given owner program and Realm and returns default RealmConfigAccount pub fn get_realm_config_data_for_realm( program_id: &Pubkey, realm_config_info: &AccountInfo, realm: &Pubkey, ) -> Result { - let realm_config_data = get_realm_config_data(program_id, realm_config_info)?; + let realm_config_data = if realm_config_info.data_is_empty() { + // If RealmConfigAccount doesn't exist yet then validate its PDA + // PDA validation is required because RealmConfigAccount might not exist for legacy Realms + // and then its absense is used as default RealmConfigAccount value with no plugins and Liquid governance tokens + let realm_config_address = get_realm_config_address(program_id, realm); - if realm_config_data.realm != *realm { - return Err(GovernanceError::InvalidRealmConfigForRealm.into()); - } + if realm_config_address != *realm_config_info.key { + return Err(GovernanceError::InvalidRealmConfigAddress.into()); + } + + RealmConfigAccount { + account_type: GovernanceAccountType::RealmConfig, + realm: *realm, + community_token_config: GoverningTokenConfig::default(), + council_token_config: GoverningTokenConfig::default(), + reserved: Reserved110::default(), + } + } else { + let realm_config_data = get_realm_config_data(program_id, realm_config_info)?; + + if realm_config_data.realm != *realm { + return Err(GovernanceError::InvalidRealmConfigForRealm.into()); + } + + realm_config_data + }; Ok(realm_config_data) } @@ -83,6 +272,32 @@ pub fn get_realm_config_address_seeds(realm: &Pubkey) -> [&[u8]; 2] { pub fn get_realm_config_address(program_id: &Pubkey, realm: &Pubkey) -> Pubkey { Pubkey::find_program_address(&get_realm_config_address_seeds(realm), program_id).0 } +/// Resolves GoverningTokenConfig from GoverningTokenConfigArgs and instruction accounts +pub fn resolve_governing_token_config( + account_info_iter: &mut Iter, + governing_token_config_args: &GoverningTokenConfigArgs, +) -> Result { + let voter_weight_addin = if governing_token_config_args.use_voter_weight_addin { + let voter_weight_addin_info = next_account_info(account_info_iter)?; + Some(*voter_weight_addin_info.key) + } else { + None + }; + + let max_voter_weight_addin = if governing_token_config_args.use_max_voter_weight_addin { + let max_voter_weight_addin_info = next_account_info(account_info_iter)?; + Some(*max_voter_weight_addin_info.key) + } else { + None + }; + + Ok(GoverningTokenConfig { + voter_weight_addin, + max_voter_weight_addin, + token_type: governing_token_config_args.token_type.clone(), + reserved: [0; 8], + }) +} #[cfg(test)] mod test { @@ -94,11 +309,19 @@ mod test { let realm_config = RealmConfigAccount { account_type: GovernanceAccountType::RealmV2, realm: Pubkey::new_unique(), - community_voter_weight_addin: Some(Pubkey::new_unique()), - max_community_voter_weight_addin: Some(Pubkey::new_unique()), - council_voter_weight_addin: Some(Pubkey::new_unique()), - council_max_vote_weight_addin: Some(Pubkey::new_unique()), - reserved: [0; 128], + community_token_config: GoverningTokenConfig { + voter_weight_addin: Some(Pubkey::new_unique()), + max_voter_weight_addin: Some(Pubkey::new_unique()), + token_type: GoverningTokenType::Liquid, + reserved: [0; 8], + }, + council_token_config: GoverningTokenConfig { + voter_weight_addin: Some(Pubkey::new_unique()), + max_voter_weight_addin: Some(Pubkey::new_unique()), + token_type: GoverningTokenType::Liquid, + reserved: [0; 8], + }, + reserved: Reserved110::default(), }; let size = realm_config.try_to_vec().unwrap().len(); diff --git a/governance/program/src/state/signatory_record.rs b/governance/program/src/state/signatory_record.rs index b97b200a..eedd9eb5 100644 --- a/governance/program/src/state/signatory_record.rs +++ b/governance/program/src/state/signatory_record.rs @@ -15,7 +15,7 @@ use crate::{error::GovernanceError, PROGRAM_AUTHORITY_SEED}; use crate::state::{enums::GovernanceAccountType, legacy::SignatoryRecordV1}; /// Account PDA seeds: ['governance', proposal, signatory] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct SignatoryRecordV2 { /// Governance account type pub account_type: GovernanceAccountType, diff --git a/governance/program/src/state/token_owner_record.rs b/governance/program/src/state/token_owner_record.rs index 1cd43af3..56ba8a8d 100644 --- a/governance/program/src/state/token_owner_record.rs +++ b/governance/program/src/state/token_owner_record.rs @@ -10,7 +10,7 @@ use crate::{ error::GovernanceError, state::{ enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1, - realm::RealmV2, realm_config::get_realm_config_data_for_realm, + realm::RealmV2, }, PROGRAM_AUTHORITY_SEED, }; @@ -26,9 +26,11 @@ use solana_program::{ use spl_governance_addin_api::voter_weight::VoterWeightAction; use spl_governance_tools::account::{get_account_data, AccountMaxSize}; +use crate::state::realm_config::RealmConfigAccount; + /// Governance Token Owner Record /// Account PDA seeds: ['governance', realm, token_mint, token_owner ] -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct TokenOwnerRecordV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -189,25 +191,22 @@ impl TokenOwnerRecordV2 { #[allow(clippy::too_many_arguments)] pub fn resolve_voter_weight( &self, - program_id: &Pubkey, - realm_config_info: &AccountInfo, account_info_iter: &mut Iter, - realm: &Pubkey, realm_data: &RealmV2, + realm_config_data: &RealmConfigAccount, weight_action: VoterWeightAction, weight_action_target: &Pubkey, ) -> Result { - // if the realm uses addin for community voter weight then use the externally provided weight - if realm_data.config.use_community_voter_weight_addin - && realm_data.community_mint == self.governing_token_mint + // if the Realm is configured to use voter weight plugin for our governing_token_mint then use the externally provided voter_weight + // instead of governing_token_deposit_amount + if let Some(voter_weight_addin) = realm_config_data + .get_token_config(realm_data, &self.governing_token_mint)? + .voter_weight_addin { let voter_weight_record_info = next_account_info(account_info_iter)?; - let realm_config_data = - get_realm_config_data_for_realm(program_id, realm_config_info, realm)?; - let voter_weight_record_data = get_voter_weight_record_data_for_token_owner_record( - &realm_config_data.community_voter_weight_addin.unwrap(), + &voter_weight_addin, voter_weight_record_info, self, )?; diff --git a/governance/program/src/state/vote_record.rs b/governance/program/src/state/vote_record.rs index c9e8b99a..2bd06567 100644 --- a/governance/program/src/state/vote_record.rs +++ b/governance/program/src/state/vote_record.rs @@ -25,10 +25,10 @@ use crate::state::{ /// Voter choice for a proposal option /// In the current version only 1) Single choice and 2) Multiple choices proposals are supported /// In the future versions we can add support for 1) Quadratic voting, 2) Ranked choice voting and 3) Weighted voting -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct VoteChoice { /// The rank given to the choice by voter - /// Note: The filed is not used in the current version + /// Note: The field is not used in the current version pub rank: u8, /// The voter's weight percentage given by the voter to the choice @@ -47,7 +47,7 @@ impl VoteChoice { } /// User's vote -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum Vote { /// Vote approving choices Approve(Vec), @@ -64,7 +64,7 @@ pub enum Vote { } /// VoteKind defines the type of the vote being cast -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub enum VoteKind { /// Electorate vote is cast by the voting population identified by governing_token_mint /// Approve, Deny and Abstain votes are Electorate votes @@ -83,7 +83,7 @@ pub fn get_vote_kind(vote: &Vote) -> VoteKind { } /// Proposal VoteRecord -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct VoteRecordV2 { /// Governance account type pub account_type: GovernanceAccountType, diff --git a/governance/program/src/tools/spl_token.rs b/governance/program/src/tools/spl_token.rs index aad3db58..b7659f82 100644 --- a/governance/program/src/tools/spl_token.rs +++ b/governance/program/src/tools/spl_token.rs @@ -121,6 +121,37 @@ pub fn transfer_spl_tokens<'a>( Ok(()) } +/// Mint SPL Tokens +pub fn mint_spl_tokens_to<'a>( + mint_info: &AccountInfo<'a>, + destination_info: &AccountInfo<'a>, + mint_authority_info: &AccountInfo<'a>, + amount: u64, + spl_token_info: &AccountInfo<'a>, +) -> ProgramResult { + let mint_to_ix = spl_token::instruction::mint_to( + &spl_token::id(), + mint_info.key, + destination_info.key, + mint_authority_info.key, + &[], + amount, + ) + .unwrap(); + + invoke( + &mint_to_ix, + &[ + spl_token_info.clone(), + mint_authority_info.clone(), + mint_info.clone(), + destination_info.clone(), + ], + )?; + + Ok(()) +} + /// Transfers SPL Tokens from a token account owned by the provided PDA authority with seeds pub fn transfer_spl_tokens_signed<'a>( source_info: &AccountInfo<'a>, @@ -170,6 +201,55 @@ pub fn transfer_spl_tokens_signed<'a>( Ok(()) } +/// Burns SPL Tokens from a token account owned by the provided PDA authority with seeds +pub fn burn_spl_tokens_signed<'a>( + token_account_info: &AccountInfo<'a>, + token_mint_info: &AccountInfo<'a>, + authority_info: &AccountInfo<'a>, + authority_seeds: &[&[u8]], + program_id: &Pubkey, + amount: u64, + spl_token_info: &AccountInfo<'a>, +) -> ProgramResult { + let (authority_address, bump_seed) = Pubkey::find_program_address(authority_seeds, program_id); + + if authority_address != *authority_info.key { + msg!( + "Burn SPL Token with Authority PDA: {:?} was requested while PDA: {:?} was expected", + authority_info.key, + authority_address + ); + return Err(ProgramError::InvalidSeeds); + } + + let burn_ix = spl_token::instruction::burn( + &spl_token::id(), + token_account_info.key, + token_mint_info.key, + authority_info.key, + &[], + amount, + ) + .unwrap(); + + let mut signers_seeds = authority_seeds.to_vec(); + let bump = &[bump_seed]; + signers_seeds.push(bump); + + invoke_signed( + &burn_ix, + &[ + spl_token_info.clone(), + token_account_info.clone(), + token_mint_info.clone(), + authority_info.clone(), + ], + &[&signers_seeds[..]], + )?; + + Ok(()) +} + /// Asserts the given account_info represents a valid SPL Token account which is initialized and belongs to spl_token program pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<(), ProgramError> { if account_info.data_is_empty() { @@ -195,6 +275,11 @@ pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<( Ok(()) } +/// Checks if the given account_info is spl-token token account +pub fn is_spl_token_account(account_info: &AccountInfo) -> bool { + assert_is_valid_spl_token_account(account_info).is_ok() +} + /// Asserts the given mint_info represents a valid SPL Token Mint account which is initialized and belongs to spl_token program pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), ProgramError> { if mint_info.data_is_empty() { @@ -210,7 +295,7 @@ pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), Pro } // In token program [36, 8, 1, is_initialized(1), 36] is the layout - let data = mint_info.try_borrow_data().unwrap(); + let data = mint_info.try_borrow_data()?; let is_initialized = array_ref![data, 45, 1]; if is_initialized == &[0] { @@ -220,6 +305,11 @@ pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), Pro Ok(()) } +/// Checks if the given account_info is be spl-token mint account +pub fn is_spl_token_mint(mint_info: &AccountInfo) -> bool { + assert_is_valid_spl_token_mint(mint_info).is_ok() +} + /// Computationally cheap method to get mint from a token account /// It reads mint without deserializing full account data pub fn get_spl_token_mint(token_account_info: &AccountInfo) -> Result { diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index 414cf5ed..4f47f818 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -8,7 +8,10 @@ use solana_program_test::tokio; use program_test::*; use spl_governance::{ error::GovernanceError, - state::enums::{ProposalState, VoteThreshold, VoteTipping}, + state::{ + enums::{ProposalState, VoteThreshold, VoteTipping}, + vote_record::Vote, + }, }; #[tokio::test] @@ -1243,3 +1246,52 @@ async fn test_cast_council_vote() { proposal_account.vote_threshold ); } + +#[tokio::test] +async fn test_cast_vote_with_invalid_realm_config_account_address_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Try bypass config check by using none existing config account + let realm_config_address = Pubkey::new_unique(); + + // Act + let err = governance_test + .with_cast_vote_using_instruction( + &proposal_cookie, + &token_owner_record_cookie, + Vote::Deny, + |i| { + i.accounts[10].pubkey = realm_config_address; // realm_config_address + }, + None, + ) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::InvalidRealmConfigAddress.into()); +} diff --git a/governance/program/tests/process_create_proposal.rs b/governance/program/tests/process_create_proposal.rs index 13cf0319..cb49ec6c 100644 --- a/governance/program/tests/process_create_proposal.rs +++ b/governance/program/tests/process_create_proposal.rs @@ -542,3 +542,41 @@ async fn test_create_proposal_with_disabled_community_vote_error() { GovernanceError::GoverningTokenMintNotAllowedToVote.into() ); } + +#[tokio::test] +async fn test_create_proposal_with_invalid_realm_config_account_address_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // Try bypass config check by using none existing config account + let realm_config_address = Pubkey::new_unique(); + + // Act + let err = governance_test + .with_proposal_using_instruction(&token_owner_record_cookie, &mut governance_cookie, |i| { + i.accounts[8].pubkey = realm_config_address; + }) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::InvalidRealmConfigAddress.into()); +} diff --git a/governance/program/tests/process_create_realm.rs b/governance/program/tests/process_create_realm.rs index bc6fd0cc..cbe86b85 100644 --- a/governance/program/tests/process_create_realm.rs +++ b/governance/program/tests/process_create_realm.rs @@ -5,12 +5,9 @@ use solana_program_test::*; mod program_test; use program_test::*; -use spl_governance::state::{ - enums::MintMaxVoteWeightSource, - realm::{get_realm_address, RealmConfigArgs}, -}; +use spl_governance::state::{enums::MintMaxVoteWeightSource, realm::get_realm_address}; -use self::args::SetRealmConfigArgs; +use crate::program_test::args::RealmSetupArgs; #[tokio::test] async fn test_create_realm() { @@ -33,23 +30,16 @@ async fn test_create_realm_with_non_default_config() { // Arrange let mut governance_test = GovernanceProgramTest::start_new().await; - let realm_config_args = RealmConfigArgs { + let realm_setup_args = RealmSetupArgs { use_council_mint: false, community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(1), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, + min_community_weight_to_create_governance: 1, + ..Default::default() }; // Act let realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; // Assert diff --git a/governance/program/tests/process_deposit_governing_tokens.rs b/governance/program/tests/process_deposit_governing_tokens.rs index cf39b3fb..98406b51 100644 --- a/governance/program/tests/process_deposit_governing_tokens.rs +++ b/governance/program/tests/process_deposit_governing_tokens.rs @@ -7,7 +7,12 @@ mod program_test; use program_test::*; use solana_sdk::signature::{Keypair, Signer}; -use spl_governance::{error::GovernanceError, instruction::deposit_governing_tokens}; +use spl_governance::{ + error::GovernanceError, instruction::deposit_governing_tokens, + state::realm_config::GoverningTokenType, +}; + +use crate::program_test::args::*; #[tokio::test] async fn test_deposit_initial_community_tokens() { @@ -235,55 +240,6 @@ async fn test_deposit_initial_community_tokens_with_owner_must_sign_error() { // Assert assert_eq!(error, GovernanceError::GoverningTokenOwnerMustSign.into()); } -#[tokio::test] -async fn test_deposit_initial_community_tokens_with_invalid_owner_error() { - // Arrange - let mut governance_test = GovernanceProgramTest::start_new().await; - let realm_cookie = governance_test.with_realm().await; - - let token_owner = Keypair::new(); - let transfer_authority = Keypair::new(); - let token_source = Keypair::new(); - - let invalid_owner = Keypair::new(); - - let amount = 10; - - governance_test - .bench - .create_token_account_with_transfer_authority( - &token_source, - &realm_cookie.account.community_mint, - &realm_cookie.community_mint_authority, - amount, - &token_owner, - &transfer_authority.pubkey(), - ) - .await; - - let deposit_ix = deposit_governing_tokens( - &governance_test.program_id, - &realm_cookie.address, - &token_source.pubkey(), - &invalid_owner.pubkey(), - &transfer_authority.pubkey(), - &governance_test.bench.context.payer.pubkey(), - amount, - &realm_cookie.account.community_mint, - ); - - // // Act - - let error = governance_test - .bench - .process_transaction(&[deposit_ix], Some(&[&transfer_authority, &invalid_owner])) - .await - .err() - .unwrap(); - - // Assert - assert_eq!(error, GovernanceError::GoverningTokenOwnerMustSign.into()); -} #[tokio::test] async fn test_deposit_community_tokens_with_malicious_holding_account_error() { @@ -340,3 +296,62 @@ async fn test_deposit_community_tokens_with_malicious_holding_account_error() { GovernanceError::InvalidGoverningTokenHoldingAccount.into() ); } + +#[tokio::test] +async fn test_deposit_community_tokens_using_mint() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + let realm_cookie = governance_test.with_realm().await; + + // Act + let token_owner_record_cookie = governance_test + .with_initial_governing_token_deposit_using_mint( + &realm_cookie.address, + &realm_cookie.account.community_mint, + &realm_cookie.community_mint_authority, + 10, + None, + ) + .await + .unwrap(); + + // Assert + + let token_owner_record = governance_test + .get_token_owner_record_account(&token_owner_record_cookie.address) + .await; + + assert_eq!(token_owner_record_cookie.account, token_owner_record); + + let holding_account = governance_test + .get_token_account(&realm_cookie.community_token_holding_account) + .await; + + assert_eq!( + token_owner_record.governing_token_deposit_amount, + holding_account.amount + ); +} + +#[tokio::test] +async fn test_deposit_comunity_tokens_with_cannot_deposit_dormant_tokens_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Dormant; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + // Act + let err = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::CannotDepositDormantTokens.into()); +} diff --git a/governance/program/tests/process_revoke_governing_tokens.rs b/governance/program/tests/process_revoke_governing_tokens.rs new file mode 100644 index 00000000..8114da1e --- /dev/null +++ b/governance/program/tests/process_revoke_governing_tokens.rs @@ -0,0 +1,468 @@ +#![cfg(feature = "test-bpf")] + +use solana_program::pubkey::Pubkey; +use solana_program_test::*; + +mod program_test; + +use program_test::*; + +use spl_governance::{ + error::GovernanceError, + state::{realm::get_governing_token_holding_address, realm_config::GoverningTokenType}, +}; +use spl_governance_test_sdk::tools::NopOverride; + +use crate::program_test::args::RealmSetupArgs; +use solana_sdk::signature::{Keypair, Signer}; + +#[tokio::test] +async fn test_revoke_community_tokens() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.community_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + governance_test + .revoke_community_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + // Assert + + let token_owner_record = governance_test + .get_token_owner_record_account(&token_owner_record_cookie.address) + .await; + + assert_eq!(token_owner_record.governing_token_deposit_amount, 0); + + let holding_account = governance_test + .get_token_account(&realm_cookie.community_token_holding_account) + .await; + + assert_eq!(holding_account.amount, 0); +} + +#[tokio::test] +async fn test_revoke_council_tokens() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + governance_test + .revoke_council_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + // Assert + + let token_owner_record = governance_test + .get_token_owner_record_account(&token_owner_record_cookie.address) + .await; + + assert_eq!(token_owner_record.governing_token_deposit_amount, 0); + + let holding_account = governance_test + .get_token_account(&realm_cookie.council_token_holding_account.unwrap()) + .await; + + assert_eq!(holding_account.amount, 0); +} + +#[tokio::test] +async fn test_revoke_community_tokens_with_cannot_revoke_liquid_token_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .revoke_community_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::CannotRevokeGoverningTokens.into()); +} + +#[tokio::test] +async fn test_revoke_community_tokens_with_cannot_revoke_dormant_token_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.community_token_config_args.token_type = GoverningTokenType::Dormant; + + governance_test + .set_realm_config(&mut realm_cookie, &realm_config_args) + .await + .unwrap(); + + // Act + let err = governance_test + .revoke_community_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::CannotRevokeGoverningTokens.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_realm_authority_must_sign_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[1].is_signer = false, // realm_authority + Some(&[]), + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::RealmAuthorityMustSign.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_invalid_realm_authority_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Try to use fake auhtority + let realm_authority = Keypair::new(); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[1].pubkey = realm_authority.pubkey(), // realm_authority + Some(&[&realm_authority]), + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::InvalidAuthorityForRealm.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_invalid_token_holding_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Try to revoke from the community holding account + let governing_token_holding_address = get_governing_token_holding_address( + &governance_test.program_id, + &realm_cookie.address, + &realm_cookie.account.community_mint, + ); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[2].pubkey = governing_token_holding_address, // governing_token_holding_address + None, + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!( + err, + GovernanceError::InvalidGoverningTokenHoldingAccount.into() + ); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_other_realm_config_account_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Try use other Realm config + let realm_cookie2 = governance_test.with_realm().await; + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[5].pubkey = realm_cookie2.realm_config.address, //realm_config_address + None, + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::InvalidRealmConfigForRealm.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_invalid_realm_config_account_address_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Try bypass config check by using none existing config account + let realm_config_address = Pubkey::new_unique(); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[5].pubkey = realm_config_address, // realm_config_address + None, + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::InvalidRealmConfigAddress.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_token_owner_record_for_different_mint_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Try to revoke from the community token owner record + let token_owner_record_cookie2 = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 1, + |i| i.accounts[3].pubkey = token_owner_record_cookie2.address, // token_owner_record_address + None, + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!( + err, + GovernanceError::InvalidGoverningMintForTokenOwnerRecord.into() + ); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_too_large_amount_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 200, + NopOverride, + None, + ) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::InvalidRevokeAmount.into()); +} + +#[tokio::test] +async fn test_revoke_council_tokens_with_partial_revoke_amount() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + governance_test + .revoke_governing_tokens_using_instruction( + &realm_cookie, + &token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + 5, + NopOverride, + None, + ) + .await + .unwrap(); + + // Assert + + let token_owner_record = governance_test + .get_token_owner_record_account(&token_owner_record_cookie.address) + .await; + + assert_eq!(token_owner_record.governing_token_deposit_amount, 95); +} diff --git a/governance/program/tests/process_set_realm_config.rs b/governance/program/tests/process_set_realm_config.rs index 83ab2ed6..d790dc46 100644 --- a/governance/program/tests/process_set_realm_config.rs +++ b/governance/program/tests/process_set_realm_config.rs @@ -8,10 +8,10 @@ mod program_test; use program_test::*; use spl_governance::{ error::GovernanceError, - state::{enums::MintMaxVoteWeightSource, realm::RealmConfigArgs}, + state::{realm::GoverningTokenConfigAccountArgs, realm_config::GoverningTokenType}, }; -use self::args::SetRealmConfigArgs; +use crate::program_test::args::RealmSetupArgs; #[tokio::test] async fn test_set_realm_config() { @@ -20,25 +20,12 @@ async fn test_set_realm_config() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let realm_setup_args = RealmSetupArgs::default(); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); @@ -57,27 +44,14 @@ async fn test_set_realm_config_with_authority_must_sign_error() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let realm_setup_args = RealmSetupArgs::default(); // Act let err = governance_test .set_realm_config_using_instruction( &mut realm_cookie, - &set_realm_config_args, + &realm_setup_args, |i| i.accounts[1].is_signer = false, Some(&[]), ) @@ -96,20 +70,7 @@ async fn test_set_realm_config_with_no_authority_error() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let realm_setup_args = RealmSetupArgs::default(); governance_test .set_realm_authority(&realm_cookie, None) @@ -121,7 +82,7 @@ async fn test_set_realm_config_with_no_authority_error() { let err = governance_test .set_realm_config_using_instruction( &mut realm_cookie, - &set_realm_config_args, + &realm_setup_args, |i| i.accounts[1].is_signer = false, Some(&[]), ) @@ -140,20 +101,7 @@ async fn test_set_realm_config_with_invalid_authority_error() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let realm_setup_args = RealmSetupArgs::default(); let realm_cookie2 = governance_test.with_realm().await; @@ -163,7 +111,7 @@ async fn test_set_realm_config_with_invalid_authority_error() { // Act let err = governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .err() .unwrap(); @@ -179,24 +127,12 @@ async fn test_set_realm_config_with_remove_council() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: false, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let mut realm_setup_args = RealmSetupArgs::default(); + realm_setup_args.use_council_mint = false; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); @@ -216,27 +152,14 @@ async fn test_set_realm_config_with_council_change_error() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let realm_setup_args = RealmSetupArgs::default(); // Try to replace council mint realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique()); // Act let err = governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .err() .unwrap(); @@ -255,33 +178,21 @@ async fn test_set_realm_config_with_council_restore_error() { let mut realm_cookie = governance_test.with_realm().await; - let realm_config_args = RealmConfigArgs { - use_council_mint: false, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - - let mut set_realm_config_args = SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, - }; + let mut realm_setup_args = RealmSetupArgs::default(); + realm_setup_args.use_council_mint = false; governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Try to restore council mint after removing it - set_realm_config_args.realm_config_args.use_council_mint = true; + realm_setup_args.use_council_mint = true; realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique()); // Act let err = governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .err() .unwrap(); @@ -292,3 +203,134 @@ async fn test_set_realm_config_with_council_restore_error() { GovernanceError::RealmCouncilMintChangeIsNotSupported.into() ); } + +#[tokio::test] +async fn test_set_realm_config_with_liquid_community_token_cannot_be_changed_to_memebership_error() +{ + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + + let mut realm_setup_args = RealmSetupArgs::default(); + + // Try to change Community token type to Membership + realm_setup_args.community_token_config_args.token_type = GoverningTokenType::Membership; + + // Act + let err = governance_test + .set_realm_config(&mut realm_cookie, &realm_setup_args) + .await + .err() + .unwrap(); + + // Assert + assert_eq!( + err, + GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into() + ); +} + +#[tokio::test] +async fn test_set_realm_config_for_community_token_config() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + + let mut realm_setup_args = RealmSetupArgs::default(); + + // Change Community token type to Dormant and set plugins + realm_setup_args.community_token_config_args = GoverningTokenConfigAccountArgs { + voter_weight_addin: Some(Pubkey::new_unique()), + max_voter_weight_addin: Some(Pubkey::new_unique()), + token_type: GoverningTokenType::Dormant, + }; + + // Act + + governance_test + .set_realm_config(&mut realm_cookie, &realm_setup_args) + .await + .unwrap(); + + // Assert + + let realm_config_account = governance_test + .get_realm_config_account(&realm_cookie.realm_config.address) + .await; + + assert_eq!( + realm_config_account.community_token_config.token_type, + GoverningTokenType::Dormant + ); + + assert_eq!( + realm_config_account + .community_token_config + .voter_weight_addin, + realm_setup_args + .community_token_config_args + .voter_weight_addin + ); + + assert_eq!( + realm_config_account + .community_token_config + .max_voter_weight_addin, + realm_setup_args + .community_token_config_args + .max_voter_weight_addin + ); +} + +#[tokio::test] +async fn test_set_realm_config_for_council_token_config() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + + let mut realm_setup_args = RealmSetupArgs::default(); + + // Change Council token type to Membership and set plugins + realm_setup_args.council_token_config_args = GoverningTokenConfigAccountArgs { + voter_weight_addin: Some(Pubkey::new_unique()), + max_voter_weight_addin: Some(Pubkey::new_unique()), + token_type: GoverningTokenType::Membership, + }; + + // Act + + governance_test + .set_realm_config(&mut realm_cookie, &realm_setup_args) + .await + .unwrap(); + + // Assert + + let realm_config_account = governance_test + .get_realm_config_account(&realm_cookie.realm_config.address) + .await; + + assert_eq!( + realm_config_account.council_token_config.token_type, + GoverningTokenType::Membership + ); + + assert_eq!( + realm_config_account.council_token_config.voter_weight_addin, + realm_setup_args + .council_token_config_args + .voter_weight_addin + ); + + assert_eq!( + realm_config_account + .council_token_config + .max_voter_weight_addin, + realm_setup_args + .council_token_config_args + .max_voter_weight_addin + ); +} diff --git a/governance/program/tests/process_withdraw_governing_tokens.rs b/governance/program/tests/process_withdraw_governing_tokens.rs index 10eb166a..2e593b52 100644 --- a/governance/program/tests/process_withdraw_governing_tokens.rs +++ b/governance/program/tests/process_withdraw_governing_tokens.rs @@ -9,10 +9,13 @@ use program_test::*; use solana_sdk::signature::Signer; use spl_governance::{ - error::GovernanceError, instruction::withdraw_governing_tokens, - state::token_owner_record::get_token_owner_record_address, + error::GovernanceError, + instruction::withdraw_governing_tokens, + state::{realm_config::GoverningTokenType, token_owner_record::get_token_owner_record_address}, }; +use crate::program_test::args::RealmSetupArgs; + #[tokio::test] async fn test_withdraw_community_tokens() { // Arrange @@ -420,3 +423,65 @@ async fn test_withdraw_governing_tokens_after_proposal_cancelled() { source_account.amount ); } + +#[tokio::test] +async fn test_withdraw_council_tokens_with_cannot_withdraw_membership_tokens_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_config_args = RealmSetupArgs::default(); + realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership; + + let realm_cookie = governance_test + .with_realm_using_args(&realm_config_args) + .await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Act + let err = governance_test + .withdraw_council_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::CannotWithdrawMembershipTokens.into()); +} + +#[tokio::test] +async fn test_withdraw_dormant_community_tokens() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let mut realm_cookie = governance_test.with_realm().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut realm_setup_args = RealmSetupArgs::default(); + realm_setup_args.community_token_config_args.token_type = GoverningTokenType::Dormant; + + governance_test + .set_realm_config(&mut realm_cookie, &realm_setup_args) + .await + .unwrap(); + + // Act + governance_test + .withdraw_community_tokens(&realm_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + // Assert + let token_owner_record = governance_test + .get_token_owner_record_account(&token_owner_record_cookie.address) + .await; + + assert_eq!(0, token_owner_record.governing_token_deposit_amount); +} diff --git a/governance/program/tests/program_test/args.rs b/governance/program/tests/program_test/args.rs index 75dd8c58..c4e74f52 100644 --- a/governance/program/tests/program_test/args.rs +++ b/governance/program/tests/program_test/args.rs @@ -1,28 +1,70 @@ -use solana_program::pubkey::Pubkey; -use spl_governance::state::{enums::MintMaxVoteWeightSource, realm::RealmConfigArgs}; +use spl_governance::state::{ + enums::MintMaxVoteWeightSource, realm::GoverningTokenConfigAccountArgs, +}; #[derive(Clone, Debug, PartialEq)] -pub struct SetRealmConfigArgs { - pub realm_config_args: RealmConfigArgs, - pub community_voter_weight_addin: Option, - pub max_community_voter_weight_addin: Option, +pub struct RealmSetupArgs { + pub use_council_mint: bool, + pub min_community_weight_to_create_governance: u64, + pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, + pub community_token_config_args: GoverningTokenConfigAccountArgs, + pub council_token_config_args: GoverningTokenConfigAccountArgs, } -impl Default for SetRealmConfigArgs { +impl Default for RealmSetupArgs { fn default() -> Self { - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100), - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, - }; - Self { - realm_config_args, - community_voter_weight_addin: None, - max_community_voter_weight_addin: None, + use_council_mint: true, + community_token_config_args: GoverningTokenConfigAccountArgs::default(), + council_token_config_args: GoverningTokenConfigAccountArgs::default(), + min_community_weight_to_create_governance: 10, + community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, } } } + +#[derive(Clone, Debug, PartialEq)] +pub struct PluginSetupArgs { + pub use_community_voter_weight_addin: bool, + pub use_max_community_voter_weight_addin: bool, + pub use_council_voter_weight_addin: bool, + pub use_max_council_voter_weight_addin: bool, +} + +impl PluginSetupArgs { + #[allow(dead_code)] + pub const COMMUNITY_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs { + use_community_voter_weight_addin: true, + use_max_community_voter_weight_addin: false, + use_council_voter_weight_addin: false, + use_max_council_voter_weight_addin: false, + }; + #[allow(dead_code)] + pub const COMMUNITY_MAX_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs { + use_community_voter_weight_addin: false, + use_max_community_voter_weight_addin: true, + use_council_voter_weight_addin: false, + use_max_council_voter_weight_addin: false, + }; + #[allow(dead_code)] + pub const COUNCIL_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs { + use_community_voter_weight_addin: false, + use_max_community_voter_weight_addin: false, + use_council_voter_weight_addin: true, + use_max_council_voter_weight_addin: false, + }; + #[allow(dead_code)] + pub const COUNCIL_MAX_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs { + use_community_voter_weight_addin: false, + use_max_community_voter_weight_addin: false, + use_council_voter_weight_addin: false, + use_max_council_voter_weight_addin: true, + }; + #[allow(dead_code)] + pub const ALL: PluginSetupArgs = PluginSetupArgs { + use_community_voter_weight_addin: true, + use_max_community_voter_weight_addin: true, + use_council_voter_weight_addin: true, + use_max_council_voter_weight_addin: true, + }; +} diff --git a/governance/program/tests/program_test/cookies.rs b/governance/program/tests/program_test/cookies.rs index 5f3436fa..875d5a4d 100644 --- a/governance/program/tests/program_test/cookies.rs +++ b/governance/program/tests/program_test/cookies.rs @@ -32,7 +32,7 @@ pub struct RealmCookie { pub realm_authority: Option, - pub realm_config: Option, + pub realm_config: RealmConfigCookie, } #[derive(Debug)] diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index f1d0bbec..13138792 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -20,9 +20,9 @@ use spl_governance::{ create_native_treasury, create_program_governance, create_proposal, create_realm, create_token_governance, create_token_owner_record, deposit_governing_tokens, execute_transaction, finalize_vote, flag_transaction_error, insert_transaction, - relinquish_vote, remove_signatory, remove_transaction, set_governance_config, - set_governance_delegate, set_realm_authority, set_realm_config, sign_off_proposal, - upgrade_program_metadata, withdraw_governing_tokens, + relinquish_vote, remove_signatory, remove_transaction, revoke_governing_tokens, + set_governance_config, set_governance_delegate, set_realm_authority, set_realm_config, + sign_off_proposal, upgrade_program_metadata, withdraw_governing_tokens, }, processor::process_instruction, state::{ @@ -41,10 +41,12 @@ use spl_governance::{ get_proposal_transaction_address, InstructionData, ProposalTransactionV2, }, realm::{ - get_governing_token_holding_address, get_realm_address, RealmConfig, RealmConfigArgs, - RealmV2, SetRealmAuthorityAction, + get_governing_token_holding_address, get_realm_address, + GoverningTokenConfigAccountArgs, RealmConfig, RealmV2, SetRealmAuthorityAction, + }, + realm_config::{ + get_realm_config_address, GoverningTokenConfig, RealmConfigAccount, Reserved110, }, - realm_config::{get_realm_config_address, RealmConfigAccount}, signatory_record::{get_signatory_record_address, SignatoryRecordV2}, token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2}, vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2}, @@ -73,8 +75,8 @@ use spl_governance_test_sdk::{ ProgramTestBench, }; -use self::{ - args::SetRealmConfigArgs, +use crate::{ + args::{PluginSetupArgs, RealmSetupArgs}, cookies::{ GovernanceCookie, GovernedAccountCookie, GovernedMintCookie, GovernedProgramCookie, GovernedTokenCookie, MaxVoterWeightRecordCookie, NativeTreasuryCookie, @@ -178,47 +180,52 @@ impl GovernanceProgramTest { } } - #[allow(dead_code)] - pub fn get_default_set_realm_config_args(&mut self) -> SetRealmConfigArgs { - let realm_config_args = RealmConfigArgs { - use_council_mint: true, - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, - min_community_weight_to_create_governance: 10, - use_community_voter_weight_addin: self.voter_weight_addin_id.is_some(), - use_max_community_voter_weight_addin: self.max_voter_weight_addin_id.is_some(), - }; - - let community_voter_weight_addin = if realm_config_args.use_community_voter_weight_addin { - self.voter_weight_addin_id - } else { - None - }; - - let max_community_voter_weight_addin = - if realm_config_args.use_max_community_voter_weight_addin { - self.max_voter_weight_addin_id - } else { - None - }; - - SetRealmConfigArgs { - realm_config_args, - community_voter_weight_addin, - max_community_voter_weight_addin, - } - } - #[allow(dead_code)] pub async fn with_realm(&mut self) -> RealmCookie { - let set_realm_config_args = self.get_default_set_realm_config_args(); - self.with_realm_using_config_args(&set_realm_config_args) - .await + let realm_setup_args = RealmSetupArgs::default(); + self.with_realm_using_args(&realm_setup_args).await } #[allow(dead_code)] - pub async fn with_realm_using_config_args( + pub async fn with_realm_using_addins( &mut self, - set_realm_config_args: &SetRealmConfigArgs, + plugin_setup_args: PluginSetupArgs, + ) -> RealmCookie { + let mut realm_setup_args = RealmSetupArgs::default(); + + if plugin_setup_args.use_community_voter_weight_addin { + realm_setup_args + .community_token_config_args + .voter_weight_addin = self.voter_weight_addin_id; + } + + if plugin_setup_args.use_max_community_voter_weight_addin { + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = self.max_voter_weight_addin_id; + } + + if plugin_setup_args.use_council_voter_weight_addin { + realm_setup_args + .council_token_config_args + .voter_weight_addin = self.voter_weight_addin_id; + } + + if plugin_setup_args.use_max_council_voter_weight_addin { + realm_setup_args + .council_token_config_args + .max_voter_weight_addin = self.max_voter_weight_addin_id; + } + + let realm_cookie = self.with_realm_using_args(&realm_setup_args).await; + + realm_cookie + } + + #[allow(dead_code)] + pub async fn with_realm_using_args( + &mut self, + realm_setup_args: &RealmSetupArgs, ) -> RealmCookie { let name = format!("Realm #{}", self.next_realm_id).to_string(); self.next_realm_id += 1; @@ -246,7 +253,7 @@ impl GovernanceProgramTest { council_token_mint_pubkey, council_token_holding_address, council_token_mint_authority, - ) = if set_realm_config_args.realm_config_args.use_council_mint { + ) = if realm_setup_args.use_council_mint { let council_token_mint_keypair = Keypair::new(); let council_token_mint_authority = Keypair::new(); @@ -275,20 +282,43 @@ impl GovernanceProgramTest { let realm_authority = Keypair::new(); + let community_token_args = GoverningTokenConfigAccountArgs { + voter_weight_addin: realm_setup_args + .community_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .community_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .community_token_config_args + .token_type + .clone(), + }; + + let council_token_args = GoverningTokenConfigAccountArgs { + voter_weight_addin: realm_setup_args + .council_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .council_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .council_token_config_args + .token_type + .clone(), + }; + let create_realm_ix = create_realm( &self.program_id, &realm_authority.pubkey(), &community_token_mint_keypair.pubkey(), &self.bench.payer.pubkey(), council_token_mint_pubkey, - set_realm_config_args.community_voter_weight_addin, - set_realm_config_args.max_community_voter_weight_addin, + Some(community_token_args), + Some(council_token_args), name.clone(), - set_realm_config_args - .realm_config_args - .min_community_weight_to_create_governance, - set_realm_config_args - .realm_config_args + realm_setup_args.min_community_weight_to_create_governance, + realm_setup_args .community_mint_max_vote_weight_source .clone(), ); @@ -309,41 +339,51 @@ impl GovernanceProgramTest { council_mint: council_token_mint_pubkey, reserved: [0; 6], - min_community_weight_to_create_governance: set_realm_config_args - .realm_config_args + min_community_weight_to_create_governance: realm_setup_args .min_community_weight_to_create_governance, - community_mint_max_vote_weight_source: set_realm_config_args - .realm_config_args + community_mint_max_vote_weight_source: realm_setup_args .community_mint_max_vote_weight_source .clone(), - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, + legacy1: 0, + legacy2: 0, }, voting_proposal_count: 0, reserved_v2: [0; 128], }; - let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some() - || set_realm_config_args - .max_community_voter_weight_addin - .is_some() - { - Some(RealmConfigCookie { - address: get_realm_config_address(&self.program_id, &realm_address), - account: RealmConfigAccount { - account_type: GovernanceAccountType::RealmConfig, - realm: realm_address, - community_voter_weight_addin: set_realm_config_args - .community_voter_weight_addin, - max_community_voter_weight_addin: set_realm_config_args - .max_community_voter_weight_addin, - council_voter_weight_addin: None, - council_max_vote_weight_addin: None, - reserved: [0; 128], + let realm_config_cookie = RealmConfigCookie { + address: get_realm_config_address(&self.program_id, &realm_address), + account: RealmConfigAccount { + account_type: GovernanceAccountType::RealmConfig, + realm: realm_address, + reserved: Reserved110::default(), + community_token_config: GoverningTokenConfig { + voter_weight_addin: realm_setup_args + .community_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .community_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .community_token_config_args + .token_type + .clone(), + reserved: [0; 8], }, - }) - } else { - None + council_token_config: GoverningTokenConfig { + voter_weight_addin: realm_setup_args + .council_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .council_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .council_token_config_args + .token_type + .clone(), + reserved: [0; 8], + }, + }, }; RealmCookie { @@ -405,8 +445,8 @@ impl GovernanceProgramTest { community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, min_community_weight_to_create_governance, - use_community_voter_weight_addin: false, - use_max_community_voter_weight_addin: false, + legacy1: 0, + legacy2: 0, }, voting_proposal_count: 0, reserved_v2: [0; 128], @@ -421,6 +461,17 @@ impl GovernanceProgramTest { let council_token_holding_address = get_governing_token_holding_address(&self.program_id, &realm_address, &council_mint); + let realm_config_cookie = RealmConfigCookie { + address: get_realm_config_address(&self.program_id, &realm_address), + account: RealmConfigAccount { + account_type: GovernanceAccountType::RealmConfig, + realm: realm_address, + council_token_config: GoverningTokenConfig::default(), + reserved: Reserved110::default(), + community_token_config: GoverningTokenConfig::default(), + }, + }; + RealmCookie { address: realm_address, account, @@ -433,7 +484,7 @@ impl GovernanceProgramTest { realm_cookie.council_mint_authority.as_ref().unwrap(), )), realm_authority: Some(realm_authority), - realm_config: None, + realm_config: realm_config_cookie, } } @@ -457,6 +508,28 @@ impl GovernanceProgramTest { pub async fn with_community_token_owner_record( &mut self, realm_cookie: &RealmCookie, + ) -> TokenOwnerRecordCookie { + self.with_token_owner_record(realm_cookie, &realm_cookie.account.community_mint) + .await + } + + #[allow(dead_code)] + pub async fn with_council_token_owner_record( + &mut self, + realm_cookie: &RealmCookie, + ) -> TokenOwnerRecordCookie { + self.with_token_owner_record( + realm_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + ) + .await + } + + #[allow(dead_code)] + pub async fn with_token_owner_record( + &mut self, + realm_cookie: &RealmCookie, + governing_token_mint: &Pubkey, ) -> TokenOwnerRecordCookie { let token_owner = Keypair::new(); @@ -464,7 +537,7 @@ impl GovernanceProgramTest { &self.program_id, &realm_cookie.address, &token_owner.pubkey(), - &realm_cookie.account.community_mint, + governing_token_mint, &self.bench.payer.pubkey(), ); @@ -476,7 +549,7 @@ impl GovernanceProgramTest { let account = TokenOwnerRecordV2 { account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: realm_cookie.address, - governing_token_mint: realm_cookie.account.community_mint, + governing_token_mint: *governing_token_mint, governing_token_owner: token_owner.pubkey(), governing_token_deposit_amount: 0, governance_delegate: None, @@ -490,7 +563,7 @@ impl GovernanceProgramTest { let token_owner_record_address = get_token_owner_record_address( &self.program_id, &realm_cookie.address, - &realm_cookie.account.community_mint, + governing_token_mint, &token_owner.pubkey(), ); @@ -741,6 +814,73 @@ impl GovernanceProgramTest { }) } + #[allow(dead_code)] + pub async fn with_initial_governing_token_deposit_using_mint( + &mut self, + realm_address: &Pubkey, + governing_mint: &Pubkey, + governing_mint_authority: &Keypair, + amount: u64, + token_owner: Option, + ) -> Result { + let token_owner = token_owner.unwrap_or(Keypair::new()); + let token_source = Keypair::new(); + + let deposit_governing_tokens_ix = deposit_governing_tokens( + &self.program_id, + realm_address, + governing_mint, + &token_owner.pubkey(), + &governing_mint_authority.pubkey(), + &self.bench.payer.pubkey(), + amount, + governing_mint, + ); + + self.bench + .process_transaction( + &[deposit_governing_tokens_ix], + Some(&[&token_owner, &governing_mint_authority]), + ) + .await?; + + let token_owner_record_address = get_token_owner_record_address( + &self.program_id, + realm_address, + governing_mint, + &token_owner.pubkey(), + ); + + let account = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, + realm: *realm_address, + governing_token_mint: *governing_mint, + governing_token_owner: token_owner.pubkey(), + governing_token_deposit_amount: amount, + governance_delegate: None, + unrelinquished_votes_count: 0, + total_votes_count: 0, + outstanding_proposal_count: 0, + reserved: [0; 7], + reserved_v2: [0; 128], + }; + + let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string()); + + Ok(TokenOwnerRecordCookie { + address: token_owner_record_address, + account, + + token_source_amount: amount, + token_source: token_source.pubkey(), + token_owner, + governance_authority: None, + governance_delegate, + voter_weight_record: None, + max_voter_weight_record: None, + }) + } + #[allow(dead_code)] pub async fn mint_community_tokens(&mut self, realm_cookie: &RealmCookie, amount: u64) { let token_account_keypair = Keypair::new(); @@ -974,62 +1114,36 @@ impl GovernanceProgramTest { pub async fn set_realm_config( &mut self, realm_cookie: &mut RealmCookie, - set_realm_config_args: &SetRealmConfigArgs, + realm_setup_args: &RealmSetupArgs, ) -> Result<(), ProgramError> { - self.set_realm_config_using_instruction( - realm_cookie, - set_realm_config_args, - NopOverride, - None, - ) - .await + self.set_realm_config_using_instruction(realm_cookie, realm_setup_args, NopOverride, None) + .await } #[allow(dead_code)] pub async fn set_realm_config_using_instruction( &mut self, realm_cookie: &mut RealmCookie, - set_realm_config_args: &SetRealmConfigArgs, + realm_setup_args: &RealmSetupArgs, instruction_override: F, signers_override: Option<&[&Keypair]>, ) -> Result<(), ProgramError> { - let council_token_mint = if set_realm_config_args.realm_config_args.use_council_mint { + let council_token_mint = if realm_setup_args.use_council_mint { realm_cookie.account.config.council_mint } else { None }; - let community_voter_weight_addin = if set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin - { - set_realm_config_args.community_voter_weight_addin - } else { - None - }; - - let max_community_voter_weight_addin = if set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin - { - set_realm_config_args.max_community_voter_weight_addin - } else { - None - }; - let mut set_realm_config_ix = set_realm_config( &self.program_id, &realm_cookie.address, &realm_cookie.realm_authority.as_ref().unwrap().pubkey(), council_token_mint, &self.bench.payer.pubkey(), - community_voter_weight_addin, - max_community_voter_weight_addin, - set_realm_config_args - .realm_config_args - .min_community_weight_to_create_governance, - set_realm_config_args - .realm_config_args + Some(realm_setup_args.community_token_config_args.clone()), + Some(realm_setup_args.council_token_config_args.clone()), + realm_setup_args.min_community_weight_to_create_governance, + realm_setup_args .community_mint_max_vote_weight_source .clone(), ); @@ -1043,32 +1157,44 @@ impl GovernanceProgramTest { realm_cookie .account .config - .community_mint_max_vote_weight_source = set_realm_config_args - .realm_config_args + .community_mint_max_vote_weight_source = realm_setup_args .community_mint_max_vote_weight_source .clone(); - if set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin - || set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin - { - realm_cookie.realm_config = Some(RealmConfigCookie { - address: get_realm_config_address(&self.program_id, &realm_cookie.address), - account: RealmConfigAccount { - account_type: GovernanceAccountType::RealmConfig, - realm: realm_cookie.address, - community_voter_weight_addin, - max_community_voter_weight_addin, - council_voter_weight_addin: None, - council_max_vote_weight_addin: None, - reserved: [0; 128], + realm_cookie.realm_config = RealmConfigCookie { + address: get_realm_config_address(&self.program_id, &realm_cookie.address), + account: RealmConfigAccount { + account_type: GovernanceAccountType::RealmConfig, + realm: realm_cookie.address, + reserved: Reserved110::default(), + community_token_config: GoverningTokenConfig { + voter_weight_addin: realm_setup_args + .community_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .community_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .community_token_config_args + .token_type + .clone(), + reserved: [0; 8], }, - }) - } - + council_token_config: GoverningTokenConfig { + voter_weight_addin: realm_setup_args + .council_token_config_args + .voter_weight_addin, + max_voter_weight_addin: realm_setup_args + .council_token_config_args + .max_voter_weight_addin, + token_type: realm_setup_args + .council_token_config_args + .token_type + .clone(), + reserved: [0; 8], + }, + }, + }; self.bench .process_transaction(&[set_realm_config_ix], Some(signers)) .await @@ -1129,6 +1255,73 @@ impl GovernanceProgramTest { .await } + #[allow(dead_code)] + pub async fn revoke_community_tokens( + &mut self, + realm_cookie: &RealmCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, + ) -> Result<(), ProgramError> { + self.revoke_governing_tokens_using_instruction( + realm_cookie, + token_owner_record_cookie, + &realm_cookie.account.community_mint, + token_owner_record_cookie + .account + .governing_token_deposit_amount, + NopOverride, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn revoke_council_tokens( + &mut self, + realm_cookie: &RealmCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, + ) -> Result<(), ProgramError> { + self.revoke_governing_tokens_using_instruction( + realm_cookie, + token_owner_record_cookie, + &realm_cookie.account.config.council_mint.unwrap(), + token_owner_record_cookie + .account + .governing_token_deposit_amount, + NopOverride, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn revoke_governing_tokens_using_instruction( + &mut self, + realm_cookie: &RealmCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, + governing_token_mint: &Pubkey, + amount: u64, + instruction_override: F, + signers_override: Option<&[&Keypair]>, + ) -> Result<(), ProgramError> { + let mut revoke_governing_tokens_ix = revoke_governing_tokens( + &self.program_id, + &realm_cookie.address, + &realm_cookie.account.authority.unwrap(), + &token_owner_record_cookie.account.governing_token_owner, + governing_token_mint, + amount, + ); + + instruction_override(&mut revoke_governing_tokens_ix); + + let default_signers = &[realm_cookie.realm_authority.as_ref().unwrap()]; + let signers = signers_override.unwrap_or(default_signers); + + self.bench + .process_transaction(&[revoke_governing_tokens_ix], Some(signers)) + .await + } + #[allow(dead_code)] pub async fn with_governed_account(&mut self) -> GovernedAccountCookie { GovernedAccountCookie { @@ -2157,13 +2350,31 @@ impl GovernanceProgramTest { self.with_cast_vote(proposal_cookie, token_owner_record_cookie, vote) .await } - #[allow(dead_code)] pub async fn with_cast_vote( &mut self, proposal_cookie: &ProposalCookie, token_owner_record_cookie: &TokenOwnerRecordCookie, vote: Vote, + ) -> Result { + self.with_cast_vote_using_instruction( + proposal_cookie, + token_owner_record_cookie, + vote, + NopOverride, + None, + ) + .await + } + + #[allow(dead_code)] + pub async fn with_cast_vote_using_instruction( + &mut self, + proposal_cookie: &ProposalCookie, + token_owner_record_cookie: &TokenOwnerRecordCookie, + vote: Vote, + instruction_override: F, + signers_override: Option<&[&Keypair]>, ) -> Result { let voter_weight_record = if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { @@ -2180,7 +2391,7 @@ impl GovernanceProgramTest { None }; - let cast_vote_ix = cast_vote( + let mut cast_vote_ix = cast_vote( &self.program_id, &token_owner_record_cookie.account.realm, &proposal_cookie.account.governance, @@ -2195,11 +2406,13 @@ impl GovernanceProgramTest { vote.clone(), ); + instruction_override(&mut cast_vote_ix); + + let default_signers = &[&token_owner_record_cookie.token_owner]; + let signers = signers_override.unwrap_or(default_signers); + self.bench - .process_transaction( - &[cast_vote_ix], - Some(&[&token_owner_record_cookie.token_owner]), - ) + .process_transaction(&[cast_vote_ix], Some(signers)) .await?; let vote_amount = token_owner_record_cookie @@ -2628,7 +2841,7 @@ impl GovernanceProgramTest { } #[allow(dead_code)] - pub async fn get_realm_config_data( + pub async fn get_realm_config_account( &mut self, realm_config_address: &Pubkey, ) -> RealmConfigAccount { diff --git a/governance/program/tests/setup_realm_with_all_addins.rs b/governance/program/tests/setup_realm_with_all_addins.rs index b6a72bc1..694bb749 100644 --- a/governance/program/tests/setup_realm_with_all_addins.rs +++ b/governance/program/tests/setup_realm_with_all_addins.rs @@ -7,36 +7,46 @@ mod program_test; use program_test::*; +use crate::program_test::args::RealmSetupArgs; + #[tokio::test] async fn test_create_realm_with_all_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; + let mut realm_setup_args = RealmSetupArgs::default(); + + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; + + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; + // Act - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_args(&realm_setup_args) + .await; // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -44,56 +54,44 @@ async fn test_set_all_addins_for_realm_without_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -101,58 +99,46 @@ async fn test_set_all_addin_for_realm_without_council_and_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); + let mut realm_setup_args = RealmSetupArgs::default(); - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args.realm_config_args.use_council_mint = false; + realm_setup_args.use_council_mint = false; let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -162,60 +148,61 @@ async fn test_set_all_realm_addins_for_realm_with_all_addins() { let mut realm_cookie = governance_test.with_realm().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); + let mut realm_setup_args = RealmSetupArgs::default(); - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; let max_community_voter_weight_addin_address = Pubkey::new_unique(); - set_realm_config_args.max_community_voter_weight_addin = - Some(max_community_voter_weight_addin_address); + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = Some(max_community_voter_weight_addin_address); let community_voter_weight_addin_address = Pubkey::new_unique(); - set_realm_config_args.community_voter_weight_addin = Some(community_voter_weight_addin_address); + realm_setup_args + .community_token_config_args + .voter_weight_addin = Some(community_voter_weight_addin_address); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); assert_eq!( - realm_config_data.max_community_voter_weight_addin, + realm_config_data + .community_token_config + .max_voter_weight_addin, Some(max_community_voter_weight_addin_address) ); assert_eq!( - realm_config_data.community_voter_weight_addin, + realm_config_data.community_token_config.voter_weight_addin, Some(community_voter_weight_addin_address) ); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -223,48 +210,42 @@ async fn test_set_realm_config_without_addins_for_realm_without_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = None; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + realm_setup_args + .community_token_config_args + .voter_weight_addin = None; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) + let realm_config_data = governance_test + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!( - !realm_account_data - .config - .use_max_community_voter_weight_addin - ); + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_none()); - assert!(!realm_account_data.config.use_community_voter_weight_addin); + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_none()); } #[tokio::test] @@ -273,35 +254,28 @@ async fn test_set_realm_config_without_any_addins_for_realm_with_existing_addins let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; let mut realm_cookie = governance_test.with_realm().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let realm_setup_args = RealmSetupArgs::default(); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(!realm_account_data.config.use_community_voter_weight_addin); - let realm_config_data = governance_test - .get_realm_config_data(&realm_cookie.realm_config.unwrap().address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!(realm_config_data.max_community_voter_weight_addin.is_none()); - assert!(realm_config_data.community_voter_weight_addin.is_none()); + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_none()); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_none()); } diff --git a/governance/program/tests/setup_realm_with_max_voter_weight_addin.rs b/governance/program/tests/setup_realm_with_max_voter_weight_addin.rs index 7caf4f93..686615e2 100644 --- a/governance/program/tests/setup_realm_with_max_voter_weight_addin.rs +++ b/governance/program/tests/setup_realm_with_max_voter_weight_addin.rs @@ -7,34 +7,37 @@ mod program_test; use program_test::*; +use crate::program_test::args::RealmSetupArgs; + #[tokio::test] async fn test_create_realm_with_max_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; + let mut realm_setup_args = RealmSetupArgs::default(); + + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; + // Act - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_args(&realm_setup_args) + .await; // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -42,50 +45,35 @@ async fn test_set_realm_max_voter_weight_addin_for_realm_without_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -93,52 +81,37 @@ async fn test_set_realm_max_voter_weight_addin_for_realm_without_council_and_add // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); + let mut realm_setup_args = RealmSetupArgs::default(); - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; - - set_realm_config_args.realm_config_args.use_council_mint = false; + realm_setup_args.use_council_mint = false; let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -148,46 +121,42 @@ async fn test_set_realm_max_voter_weight_addin_for_realm_with_existing_voter_wei let mut realm_cookie = governance_test.with_realm().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); + let mut realm_setup_args = RealmSetupArgs::default(); - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = governance_test.max_voter_weight_addin_id; let max_community_voter_weight_addin_address = Pubkey::new_unique(); - set_realm_config_args.max_community_voter_weight_addin = - Some(max_community_voter_weight_addin_address); + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = Some(max_community_voter_weight_addin_address); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!( - realm_account_data - .config - .use_max_community_voter_weight_addin - ); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); assert_eq!( - realm_config_data.max_community_voter_weight_addin, + realm_config_data + .community_token_config + .max_voter_weight_addin, Some(max_community_voter_weight_addin_address) ); + + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_some()); } #[tokio::test] @@ -195,38 +164,33 @@ async fn test_set_realm_config_with_no_max_voter_weight_addin_for_realm_without_ // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; + realm_setup_args + .community_token_config_args + .max_voter_weight_addin = None; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) + let realm_config_data = governance_test + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!( - !realm_account_data - .config - .use_max_community_voter_weight_addin - ); + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_none()); } #[tokio::test] @@ -235,30 +199,28 @@ async fn test_set_realm_config_with_no_max_voter_weight_addin_for_realm_with_exi let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let mut realm_cookie = governance_test.with_realm().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - - set_realm_config_args - .realm_config_args - .use_max_community_voter_weight_addin = false; + let realm_setup_args = RealmSetupArgs::default(); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(!realm_account_data.config.use_community_voter_weight_addin); - let realm_config_data = governance_test - .get_realm_config_data(&realm_cookie.realm_config.unwrap().address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!(realm_config_data.max_community_voter_weight_addin.is_none()); + assert!(realm_config_data + .community_token_config + .max_voter_weight_addin + .is_none()); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_none()); } diff --git a/governance/program/tests/setup_realm_with_voter_weight_addin.rs b/governance/program/tests/setup_realm_with_voter_weight_addin.rs index 6f6c6918..a92339d1 100644 --- a/governance/program/tests/setup_realm_with_voter_weight_addin.rs +++ b/governance/program/tests/setup_realm_with_voter_weight_addin.rs @@ -5,6 +5,7 @@ use solana_program_test::*; mod program_test; +use crate::program_test::args::RealmSetupArgs; use program_test::*; #[tokio::test] @@ -12,25 +13,30 @@ async fn test_create_realm_with_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; + let mut realm_setup_args = RealmSetupArgs::default(); + + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; + // Act - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_args(&realm_setup_args) + .await; // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); } #[tokio::test] @@ -38,41 +44,38 @@ async fn test_set_realm_voter_weight_addin_for_realm_without_addins() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); + realm_setup_args + .community_token_config_args + .voter_weight_addin = None; let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); } #[tokio::test] @@ -80,42 +83,37 @@ async fn test_set_realm_voter_weight_addin_for_realm_without_council_and_addins( // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; - set_realm_config_args.realm_config_args.use_council_mint = false; + let mut realm_setup_args = RealmSetupArgs::default(); + + realm_setup_args.use_council_mint = false; let mut realm_cookie = governance_test - .with_realm_using_config_args(&set_realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); } #[tokio::test] @@ -123,43 +121,44 @@ async fn test_set_realm_voter_weight_addin_for_realm_with_existing_voter_weight_ // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; - let mut realm_cookie = governance_test.with_realm().await; + let mut realm_setup_args = RealmSetupArgs::default(); - let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); + realm_setup_args + .community_token_config_args + .voter_weight_addin = governance_test.voter_weight_addin_id; - set_realm_config_args - .realm_config_args - .use_community_voter_weight_addin = true; + let mut realm_cookie = governance_test + .with_realm_using_args(&realm_setup_args) + .await; let community_voter_weight_addin_address = Pubkey::new_unique(); - set_realm_config_args.community_voter_weight_addin = Some(community_voter_weight_addin_address); + realm_setup_args + .community_token_config_args + .voter_weight_addin = Some(community_voter_weight_addin_address); // Act governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(realm_account_data.config.use_community_voter_weight_addin); - - let realm_config_cookie = realm_cookie.realm_config.unwrap(); - let realm_config_data = governance_test - .get_realm_config_data(&realm_config_cookie.address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert_eq!(realm_config_cookie.account, realm_config_data); + assert_eq!(realm_cookie.realm_config.account, realm_config_data); assert_eq!( - realm_config_data.community_voter_weight_addin, + realm_config_data.community_token_config.voter_weight_addin, Some(community_voter_weight_addin_address) ); + + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_some()); } #[tokio::test] @@ -167,33 +166,37 @@ async fn test_set_realm_config_with_no_voter_weight_addin_for_realm_without_addi // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; - let mut realm_config_args = governance_test.get_default_set_realm_config_args(); - realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let mut realm_setup_args = RealmSetupArgs::default(); + + realm_setup_args + .community_token_config_args + .voter_weight_addin = None; let mut realm_cookie = governance_test - .with_realm_using_config_args(&realm_config_args) + .with_realm_using_args(&realm_setup_args) .await; - realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + realm_setup_args + .community_token_config_args + .voter_weight_addin = None; // Act governance_test - .set_realm_config(&mut realm_cookie, &realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) + let realm_config_data = governance_test + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!(!realm_account_data.config.use_community_voter_weight_addin); + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_none()); } #[tokio::test] @@ -202,29 +205,23 @@ async fn test_set_realm_config_with_no_voter_weight_addin_for_realm_with_existin let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let mut realm_cookie = governance_test.with_realm().await; - let mut realm_config_args = governance_test.get_default_set_realm_config_args(); - realm_config_args - .realm_config_args - .use_community_voter_weight_addin = false; + let realm_setup_args = RealmSetupArgs::default(); // Act governance_test - .set_realm_config(&mut realm_cookie, &realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap(); // Assert - let realm_account_data = governance_test - .get_realm_account(&realm_cookie.address) - .await; - - assert!(!realm_account_data.config.use_community_voter_weight_addin); - let realm_config_data = governance_test - .get_realm_config_data(&realm_cookie.realm_config.unwrap().address) + .get_realm_config_account(&realm_cookie.realm_config.address) .await; - assert!(realm_config_data.community_voter_weight_addin.is_none()); + assert!(realm_config_data + .community_token_config + .voter_weight_addin + .is_none()); } diff --git a/governance/program/tests/use_realm_with_all_addins.rs b/governance/program/tests/use_realm_with_all_addins.rs index ba1fad9f..10c60073 100644 --- a/governance/program/tests/use_realm_with_all_addins.rs +++ b/governance/program/tests/use_realm_with_all_addins.rs @@ -4,15 +4,18 @@ use solana_program_test::*; mod program_test; +use program_test::args::*; use program_test::*; use spl_governance::state::enums::ProposalState; #[tokio::test] -async fn test_cast_vote_with_all_addin() { +async fn test_cast_community_vote_with_all_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::ALL) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -67,12 +70,76 @@ async fn test_cast_vote_with_all_addin() { assert_eq!(proposal_account.state, ProposalState::Voting) } +#[tokio::test] +async fn test_cast_council_vote_with_all_addin() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; + + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::ALL) + .await; + + let mut token_owner_record_cookie = governance_test + .with_council_token_owner_record(&realm_cookie) + .await; + + // voter weight 120 + governance_test + .with_voter_weight_addin_record(&mut token_owner_record_cookie) + .await + .unwrap(); + + // max voter weight 250 + governance_test + .with_max_voter_weight_addin_record_impl(&mut token_owner_record_cookie, 250, None) + .await + .unwrap(); + + let governed_account_cookie = governance_test.with_governed_account().await; + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Act + + let vote_record_cookie = governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Assert + let vote_record_account = governance_test + .get_vote_record_account(&vote_record_cookie.address) + .await; + + assert_eq!(120, vote_record_account.voter_weight); + + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + + assert_eq!(proposal_account.state, ProposalState::Voting) +} + #[tokio::test] async fn test_tip_vote_with_all_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::ALL) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -132,7 +199,9 @@ async fn test_finalize_vote_with_all_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::ALL) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) diff --git a/governance/program/tests/use_realm_with_max_voter_weight_addin.rs b/governance/program/tests/use_realm_with_max_voter_weight_addin.rs index a2a65964..99a4f502 100644 --- a/governance/program/tests/use_realm_with_max_voter_weight_addin.rs +++ b/governance/program/tests/use_realm_with_max_voter_weight_addin.rs @@ -4,16 +4,19 @@ use solana_program_test::*; mod program_test; +use program_test::args::*; use program_test::*; use spl_governance::{error::GovernanceError, state::enums::ProposalState}; #[tokio::test] -async fn test_cast_vote_with_max_voter_weight_addin() { +async fn test_cast_vote_with_community_max_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 100 let mut token_owner_record_cookie = governance_test @@ -56,13 +59,66 @@ async fn test_cast_vote_with_max_voter_weight_addin() { assert_eq!(proposal_account.state, ProposalState::Voting) } +#[tokio::test] +async fn test_cast_vote_with_council_max_voter_weight_addin() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COUNCIL_MAX_VOTER_WEIGHT) + .await; + + // TokenOwnerRecord with voting power of 100 + let mut token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Bump MaxVoterWeight to 200 + governance_test + .with_max_voter_weight_addin_record(&mut token_owner_record_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Act + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Assert + + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + + assert_eq!(proposal_account.state, ProposalState::Voting) +} + #[tokio::test] async fn test_tip_vote_with_max_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 180 let mut token_owner_record_cookie = governance_test @@ -112,7 +168,9 @@ async fn test_tip_vote_with_max_voter_weight_addin_and_max_below_total_cast_vote let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 100 let mut token_owner_record_cookie = governance_test @@ -162,7 +220,9 @@ async fn test_finalize_vote_with_max_voter_weight_addin() { let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 100 let mut token_owner_record_cookie = governance_test @@ -233,7 +293,9 @@ async fn test_finalize_vote_with_max_voter_weight_addin_and_max_below_total_cast let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 100 let mut token_owner_record_cookie = governance_test @@ -304,7 +366,9 @@ async fn test_cast_vote_with_max_voter_weight_addin_and_expired_record_error() { let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT) + .await; // TokenOwnerRecord with voting power of 100 let mut token_owner_record_cookie = governance_test diff --git a/governance/program/tests/use_realm_with_voter_weight_addin.rs b/governance/program/tests/use_realm_with_voter_weight_addin.rs index aa6fcaf7..370c9f5e 100644 --- a/governance/program/tests/use_realm_with_voter_weight_addin.rs +++ b/governance/program/tests/use_realm_with_voter_weight_addin.rs @@ -5,7 +5,9 @@ use solana_program_test::*; mod program_test; +use program_test::args::*; use program_test::*; + use spl_governance::{ error::GovernanceError, state::vote_record::{Vote, VoteChoice}, @@ -13,12 +15,14 @@ use spl_governance::{ use spl_governance_addin_api::voter_weight::VoterWeightAction; #[tokio::test] -async fn test_create_governance_with_voter_weight_addin() { +async fn test_create_governance_with_community_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -47,13 +51,52 @@ async fn test_create_governance_with_voter_weight_addin() { assert_eq!(governance_cookie.account, governance_account); } +#[tokio::test] +async fn test_create_governance_with_council_voter_weight_addin() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COUNCIL_VOTER_WEIGHT) + .await; + + let mut token_owner_record_cookie = governance_test + .with_council_token_owner_record(&realm_cookie) + .await; + + governance_test + .with_voter_weight_addin_record(&mut token_owner_record_cookie) + .await + .unwrap(); + + // Act + let governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // // Assert + let governance_account = governance_test + .get_governance_account(&governance_cookie.address) + .await; + + assert_eq!(governance_cookie.account, governance_account); +} + #[tokio::test] async fn test_create_proposal_with_voter_weight_addin() { // Arrange let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -93,7 +136,9 @@ async fn test_cast_vote_with_voter_weight_addin() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -152,7 +197,9 @@ async fn test_create_token_governance_with_voter_weight_addin() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_token_cookie = governance_test.with_governed_token().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -187,7 +234,9 @@ async fn test_create_mint_governance_with_voter_weight_addin() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_mint_cookie = governance_test.with_governed_mint().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -222,7 +271,9 @@ async fn test_create_program_governance_with_voter_weight_addin() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_program_cookie = governance_test.with_governed_program().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -260,7 +311,9 @@ async fn test_create_governance_with_voter_weight_action_error() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -298,7 +351,9 @@ async fn test_create_governance_with_voter_weight_expiry_error() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -338,7 +393,9 @@ async fn test_cast_vote_with_voter_weight_action_error() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -394,7 +451,9 @@ async fn test_create_governance_with_voter_weight_action_target_error() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -437,7 +496,9 @@ async fn test_create_proposal_with_voter_weight_action_error() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) @@ -481,7 +542,9 @@ async fn test_create_governance_with_voter_weight_record() { let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let governed_account_cookie = governance_test.with_governed_account().await; - let realm_cookie = governance_test.with_realm().await; + let realm_cookie = governance_test + .with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT) + .await; let mut token_owner_record_cookie = governance_test .with_community_token_owner_record(&realm_cookie) diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index e12e0655..fafb0612 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -15,7 +15,7 @@ use spl_governance::{ }; use spl_governance_test_sdk::tools::clone_keypair; -use self::args::SetRealmConfigArgs; +use crate::program_test::args::RealmSetupArgs; #[tokio::test] async fn test_cast_veto_vote() { @@ -431,11 +431,11 @@ async fn test_cast_veto_vote_with_no_council_error() { .unwrap(); // Remove Council - let mut set_realm_config_args = SetRealmConfigArgs::default(); - set_realm_config_args.realm_config_args.use_council_mint = false; + let mut realm_setup_args = RealmSetupArgs::default(); + realm_setup_args.use_council_mint = false; governance_test - .set_realm_config(&mut realm_cookie, &set_realm_config_args) + .set_realm_config(&mut realm_cookie, &realm_setup_args) .await .unwrap();