Governance: Council membership & plugins (#3344)

* chore: Use GovernanceTokenConfig

* wip: add council_token_config to RealmConfigAccount

* chore: update comment

* wip: use GoverningTokenConfigArgs for community token config

* chore: Change Proxy token type to Dormant

* chore: Update comments

* chore: Use GoverningTokenConfigAccountArgs for instructions creators

* chore: Use default  GoverningTokenType

* chore: Make community_token_args optional

* chore: Make Clippy happy

* chore: Update comments

* chore: Always create RealmConfigAccount

* chore: Set CouncilTokenConfig when realm is created

* feat: Update RealmConfigArgs in SetRealmConfig

* chore: Make Clippy happy

* chore: Assert RealmConfigAccount PDA

* wip: Use default RealmConfigAccount if the account doesn't exist

* chore: Remove use_community_voter_weight_addin and use_max_community_voter_weight_addin from RealmConfig

* chore: code review cleanup

* wip: Use voter weight addin for Council

* feat: Use max_voter_weight_plugin for Council

* feat: Impl RevokeGoverningTokens instruction

* chore: Make Clippy happy

* feat: Enforce governing token withdraw and deposit rules

* feat: Support minting deposits

* feat: Do not enforce token source owner for deposit validation

* chore: test_deposit_community_tokens_using_mint

* chore: Remove not implemented comment

* fix: Fix progrma id for resolving realm config for chat

* chore: Update instructions comments

* feat: Do not allow community Liquid token change to Membership

* chore: test_set_realm_config_with_liquid_community_token_cannot_be_changed_to_memebership_error

* chore: test_set_realm_config_for_community_token

* chore: use GoverningTokenConfig for test args

* chore: Refactor SetRealmConfigArgs

* chore: Update RealmSetupArgs names

* chore: test_set_realm_config_for_council_token_config

* chore: Revoke council tokens tests

* chore: test_revoke_council_tokens_with_realm_authority_must_sign_error

* chore: test_revoke_council_tokens_with_invalid_realm_authority_error

* chore: test_revoke_council_tokens_with_invalid_token_holding_error

* chore: RevokeGoverningToken tests

* chore: Dormant and Membership tokens tests

* chore: Using council plugin tests

* chore: RealmConfigAccount validation tests

* chore: Update Dormant token type configs

* fix: Reset legacy fields

* chore: Use Default macro for derivable defaults

* Chore: use assert functions to validate spl-token account and mint

* chore: Update Membership tokens comments

* chore: Pass governing_token_config_args as reference

* chore: Use single statement to return InvalidRevokeAmount error

* chore: Make Clippy happy - add derived Eq

Co-authored-by: Jon Cinque
This commit is contained in:
Sebastian Bor 2022-08-19 19:49:41 +02:00 committed by GitHub
parent 83b5dabf02
commit 6dfc68db13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 2704 additions and 1105 deletions

View File

@ -6,7 +6,7 @@ use spl_governance_tools::account::AccountMaxSize;
/// MaxVoterWeightRecord account /// MaxVoterWeightRecord account
/// The account is used as an api interface to provide max voting power to the governance program from external addin contracts /// 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 { pub struct MaxVoterWeightRecord {
/// VoterWeightRecord discriminator sha256("account:MaxVoterWeightRecord")[..8] /// VoterWeightRecord discriminator sha256("account:MaxVoterWeightRecord")[..8]
/// Note: The discriminator size must match the addin implementing program discriminator size /// Note: The discriminator size must match the addin implementing program discriminator size

View File

@ -5,7 +5,7 @@ use solana_program::{clock::Slot, program_pack::IsInitialized, pubkey::Pubkey};
use spl_governance_tools::account::AccountMaxSize; use spl_governance_tools::account::AccountMaxSize;
/// The governance action VoterWeight is evaluated for /// 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 { pub enum VoterWeightAction {
/// Cast vote for a proposal. Target: Proposal /// Cast vote for a proposal. Target: Proposal
CastVote, CastVote,
@ -26,7 +26,7 @@ pub enum VoterWeightAction {
/// VoterWeightRecord account /// VoterWeightRecord account
/// The account is used as an api interface to provide voting power to the governance program from external addin contracts /// 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 { pub struct VoterWeightRecord {
/// VoterWeightRecord discriminator sha256("account:VoterWeightRecord")[..8] /// VoterWeightRecord discriminator sha256("account:VoterWeightRecord")[..8]
/// Note: The discriminator size must match the addin implementing program discriminator size /// Note: The discriminator size must match the addin implementing program discriminator size

View File

@ -11,7 +11,7 @@ use spl_governance_addin_api::voter_weight::VoterWeightAction;
/// Instructions supported by the VoterWeight addin program /// Instructions supported by the VoterWeight addin program
/// This program is a mock program used by spl-governance for testing and not real addin /// 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)] #[allow(clippy::large_enum_variant)]
pub enum VoterWeightAddinInstruction { pub enum VoterWeightAddinInstruction {
/// Sets up VoterWeightRecord owned by the program /// Sets up VoterWeightRecord owned by the program

View File

@ -11,7 +11,7 @@ use spl_governance::instruction::with_realm_config_accounts;
use crate::state::MessageBody; use crate::state::MessageBody;
/// Instructions supported by the GovernanceChat program /// 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)] #[allow(clippy::large_enum_variant)]
pub enum GovernanceChatInstruction { pub enum GovernanceChatInstruction {
/// Posts a message with a comment for a Proposal /// Posts a message with a comment for a Proposal

View File

@ -18,7 +18,8 @@ use solana_program::{
}; };
use spl_governance::state::{ use spl_governance::state::{
governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance, 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_addin_api::voter_weight::VoterWeightAction;
use spl_governance_tools::account::create_and_serialize_account; use spl_governance_tools::account::create_and_serialize_account;
@ -94,12 +95,13 @@ pub fn process_post_message(
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( let voter_weight = token_owner_record_data.resolve_voter_weight(
governance_program_id, account_info_iter, // voter_weight_record *11
realm_config_info,
account_info_iter, // 11
realm_info.key,
&realm_data, &realm_data,
&realm_config_data,
VoterWeightAction::CommentProposal, VoterWeightAction::CommentProposal,
proposal_info.key, proposal_info.key,
)?; )?;

View File

@ -8,7 +8,7 @@ use solana_program::{
use spl_governance_tools::account::{assert_is_valid_account_of_type, AccountMaxSize}; use spl_governance_tools::account::{assert_is_valid_account_of_type, AccountMaxSize};
/// Defines all GovernanceChat accounts types /// Defines all GovernanceChat accounts types
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum GovernanceChatAccountType { pub enum GovernanceChatAccountType {
/// Default uninitialized account state /// Default uninitialized account state
Uninitialized, Uninitialized,
@ -18,7 +18,7 @@ pub enum GovernanceChatAccountType {
} }
/// Chat message body /// Chat message body
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum MessageBody { pub enum MessageBody {
/// Text message encoded as utf-8 string /// Text message encoded as utf-8 string
Text(String), Text(String),
@ -29,7 +29,7 @@ pub enum MessageBody {
} }
/// Chat message /// Chat message
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ChatMessage { pub struct ChatMessage {
/// Account type /// Account type
pub account_type: GovernanceChatAccountType, pub account_type: GovernanceChatAccountType,

View File

@ -13,7 +13,8 @@ use spl_governance::{
enums::{MintMaxVoteWeightSource, VoteThreshold}, enums::{MintMaxVoteWeightSource, VoteThreshold},
governance::{get_governance_address, GovernanceConfig}, governance::{get_governance_address, GovernanceConfig},
proposal::{get_proposal_address, VoteType}, 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, token_owner_record::get_token_owner_record_address,
}, },
}; };
@ -108,13 +109,19 @@ impl GovernanceChatProgramTest {
let realm_authority = Keypair::new(); 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( let create_realm_ix = create_realm(
&self.governance_program_id, &self.governance_program_id,
&realm_authority.pubkey(), &realm_authority.pubkey(),
&governing_token_mint_keypair.pubkey(), &governing_token_mint_keypair.pubkey(),
&self.bench.payer.pubkey(), &self.bench.payer.pubkey(),
None, None,
self.voter_weight_addin_id, Some(community_token_config_args),
None, None,
name.clone(), name.clone(),
1, 1,

View File

@ -261,23 +261,23 @@ pub enum GovernanceError {
/// Governance PDA must sign /// Governance PDA must sign
#[error("Governance PDA must sign")] #[error("Governance PDA must sign")]
GovernancePdaMustSign, GovernancePdaMustSign, // 561
/// Transaction already flagged with error /// Transaction already flagged with error
#[error("Transaction already flagged with error")] #[error("Transaction already flagged with error")]
TransactionAlreadyFlaggedWithError, TransactionAlreadyFlaggedWithError, // 562
/// Invalid Realm for Governance /// Invalid Realm for Governance
#[error("Invalid Realm for Governance")] #[error("Invalid Realm for Governance")]
InvalidRealmForGovernance, InvalidRealmForGovernance, // 563
/// Invalid Authority for Realm /// Invalid Authority for Realm
#[error("Invalid Authority for Realm")] #[error("Invalid Authority for Realm")]
InvalidAuthorityForRealm, InvalidAuthorityForRealm, // 564
/// Realm has no authority /// Realm has no authority
#[error("Realm has no authority")] #[error("Realm has no authority")]
RealmHasNoAuthority, RealmHasNoAuthority, // 565
/// Realm authority must sign /// Realm authority must sign
#[error("Realm authority must sign")] #[error("Realm authority must sign")]
@ -285,7 +285,7 @@ pub enum GovernanceError {
/// Invalid governing token holding account /// Invalid governing token holding account
#[error("Invalid governing token holding account")] #[error("Invalid governing token holding account")]
InvalidGoverningTokenHoldingAccount, InvalidGoverningTokenHoldingAccount, // 567
/// Realm council mint change is not supported /// Realm council mint change is not supported
#[error("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 /// Cannot Relinquish in Finalizing state
#[error("Cannot Relinquish in Finalizing state")] #[error("Cannot Relinquish in Finalizing state")]
CannotRelinquishInFinalizingState, 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 { impl PrintProgramError for GovernanceError {

View File

@ -11,8 +11,11 @@ use crate::{
program_metadata::get_program_metadata_address, program_metadata::get_program_metadata_address,
proposal::{get_proposal_address, VoteType}, proposal::{get_proposal_address, VoteType},
proposal_transaction::{get_proposal_transaction_address, InstructionData}, proposal_transaction::{get_proposal_transaction_address, InstructionData},
realm::SetRealmAuthorityAction, realm::{
realm::{get_governing_token_holding_address, get_realm_address, RealmConfigArgs}, get_governing_token_holding_address, get_realm_address,
GoverningTokenConfigAccountArgs, RealmConfigArgs,
},
realm::{GoverningTokenConfigArgs, SetRealmAuthorityAction},
realm_config::get_realm_config_address, realm_config::get_realm_config_address,
signatory_record::get_signatory_record_address, signatory_record::get_signatory_record_address,
token_owner_record::get_token_owner_record_address, token_owner_record::get_token_owner_record_address,
@ -29,7 +32,7 @@ use solana_program::{
}; };
/// Instructions supported by the Governance 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)] #[allow(clippy::large_enum_variant)]
pub enum GovernanceInstruction { pub enum GovernanceInstruction {
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint /// 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] /// 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 /// The account will be created with the Realm PDA as its owner
/// 10. `[]` Optional Community Voter Weight Addin Program Id /// 10. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Max Community Voter Weight Addin Program Id
/// 12. `[writable]` Optional 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 { CreateRealm {
#[allow(dead_code)] #[allow(dead_code)]
/// UTF-8 encoded Governance Realm name /// 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 /// 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 /// 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] /// 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 /// 3. `[signer]` Governing Token Owner account
/// 4. `[signer]` Governing Token Transfer authority /// 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] /// 5. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 6. `[signer]` Payer /// 6. `[signer]` Payer
/// 7. `[]` System /// 7. `[]` System
/// 8. `[]` SPL Token /// 8. `[]` SPL Token program
/// 9. `[]` Sysvar Rent /// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
DepositGoverningTokens { DepositGoverningTokens {
/// The amount to deposit into the realm /// The amount to deposit into the realm
#[allow(dead_code)] #[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 /// 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 /// 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] /// 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 /// 2. `[writable]` Governing Token Destination account. All tokens will be transferred to this account
/// 3. `[signer]` Governing Token Owner account /// 3. `[signer]` Governing Token Owner account
/// 4. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner] /// 4. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 5. `[]` SPL Token /// 5. `[]` SPL Token program
/// 6. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
WithdrawGoverningTokens {}, WithdrawGoverningTokens {},
/// Sets Governance Delegate for the given Realm and Governing Token Mint (Community or Council) /// Sets Governance Delegate for the given Realm and Governing Token Mint (Community or Council)
@ -117,7 +127,7 @@ pub enum GovernanceInstruction {
/// 5. `[]` System program /// 5. `[]` System program
/// 6. `[]` Sysvar Rent /// 6. `[]` Sysvar Rent
/// 7. `[signer]` Governance authority /// 7. `[signer]` Governance authority
/// 8. `[]` Realm Config /// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 9. `[]` Optional Voter Weight Record /// 9. `[]` Optional Voter Weight Record
CreateGovernance { CreateGovernance {
/// Governance config /// Governance config
@ -138,7 +148,7 @@ pub enum GovernanceInstruction {
/// 8. `[]` System program /// 8. `[]` System program
/// 9. `[]` Sysvar Rent /// 9. `[]` Sysvar Rent
/// 10. `[signer]` Governance authority /// 10. `[signer]` Governance authority
/// 11. `[]` Realm Config /// 11. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 12. `[]` Optional Voter Weight Record /// 12. `[]` Optional Voter Weight Record
CreateProgramGovernance { CreateProgramGovernance {
/// Governance config /// Governance config
@ -162,7 +172,7 @@ pub enum GovernanceInstruction {
/// 5. `[signer]` Governance Authority (Token Owner or Governance Delegate) /// 5. `[signer]` Governance Authority (Token Owner or Governance Delegate)
/// 6. `[signer]` Payer /// 6. `[signer]` Payer
/// 7. `[]` System program /// 7. `[]` System program
/// 8. `[]` Realm Config /// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 9. `[]` Optional Voter Weight Record /// 9. `[]` Optional Voter Weight Record
CreateProposal { CreateProposal {
#[allow(dead_code)] #[allow(dead_code)]
@ -295,7 +305,7 @@ pub enum GovernanceInstruction {
/// Note: In the current version only Council veto is supported /// Note: In the current version only Council veto is supported
/// 8. `[signer]` Payer /// 8. `[signer]` Payer
/// 9. `[]` System program /// 9. `[]` System program
/// 10. `[]` Realm Config /// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Voter Weight Record /// 11. `[]` Optional Voter Weight Record
/// 12. `[]` Optional Max Voter Weight Record /// 12. `[]` Optional Max Voter Weight Record
CastVote { CastVote {
@ -311,7 +321,7 @@ pub enum GovernanceInstruction {
/// 2. `[writable]` Proposal account /// 2. `[writable]` Proposal account
/// 3. `[writable]` TokenOwnerRecord of the Proposal owner /// 3. `[writable]` TokenOwnerRecord of the Proposal owner
/// 4. `[]` Governing Token Mint /// 4. `[]` Governing Token Mint
/// 5. `[]` Realm Config /// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 6. `[]` Optional Max Voter Weight Record /// 6. `[]` Optional Max Voter Weight Record
FinalizeVote {}, FinalizeVote {},
@ -355,7 +365,7 @@ pub enum GovernanceInstruction {
/// 7. `[]` System program /// 7. `[]` System program
/// 8. `[]` Sysvar Rent /// 8. `[]` Sysvar Rent
/// 8. `[signer]` Governance authority /// 8. `[signer]` Governance authority
/// 9. `[]` Realm Config /// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 10. `[]` Optional Voter Weight Record /// 10. `[]` Optional Voter Weight Record
CreateMintGovernance { CreateMintGovernance {
#[allow(dead_code)] #[allow(dead_code)]
@ -381,7 +391,7 @@ pub enum GovernanceInstruction {
/// 7. `[]` System program /// 7. `[]` System program
/// 8. `[]` Sysvar Rent /// 8. `[]` Sysvar Rent
/// 9. `[signer]` Governance authority /// 9. `[signer]` Governance authority
/// 10. `[]` Realm Config /// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Voter Weight Record /// 11. `[]` Optional Voter Weight Record
CreateTokenGovernance { CreateTokenGovernance {
#[allow(dead_code)] #[allow(dead_code)]
@ -438,10 +448,14 @@ pub enum GovernanceInstruction {
/// The account will be created with the Realm PDA as its owner /// The account will be created with the Realm PDA as its owner
/// 4. `[]` System /// 4. `[]` System
/// 5. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm] /// 5. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm]
///
/// 6. `[]` Optional Community Voter Weight Addin Program Id /// 6. `[]` Optional Community Voter Weight Addin Program Id
/// 7. `[]` Optional Max 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 { SetRealmConfig {
#[allow(dead_code)] #[allow(dead_code)]
/// Realm config args /// Realm config args
@ -475,6 +489,22 @@ pub enum GovernanceInstruction {
/// 2. `[signer]` Payer /// 2. `[signer]` Payer
/// 3. `[]` System /// 3. `[]` System
CreateNativeTreasury, 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 /// Creates CreateRealm instruction
@ -486,8 +516,9 @@ pub fn create_realm(
community_token_mint: &Pubkey, community_token_mint: &Pubkey,
payer: &Pubkey, payer: &Pubkey,
council_token_mint: Option<Pubkey>, council_token_mint: Option<Pubkey>,
community_voter_weight_addin: Option<Pubkey>, // Accounts Args
max_community_voter_weight_addin: Option<Pubkey>, community_token_config_args: Option<GoverningTokenConfigAccountArgs>,
council_token_config_args: Option<GoverningTokenConfigAccountArgs>,
// Args // Args
name: String, name: String,
min_community_weight_to_create_governance: u64, min_community_weight_to_create_governance: u64,
@ -519,40 +550,22 @@ pub fn create_realm(
false 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 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
};
if use_community_voter_weight_addin || use_max_community_voter_weight_addin {
let realm_config_address = get_realm_config_address(program_id, &realm_address); let realm_config_address = get_realm_config_address(program_id, &realm_address);
accounts.push(AccountMeta::new(realm_config_address, false)); accounts.push(AccountMeta::new(realm_config_address, false));
}
let community_token_config_args =
with_governing_token_config_args(&mut accounts, community_token_config_args);
let council_token_config_args =
with_governing_token_config_args(&mut accounts, council_token_config_args);
let instruction = GovernanceInstruction::CreateRealm { let instruction = GovernanceInstruction::CreateRealm {
config_args: RealmConfigArgs { config_args: RealmConfigArgs {
use_council_mint, use_council_mint,
min_community_weight_to_create_governance, min_community_weight_to_create_governance,
community_mint_max_vote_weight_source, community_mint_max_vote_weight_source,
use_community_voter_weight_addin, community_token_config_args,
use_max_community_voter_weight_addin, council_token_config_args,
}, },
name, name,
}; };
@ -572,7 +585,7 @@ pub fn deposit_governing_tokens(
realm: &Pubkey, realm: &Pubkey,
governing_token_source: &Pubkey, governing_token_source: &Pubkey,
governing_token_owner: &Pubkey, governing_token_owner: &Pubkey,
governing_token_transfer_authority: &Pubkey, governing_token_source_authority: &Pubkey,
payer: &Pubkey, payer: &Pubkey,
// Args // Args
amount: u64, amount: u64,
@ -588,16 +601,19 @@ pub fn deposit_governing_tokens(
let governing_token_holding_address = let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint); 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![ let accounts = vec![
AccountMeta::new_readonly(*realm, false), AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, false), AccountMeta::new(governing_token_holding_address, false),
AccountMeta::new(*governing_token_source, false), AccountMeta::new(*governing_token_source, false),
AccountMeta::new_readonly(*governing_token_owner, true), 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(token_owner_record_address, false),
AccountMeta::new(*payer, true), AccountMeta::new(*payer, true),
AccountMeta::new_readonly(system_program::id(), false), AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false), AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(realm_config_address, false),
]; ];
let instruction = GovernanceInstruction::DepositGoverningTokens { amount }; let instruction = GovernanceInstruction::DepositGoverningTokens { amount };
@ -629,6 +645,8 @@ pub fn withdraw_governing_tokens(
let governing_token_holding_address = let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint); 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![ let accounts = vec![
AccountMeta::new_readonly(*realm, false), AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, 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_readonly(*governing_token_owner, true),
AccountMeta::new(token_owner_record_address, false), AccountMeta::new(token_owner_record_address, false),
AccountMeta::new_readonly(spl_token::id(), false), AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(realm_config_address, false),
]; ];
let instruction = GovernanceInstruction::WithdrawGoverningTokens {}; let instruction = GovernanceInstruction::WithdrawGoverningTokens {};
@ -1355,8 +1374,9 @@ pub fn set_realm_config(
realm_authority: &Pubkey, realm_authority: &Pubkey,
council_token_mint: Option<Pubkey>, council_token_mint: Option<Pubkey>,
payer: &Pubkey, payer: &Pubkey,
community_voter_weight_addin: Option<Pubkey>, // Accounts Args
max_community_voter_weight_addin: Option<Pubkey>, community_token_config_args: Option<GoverningTokenConfigAccountArgs>,
council_token_config_args: Option<GoverningTokenConfigAccountArgs>,
// Args // Args
min_community_weight_to_create_governance: u64, min_community_weight_to_create_governance: u64,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource, 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); let realm_config_address = get_realm_config_address(program_id, realm);
accounts.push(AccountMeta::new(realm_config_address, false)); accounts.push(AccountMeta::new(realm_config_address, false));
let use_community_voter_weight_addin = let community_token_config_args =
if let Some(community_voter_weight_addin) = community_voter_weight_addin { with_governing_token_config_args(&mut accounts, community_token_config_args);
accounts.push(AccountMeta::new_readonly(
community_voter_weight_addin,
false,
));
true
} else {
false
};
let use_max_community_voter_weight_addin = let council_token_config_args =
if let Some(max_community_voter_weight_addin) = max_community_voter_weight_addin { with_governing_token_config_args(&mut accounts, council_token_config_args);
accounts.push(AccountMeta::new_readonly(
max_community_voter_weight_addin,
false,
));
true
} else {
false
};
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 { let instruction = GovernanceInstruction::SetRealmConfig {
config_args: RealmConfigArgs { config_args: RealmConfigArgs {
use_council_mint, use_council_mint,
min_community_weight_to_create_governance, min_community_weight_to_create_governance,
community_mint_max_vote_weight_source, community_mint_max_vote_weight_source,
use_community_voter_weight_addin, community_token_config_args,
use_max_community_voter_weight_addin, council_token_config_args,
}, },
}; };
@ -1536,3 +1538,77 @@ pub fn create_native_treasury(
data: instruction.try_to_vec().unwrap(), 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<AccountMeta>,
governing_token_config_args: Option<GoverningTokenConfigAccountArgs>,
) -> 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,
}
}

View File

@ -19,6 +19,7 @@ mod process_insert_transaction;
mod process_relinquish_vote; mod process_relinquish_vote;
mod process_remove_signatory; mod process_remove_signatory;
mod process_remove_transaction; mod process_remove_transaction;
mod process_revoke_governing_tokens;
mod process_set_governance_config; mod process_set_governance_config;
mod process_set_governance_delegate; mod process_set_governance_delegate;
mod process_set_realm_authority; mod process_set_realm_authority;
@ -48,6 +49,7 @@ use process_insert_transaction::*;
use process_relinquish_vote::*; use process_relinquish_vote::*;
use process_remove_signatory::*; use process_remove_signatory::*;
use process_remove_transaction::*; use process_remove_transaction::*;
use process_revoke_governing_tokens::*;
use process_set_governance_config::*; use process_set_governance_config::*;
use process_set_governance_delegate::*; use process_set_governance_delegate::*;
use process_set_realm_authority::*; use process_set_realm_authority::*;
@ -213,5 +215,9 @@ pub fn process_instruction(
GovernanceInstruction::CreateNativeTreasury {} => { GovernanceInstruction::CreateNativeTreasury {} => {
process_create_native_treasury(program_id, accounts) process_create_native_treasury(program_id, accounts)
} }
GovernanceInstruction::RevokeGoverningTokens { amount } => {
process_revoke_governing_tokens(program_id, accounts, amount)
}
} }
} }

View File

@ -18,6 +18,7 @@ use crate::{
governance::get_governance_data_for_realm, governance::get_governance_data_for_realm,
proposal::get_proposal_data_for_governance_and_governing_mint, 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::{ token_owner_record::{
get_token_owner_record_data_for_proposal_owner, get_token_owner_record_data_for_proposal_owner,
get_token_owner_record_data_for_realm_and_governing_mint, get_token_owner_record_data_for_realm_and_governing_mint,
@ -104,17 +105,14 @@ pub fn process_cast_vote(
.checked_add(1) .checked_add(1)
.unwrap(); .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( let voter_weight = voter_token_owner_record_data.resolve_voter_weight(
program_id, account_info_iter, // voter_weight_record *10
realm_config_info,
account_info_iter, // voter_weight_record 10
realm_info.key,
&realm_data, &realm_data,
&realm_config_data,
VoterWeightAction::CastVote, VoterWeightAction::CastVote,
proposal_info.key, proposal_info.key,
)?; )?;
@ -152,12 +150,11 @@ pub fn process_cast_vote(
} }
let max_voter_weight = proposal_data.resolve_max_voter_weight( 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 account_info_iter, // max_voter_weight_record 11
realm_info.key, realm_info.key,
&realm_data, &realm_data,
&realm_config_data,
vote_governing_token_mint_info,
&vote_kind, &vote_kind,
)?; )?;

View File

@ -21,6 +21,7 @@ use crate::{
ProposalOption, ProposalV2, VoteType, ProposalOption, ProposalV2, VoteType,
}, },
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_realm, token_owner_record::get_token_owner_record_data_for_realm,
vote_record::VoteKind, vote_record::VoteKind,
}, },
@ -82,13 +83,13 @@ pub fn process_create_proposal(
.assert_token_owner_or_delegate_is_signer(governance_authority_info)?; .assert_token_owner_or_delegate_is_signer(governance_authority_info)?;
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(program_id, realm_config_info, realm_info.key)?;
let voter_weight = proposal_owner_record_data.resolve_voter_weight( let voter_weight = proposal_owner_record_data.resolve_voter_weight(
program_id, account_info_iter, // voter_weight_record *11
realm_config_info,
account_info_iter,
realm_info.key,
&realm_data, &realm_data,
&realm_config_data,
VoterWeightAction::CreateProposal, VoterWeightAction::CreateProposal,
governance_info.key, governance_info.key,
)?; )?;

View File

@ -17,7 +17,10 @@ use crate::{
assert_valid_realm_config_args, get_governing_token_holding_address_seeds, assert_valid_realm_config_args, get_governing_token_holding_address_seeds,
get_realm_address_seeds, RealmConfig, RealmConfigArgs, RealmV2, 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, tools::spl_token::create_spl_token_account_signed,
}; };
@ -27,7 +30,7 @@ pub fn process_create_realm(
program_id: &Pubkey, program_id: &Pubkey,
accounts: &[AccountInfo], accounts: &[AccountInfo],
name: String, name: String,
config_args: RealmConfigArgs, realm_config_args: RealmConfigArgs,
) -> ProgramResult { ) -> ProgramResult {
let account_info_iter = &mut accounts.iter(); let account_info_iter = &mut accounts.iter();
@ -46,8 +49,9 @@ pub fn process_create_realm(
return Err(GovernanceError::RealmAlreadyExists.into()); 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( create_spl_token_account_signed(
payer_info, payer_info,
governance_token_holding_info, governance_token_holding_info,
@ -61,7 +65,8 @@ pub fn process_create_realm(
rent, 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_mint_info = next_account_info(account_info_iter)?; // 8
let council_token_holding_info = next_account_info(account_info_iter)?; // 9 let council_token_holding_info = next_account_info(account_info_iter)?; // 9
@ -83,35 +88,27 @@ pub fn process_create_realm(
None 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 { // 11, 12
let community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 10 let community_token_config = resolve_governing_token_config(
Some(*community_voter_weight_addin_info.key) account_info_iter,
} else { &realm_config_args.community_token_config_args,
None )?;
};
let max_community_voter_weight_addin = if config_args.use_max_community_voter_weight_addin { // 13, 14
let max_community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 11 let council_token_config = resolve_governing_token_config(
Some(*max_community_voter_weight_addin_info.key) account_info_iter,
} else { &realm_config_args.council_token_config_args,
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 { let realm_config_data = RealmConfigAccount {
account_type: GovernanceAccountType::RealmConfig, account_type: GovernanceAccountType::RealmConfig,
realm: *realm_info.key, realm: *realm_info.key,
community_voter_weight_addin, community_token_config,
max_community_voter_weight_addin, council_token_config,
council_voter_weight_addin: None, reserved: Reserved110::default(),
council_max_vote_weight_addin: None,
reserved: [0; 128],
}; };
create_and_serialize_account_signed::<RealmConfigAccount>( create_and_serialize_account_signed::<RealmConfigAccount>(
@ -123,8 +120,8 @@ pub fn process_create_realm(
system_info, system_info,
rent, rent,
)?; )?;
}
// Create and serialize Realm
let realm_data = RealmV2 { let realm_data = RealmV2 {
account_type: GovernanceAccountType::RealmV2, account_type: GovernanceAccountType::RealmV2,
community_mint: *governance_token_mint_info.key, community_mint: *governance_token_mint_info.key,
@ -135,12 +132,12 @@ pub fn process_create_realm(
config: RealmConfig { config: RealmConfig {
council_mint: council_token_mint_address, council_mint: council_token_mint_address,
reserved: [0; 6], 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, .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, .min_community_weight_to_create_governance,
use_community_voter_weight_addin: config_args.use_community_voter_weight_addin, legacy1: 0,
use_max_community_voter_weight_addin: config_args.use_max_community_voter_weight_addin, legacy2: 0,
}, },
voting_proposal_count: 0, voting_proposal_count: 0,
reserved_v2: [0; 128], reserved_v2: [0; 128],

View File

@ -14,12 +14,16 @@ use crate::{
state::{ state::{
enums::GovernanceAccountType, enums::GovernanceAccountType,
realm::get_realm_data, realm::get_realm_data,
realm_config::get_realm_config_data_for_realm,
token_owner_record::{ token_owner_record::{
get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds, get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds,
TokenOwnerRecordV2, 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 /// 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_holding_info = next_account_info(account_info_iter)?; // 1
let governing_token_source_info = next_account_info(account_info_iter)?; // 2 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_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 token_owner_record_info = next_account_info(account_info_iter)?; // 5
let payer_info = next_account_info(account_info_iter)?; // 6 let payer_info = next_account_info(account_info_iter)?; // 6
let system_info = next_account_info(account_info_iter)?; // 7 let system_info = next_account_info(account_info_iter)?; // 7
let spl_token_info = next_account_info(account_info_iter)?; // 8 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()?; let rent = Rent::get()?;
@ -52,13 +57,32 @@ pub fn process_deposit_governing_tokens(
governing_token_holding_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_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( transfer_spl_tokens(
governing_token_source_info, governing_token_source_info,
governing_token_holding_info, governing_token_holding_info,
governing_token_transfer_authority_info, governing_token_source_authority_info,
amount, amount,
spl_token_info, 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( let token_owner_record_address_seeds = get_token_owner_record_address_seeds(
realm_info.key, realm_info.key,
@ -68,11 +92,7 @@ pub fn process_deposit_governing_tokens(
if token_owner_record_info.data_is_empty() { 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 // 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_info.is_signer {
if !(governing_token_owner == *governing_token_owner_info.key
&& governing_token_owner_info.is_signer)
{
return Err(GovernanceError::GoverningTokenOwnerMustSign.into()); return Err(GovernanceError::GoverningTokenOwnerMustSign.into());
} }

View File

@ -11,7 +11,7 @@ use solana_program::{
use crate::state::{ use crate::state::{
governance::get_governance_data_for_realm, governance::get_governance_data_for_realm,
proposal::get_proposal_data_for_governance_and_governing_mint, 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, token_owner_record::get_token_owner_record_data_for_proposal_owner, vote_record::VoteKind,
}; };
@ -44,14 +44,15 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P
)?; )?;
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( let max_voter_weight = proposal_data.resolve_max_voter_weight(
program_id,
realm_config_info,
governing_token_mint_info,
account_info_iter, // *6 account_info_iter, // *6
realm_info.key, realm_info.key,
&realm_data, &realm_data,
&realm_config_data,
governing_token_mint_info,
&VoteKind::Electorate, &VoteKind::Electorate,
)?; )?;

View File

@ -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(())
}

View File

@ -13,10 +13,10 @@ use spl_governance_tools::account::create_and_serialize_account_signed;
use crate::{ use crate::{
error::GovernanceError, error::GovernanceError,
state::{ state::{
enums::GovernanceAccountType,
realm::{assert_valid_realm_config_args, get_realm_data_for_authority, RealmConfigArgs}, realm::{assert_valid_realm_config_args, get_realm_data_for_authority, RealmConfigArgs},
realm_config::{ 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,44 +75,36 @@ pub fn process_set_realm_config(
} }
let system_info = next_account_info(account_info_iter)?; // 4 let system_info = next_account_info(account_info_iter)?; // 4
let realm_config_info = next_account_info(account_info_iter)?; // 5 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 { // Setup configs for tokens (plugins and token types)
let community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 6
Some(*community_voter_weight_addin_info.key)
} else {
None
};
let max_community_voter_weight_addin = if realm_config_args.use_max_community_voter_weight_addin // 6, 7
{ let community_token_config = resolve_governing_token_config(
let max_community_voter_weight_addin_info = next_account_info(account_info_iter)?; // 7 account_info_iter,
Some(*max_community_voter_weight_addin_info.key) &realm_config_args.community_token_config_args,
} else { )?;
None
};
// If any of the addins is needed then update or create (if doesn't exist yet) RealmConfigAccount // 8, 9
let update_realm_config = if realm_config_args.use_community_voter_weight_addin let council_token_config = resolve_governing_token_config(
|| realm_config_args.use_max_community_voter_weight_addin account_info_iter,
{ &realm_config_args.council_token_config_args,
// We need the payer to pay for the new account if it's created )?;
let payer_info = next_account_info(account_info_iter)?; // 8
// If RealmConfigAccount doesn't exist yet then create it 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() { if realm_config_info.data_is_empty() {
let realm_config_data = RealmConfigAccount { // For older Realms (pre v3) RealmConfigAccount might not exist yet and we have to create it
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],
};
// We need the payer to pay for the new account if it's created
let payer_info = next_account_info(account_info_iter)?; // 10
let rent = Rent::get()?; let rent = Rent::get()?;
create_and_serialize_account_signed::<RealmConfigAccount>( create_and_serialize_account_signed::<RealmConfigAccount>(
@ -124,38 +116,19 @@ pub fn process_set_realm_config(
system_info, system_info,
&rent, &rent,
)?; )?;
false // RealmConfigAccount didn't exist and was created
} else { } else {
true // RealmConfigAccount existed before and needs to be updated
}
} 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())?; 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_data.config.community_mint_max_vote_weight_source =
realm_config_args.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_data.config.min_community_weight_to_create_governance =
realm_config_args.min_community_weight_to_create_governance; realm_config_args.min_community_weight_to_create_governance;
realm_data.config.use_community_voter_weight_addin = realm_data.config.legacy1 = 0;
realm_config_args.use_community_voter_weight_addin; realm_data.config.legacy2 = 0;
realm_data.config.use_max_community_voter_weight_addin =
realm_config_args.use_max_community_voter_weight_addin;
realm_data.serialize(&mut *realm_info.data.borrow_mut())?; realm_data.serialize(&mut *realm_info.data.borrow_mut())?;

View File

@ -10,6 +10,7 @@ use crate::{
error::GovernanceError, error::GovernanceError,
state::{ state::{
realm::{get_realm_address_seeds, get_realm_data}, realm::{get_realm_address_seeds, get_realm_data},
realm_config::get_realm_config_data_for_realm,
token_owner_record::{ token_owner_record::{
get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds, get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds,
}, },
@ -30,6 +31,7 @@ pub fn process_withdraw_governing_tokens(
let governing_token_owner_info = next_account_info(account_info_iter)?; // 3 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 token_owner_record_info = next_account_info(account_info_iter)?; // 4
let spl_token_info = next_account_info(account_info_iter)?; // 5 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 { if !governing_token_owner_info.is_signer {
return Err(GovernanceError::GoverningTokenOwnerMustSign.into()); return Err(GovernanceError::GoverningTokenOwnerMustSign.into());
@ -45,6 +47,11 @@ pub fn process_withdraw_governing_tokens(
governing_token_holding_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_withdraw_governing_token(&realm_data, &governing_token_mint)?;
let token_owner_record_address_seeds = get_token_owner_record_address_seeds( let token_owner_record_address_seeds = get_token_owner_record_address_seeds(
realm_info.key, realm_info.key,
&governing_token_mint, &governing_token_mint,

View File

@ -3,7 +3,7 @@
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
/// Defines all Governance accounts types /// Defines all Governance accounts types
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum GovernanceAccountType { pub enum GovernanceAccountType {
/// Default uninitialized account state /// Default uninitialized account state
Uninitialized, Uninitialized,
@ -96,7 +96,7 @@ impl Default for GovernanceAccountType {
} }
/// What state a Proposal is in /// What state a Proposal is in
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum ProposalState { pub enum ProposalState {
/// Draft - Proposal enters Draft state when it's created /// Draft - Proposal enters Draft state when it's created
Draft, Draft,
@ -141,7 +141,7 @@ impl Default for ProposalState {
/// The type of the vote threshold used to resolve a vote on a Proposal /// 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 /// 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 { pub enum VoteThreshold {
/// Voting threshold of Yes votes in % required to tip the vote (Approval Quorum) /// 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 /// 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. /// The type of vote tipping to use on a Proposal.
/// ///
/// Vote tipping means that under some conditions voting will complete early. /// 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 { pub enum VoteTipping {
/// Tip when there is no way for another option to win and the vote threshold /// Tip when there is no way for another option to win and the vote threshold
/// has been reached. This ignores voters withdrawing their votes. /// has been reached. This ignores voters withdrawing their votes.
@ -192,7 +192,7 @@ pub enum VoteTipping {
} }
/// The status of instruction execution /// The status of instruction execution
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum TransactionExecutionStatus { pub enum TransactionExecutionStatus {
/// Transaction was not executed yet /// Transaction was not executed yet
None, None,
@ -205,7 +205,7 @@ pub enum TransactionExecutionStatus {
} }
/// Transaction execution flags defining how instructions are executed for a Proposal /// 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 { pub enum InstructionExecutionFlags {
/// No execution flags are specified /// No execution flags are specified
/// Instructions can be executed individually, in any order, as soon as they hold_up time expires /// 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 /// 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 /// 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 { pub enum MintMaxVoteWeightSource {
/// Fraction (10^10 precision) of the governing mint supply is used as max vote weight /// 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 /// The default is 100% (10^10) to use all available mint supply for voting

View File

@ -21,7 +21,7 @@ use spl_governance_tools::{
}; };
/// Governance config /// Governance config
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct GovernanceConfig { pub struct GovernanceConfig {
/// The type of the vote threshold used for community vote /// The type of the vote threshold used for community vote
/// Note: In the current version only YesVotePercentage and Disabled thresholds are supported /// Note: In the current version only YesVotePercentage and Disabled thresholds are supported
@ -56,7 +56,7 @@ pub struct GovernanceConfig {
} }
/// Governance Account /// Governance Account
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct GovernanceV2 { pub struct GovernanceV2 {
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -18,7 +18,7 @@ use solana_program::{
/// Governance Realm Account /// Governance Realm Account
/// Account PDA seeds" ['governance', name] /// Account PDA seeds" ['governance', name]
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmV1 { pub struct RealmV1 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -53,7 +53,7 @@ impl IsInitialized for RealmV1 {
/// Governance Token Owner Record /// Governance Token Owner Record
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ] /// 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 { pub struct TokenOwnerRecordV1 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -101,7 +101,7 @@ impl IsInitialized for TokenOwnerRecordV1 {
} }
/// Governance Account /// Governance Account
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct GovernanceV1 { pub struct GovernanceV1 {
/// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -171,7 +171,7 @@ impl IsInitialized for GovernanceV1 {
} }
/// Governance Proposal /// Governance Proposal
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ProposalV1 { pub struct ProposalV1 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -260,7 +260,7 @@ impl IsInitialized for ProposalV1 {
} }
/// Account PDA seeds: ['governance', proposal, signatory] /// Account PDA seeds: ['governance', proposal, signatory]
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct SignatoryRecordV1 { pub struct SignatoryRecordV1 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -282,7 +282,7 @@ impl IsInitialized for SignatoryRecordV1 {
} }
/// Proposal instruction V1 /// Proposal instruction V1
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ProposalInstructionV1 { pub struct ProposalInstructionV1 {
/// Governance Account type /// Governance Account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -315,7 +315,7 @@ impl IsInitialized for ProposalInstructionV1 {
} }
/// Vote with number of votes /// Vote with number of votes
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum VoteWeightV1 { pub enum VoteWeightV1 {
/// Yes vote /// Yes vote
Yes(u64), Yes(u64),
@ -325,7 +325,7 @@ pub enum VoteWeightV1 {
} }
/// Proposal VoteRecord /// Proposal VoteRecord
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct VoteRecordV1 { pub struct VoteRecordV1 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -6,7 +6,7 @@ use spl_governance_tools::account::AccountMaxSize;
/// Treasury account /// 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 /// 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 {} pub struct NativeTreasury {}
impl AccountMaxSize for NativeTreasury { impl AccountMaxSize for NativeTreasury {

View File

@ -10,7 +10,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize};
use crate::state::enums::GovernanceAccountType; use crate::state::enums::GovernanceAccountType;
/// Program metadata account. It stores information about the particular SPL-Governance program instance /// 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 { pub struct ProgramMetadata {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -30,7 +30,6 @@ use crate::{
governance::GovernanceConfig, governance::GovernanceConfig,
proposal_transaction::ProposalTransactionV2, proposal_transaction::ProposalTransactionV2,
realm::RealmV2, realm::RealmV2,
realm_config::get_realm_config_data_for_realm,
vote_record::Vote, vote_record::Vote,
vote_record::VoteKind, vote_record::VoteKind,
}, },
@ -38,8 +37,10 @@ use crate::{
}; };
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use crate::state::realm_config::RealmConfigAccount;
/// Proposal option vote result /// Proposal option vote result
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum OptionVoteResult { pub enum OptionVoteResult {
/// Vote on the option is not resolved yet /// Vote on the option is not resolved yet
None, None,
@ -52,7 +53,7 @@ pub enum OptionVoteResult {
} }
/// Proposal Option /// Proposal Option
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ProposalOption { pub struct ProposalOption {
/// Option label /// Option label
pub label: String, pub label: String,
@ -74,7 +75,7 @@ pub struct ProposalOption {
} }
/// Proposal vote type /// Proposal vote type
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum VoteType { pub enum VoteType {
/// Single choice vote with mutually exclusive choices /// Single choice vote with mutually exclusive choices
/// In the SingeChoice mode there can ever be a single winner /// In the SingeChoice mode there can ever be a single winner
@ -103,7 +104,7 @@ pub enum VoteType {
} }
/// Governance Proposal /// Governance Proposal
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ProposalV2 { pub struct ProposalV2 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -472,30 +473,28 @@ impl ProposalV2 {
max_voter_weight.max(total_vote_weight) 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)] #[allow(clippy::too_many_arguments)]
pub fn resolve_max_voter_weight( pub fn resolve_max_voter_weight(
&mut self, &mut self,
program_id: &Pubkey,
realm_config_info: &AccountInfo,
vote_governing_token_mint_info: &AccountInfo,
account_info_iter: &mut Iter<AccountInfo>, account_info_iter: &mut Iter<AccountInfo>,
realm: &Pubkey, realm: &Pubkey,
realm_data: &RealmV2, realm_data: &RealmV2,
realm_config_data: &RealmConfigAccount,
vote_governing_token_mint_info: &AccountInfo,
vote_kind: &VoteKind, vote_kind: &VoteKind,
) -> Result<u64, ProgramError> { ) -> Result<u64, ProgramError> {
// if the realm uses addin for max community voter weight then use the externally provided max weight // if the Realm is configured to use max voter weight for the given voting governing_token_mint then use the externally provided max_voter_weight
if realm_data.config.use_max_community_voter_weight_addin // instead of the supply based max
&& realm_data.community_mint == *vote_governing_token_mint_info.key 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_info = next_account_info(account_info_iter)?;
let max_voter_weight_record_data = let max_voter_weight_record_data =
get_max_voter_weight_record_data_for_realm_and_governing_token_mint( 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, max_voter_weight_record_info,
realm, realm,
vote_governing_token_mint_info.key, vote_governing_token_mint_info.key,
@ -1166,8 +1165,8 @@ mod test {
config: RealmConfig { config: RealmConfig {
council_mint: Some(Pubkey::new_unique()), council_mint: Some(Pubkey::new_unique()),
reserved: [0; 6], reserved: [0; 6],
use_community_voter_weight_addin: false, legacy1: 0,
use_max_community_voter_weight_addin: false, legacy2: 0,
community_mint_max_vote_weight_source: community_mint_max_vote_weight_source:
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,

View File

@ -25,7 +25,7 @@ use solana_program::{
use spl_governance_tools::account::{get_account_data, AccountMaxSize}; 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 /// 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 { pub struct InstructionData {
/// Pubkey of the instruction processor that executes this instruction /// Pubkey of the instruction processor that executes this instruction
pub program_id: Pubkey, pub program_id: Pubkey,
@ -36,7 +36,7 @@ pub struct InstructionData {
} }
/// Account metadata used to define Instructions /// Account metadata used to define Instructions
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct AccountMetaData { pub struct AccountMetaData {
/// An account's public key /// An account's public key
pub pubkey: Pubkey, pub pubkey: Pubkey,
@ -83,7 +83,7 @@ impl From<&InstructionData> for Instruction {
} }
/// Account for an instruction to be executed for Proposal /// 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 { pub struct ProposalTransactionV2 {
/// Governance Account type /// Governance Account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -21,14 +21,17 @@ use crate::{
state::{ state::{
enums::{GovernanceAccountType, MintMaxVoteWeightSource}, enums::{GovernanceAccountType, MintMaxVoteWeightSource},
legacy::RealmV1, legacy::RealmV1,
realm_config::GoverningTokenType,
token_owner_record::get_token_owner_record_data_for_realm, token_owner_record::get_token_owner_record_data_for_realm,
vote_record::VoteKind, vote_record::VoteKind,
}, },
PROGRAM_AUTHORITY_SEED, PROGRAM_AUTHORITY_SEED,
}; };
use crate::state::realm_config::get_realm_config_data_for_realm;
/// Realm Config instruction args /// Realm Config instruction args
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmConfigArgs { pub struct RealmConfigArgs {
/// Indicates whether council_mint should be used /// Indicates whether council_mint should be used
/// If yes then council_mint account must also be passed to the instruction /// 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 /// The source used for community mint max vote weight source
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
/// Indicates whether an external addin program should be used to provide community voters weights /// Community token config args
/// If yes then the voters weight program account must be passed to the instruction pub community_token_config_args: GoverningTokenConfigArgs,
pub use_community_voter_weight_addin: bool,
/// 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 /// 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<Pubkey>,
/// 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<Pubkey>,
/// Governing token type defines how the token is used for governance power
pub token_type: GoverningTokenType,
} }
/// SetRealmAuthority instruction action /// SetRealmAuthority instruction action
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum SetRealmAuthorityAction { pub enum SetRealmAuthorityAction {
/// Sets realm authority without any checks /// Sets realm authority without any checks
/// Uncheck option allows to set the realm authority to non governance accounts /// Uncheck option allows to set the realm authority to non governance accounts
@ -66,13 +97,17 @@ pub enum SetRealmAuthorityAction {
} }
/// Realm Config defining Realm parameters. /// Realm Config defining Realm parameters.
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmConfig { pub struct RealmConfig {
/// Indicates whether an external addin program should be used to provide voters weights for the community mint /// Legacy field introdcued and used in V2 as use_community_voter_weight_addin: bool
pub 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 /// Legacy field introdcued and used in V2 as use_max_community_voter_weight_addin: bool
pub 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 /// Reserved space for future versions
pub reserved: [u8; 6], pub reserved: [u8; 6],
@ -89,7 +124,7 @@ pub struct RealmConfig {
/// Governance Realm Account /// Governance Realm Account
/// Account PDA seeds" ['governance', name] /// Account PDA seeds" ['governance', name]
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmV2 { pub struct RealmV2 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -251,13 +286,13 @@ impl RealmV2 {
token_owner_record_data.assert_token_owner_or_delegate_is_signer(create_authority_info)?; 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_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( let voter_weight = token_owner_record_data.resolve_voter_weight(
program_id,
realm_config_info,
account_info_iter, account_info_iter,
realm,
self, self,
&realm_config_data,
VoterWeightAction::CreateGovernance, VoterWeightAction::CreateGovernance,
realm, realm,
)?; )?;
@ -399,8 +434,10 @@ pub fn get_governing_token_holding_address(
} }
/// Asserts given realm config args are correct /// Asserts given realm config args are correct
pub fn assert_valid_realm_config_args(config_args: &RealmConfigArgs) -> Result<(), ProgramError> { pub fn assert_valid_realm_config_args(
match config_args.community_mint_max_vote_weight_source { realm_config_args: &RealmConfigArgs,
) -> Result<(), ProgramError> {
match realm_config_args.community_mint_max_vote_weight_source {
MintMaxVoteWeightSource::SupplyFraction(fraction) => { MintMaxVoteWeightSource::SupplyFraction(fraction) => {
if !(1..=MintMaxVoteWeightSource::SUPPLY_FRACTION_BASE).contains(&fraction) { if !(1..=MintMaxVoteWeightSource::SUPPLY_FRACTION_BASE).contains(&fraction) {
return Err(GovernanceError::InvalidMaxVoteWeightSupplyFraction.into()); return Err(GovernanceError::InvalidMaxVoteWeightSupplyFraction.into());
@ -433,8 +470,8 @@ mod test {
name: "test-realm".to_string(), name: "test-realm".to_string(),
config: RealmConfig { config: RealmConfig {
council_mint: Some(Pubkey::new_unique()), council_mint: Some(Pubkey::new_unique()),
use_community_voter_weight_addin: false, legacy1: 0,
use_max_community_voter_weight_addin: false, legacy2: 0,
reserved: [0; 6], reserved: [0; 6],
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::Absolute(100), community_mint_max_vote_weight_source: MintMaxVoteWeightSource::Absolute(100),
min_community_weight_to_create_governance: 10, min_community_weight_to_create_governance: 10,
@ -450,7 +487,7 @@ mod test {
} }
/// Realm Config instruction args /// Realm Config instruction args
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmConfigArgsV1 { pub struct RealmConfigArgsV1 {
/// Indicates whether council_mint should be used /// Indicates whether council_mint should be used
/// If yes then council_mint account must also be passed to the instruction /// 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 /// Instructions supported by the Governance program
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum GovernanceInstructionV1 { pub enum GovernanceInstructionV1 {
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint /// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
CreateRealm { CreateRealm {
@ -495,8 +532,8 @@ mod test {
min_community_weight_to_create_governance: 100, min_community_weight_to_create_governance: 100,
community_mint_max_vote_weight_source: community_mint_max_vote_weight_source:
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
use_community_voter_weight_addin: false, community_token_config_args: GoverningTokenConfigArgs::default(),
use_max_community_voter_weight_addin: false, council_token_config_args: GoverningTokenConfigArgs::default(),
}, },
}; };

View File

@ -1,4 +1,7 @@
//! RealmConfig account //! RealmConfig account
use std::slice::Iter;
use solana_program::account_info::next_account_info;
use solana_program::{ use solana_program::{
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, 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::{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<Pubkey>,
/// Plugin providing max voter weight for the governing token
pub max_voter_weight_addin: Option<Pubkey>,
/// 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 /// RealmConfig account
/// The account is an optional extension to RealmConfig stored on Realm 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 { pub struct RealmConfigAccount {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -20,23 +104,14 @@ pub struct RealmConfigAccount {
/// The realm the config belong to /// The realm the config belong to
pub realm: Pubkey, pub realm: Pubkey,
/// Addin providing voter weights for community token /// Community token config
pub community_voter_weight_addin: Option<Pubkey>, pub community_token_config: GoverningTokenConfig,
/// Addin providing max vote weight for community token /// Council token config
/// Note: This field is not implemented in the current version pub council_token_config: GoverningTokenConfig,
pub max_community_voter_weight_addin: Option<Pubkey>,
/// Addin providing voter weights for council token
/// Note: This field is not implemented in the current version
pub council_voter_weight_addin: Option<Pubkey>,
/// 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<Pubkey>,
/// Reserved /// Reserved
pub reserved: [u8; 128], pub reserved: Reserved110,
} }
impl AccountMaxSize for RealmConfigAccount { 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 /// Deserializes RealmConfig account and checks owner program
pub fn get_realm_config_data( pub fn get_realm_config_data(
program_id: &Pubkey, program_id: &Pubkey,
@ -59,18 +226,40 @@ pub fn get_realm_config_data(
get_account_data::<RealmConfigAccount>(program_id, realm_config_info) get_account_data::<RealmConfigAccount>(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( pub fn get_realm_config_data_for_realm(
program_id: &Pubkey, program_id: &Pubkey,
realm_config_info: &AccountInfo, realm_config_info: &AccountInfo,
realm: &Pubkey, realm: &Pubkey,
) -> Result<RealmConfigAccount, ProgramError> { ) -> Result<RealmConfigAccount, ProgramError> {
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_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)?; let realm_config_data = get_realm_config_data(program_id, realm_config_info)?;
if realm_config_data.realm != *realm { if realm_config_data.realm != *realm {
return Err(GovernanceError::InvalidRealmConfigForRealm.into()); return Err(GovernanceError::InvalidRealmConfigForRealm.into());
} }
realm_config_data
};
Ok(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 { 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 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<AccountInfo>,
governing_token_config_args: &GoverningTokenConfigArgs,
) -> Result<GoverningTokenConfig, ProgramError> {
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)] #[cfg(test)]
mod test { mod test {
@ -94,11 +309,19 @@ mod test {
let realm_config = RealmConfigAccount { let realm_config = RealmConfigAccount {
account_type: GovernanceAccountType::RealmV2, account_type: GovernanceAccountType::RealmV2,
realm: Pubkey::new_unique(), realm: Pubkey::new_unique(),
community_voter_weight_addin: Some(Pubkey::new_unique()), community_token_config: GoverningTokenConfig {
max_community_voter_weight_addin: Some(Pubkey::new_unique()), voter_weight_addin: Some(Pubkey::new_unique()),
council_voter_weight_addin: Some(Pubkey::new_unique()), max_voter_weight_addin: Some(Pubkey::new_unique()),
council_max_vote_weight_addin: Some(Pubkey::new_unique()), token_type: GoverningTokenType::Liquid,
reserved: [0; 128], 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(); let size = realm_config.try_to_vec().unwrap().len();

View File

@ -15,7 +15,7 @@ use crate::{error::GovernanceError, PROGRAM_AUTHORITY_SEED};
use crate::state::{enums::GovernanceAccountType, legacy::SignatoryRecordV1}; use crate::state::{enums::GovernanceAccountType, legacy::SignatoryRecordV1};
/// Account PDA seeds: ['governance', proposal, signatory] /// Account PDA seeds: ['governance', proposal, signatory]
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct SignatoryRecordV2 { pub struct SignatoryRecordV2 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -10,7 +10,7 @@ use crate::{
error::GovernanceError, error::GovernanceError,
state::{ state::{
enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1, enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1,
realm::RealmV2, realm_config::get_realm_config_data_for_realm, realm::RealmV2,
}, },
PROGRAM_AUTHORITY_SEED, PROGRAM_AUTHORITY_SEED,
}; };
@ -26,9 +26,11 @@ use solana_program::{
use spl_governance_addin_api::voter_weight::VoterWeightAction; use spl_governance_addin_api::voter_weight::VoterWeightAction;
use spl_governance_tools::account::{get_account_data, AccountMaxSize}; use spl_governance_tools::account::{get_account_data, AccountMaxSize};
use crate::state::realm_config::RealmConfigAccount;
/// Governance Token Owner Record /// Governance Token Owner Record
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ] /// 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 { pub struct TokenOwnerRecordV2 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,
@ -189,25 +191,22 @@ impl TokenOwnerRecordV2 {
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn resolve_voter_weight( pub fn resolve_voter_weight(
&self, &self,
program_id: &Pubkey,
realm_config_info: &AccountInfo,
account_info_iter: &mut Iter<AccountInfo>, account_info_iter: &mut Iter<AccountInfo>,
realm: &Pubkey,
realm_data: &RealmV2, realm_data: &RealmV2,
realm_config_data: &RealmConfigAccount,
weight_action: VoterWeightAction, weight_action: VoterWeightAction,
weight_action_target: &Pubkey, weight_action_target: &Pubkey,
) -> Result<u64, ProgramError> { ) -> Result<u64, ProgramError> {
// if the realm uses addin for community voter weight then use the externally provided weight // if the Realm is configured to use voter weight plugin for our governing_token_mint then use the externally provided voter_weight
if realm_data.config.use_community_voter_weight_addin // instead of governing_token_deposit_amount
&& realm_data.community_mint == self.governing_token_mint 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 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( 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, voter_weight_record_info,
self, self,
)?; )?;

View File

@ -25,10 +25,10 @@ use crate::state::{
/// Voter choice for a proposal option /// Voter choice for a proposal option
/// In the current version only 1) Single choice and 2) Multiple choices proposals are supported /// 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 /// 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 { pub struct VoteChoice {
/// The rank given to the choice by voter /// 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, pub rank: u8,
/// The voter's weight percentage given by the voter to the choice /// The voter's weight percentage given by the voter to the choice
@ -47,7 +47,7 @@ impl VoteChoice {
} }
/// User's vote /// User's vote
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum Vote { pub enum Vote {
/// Vote approving choices /// Vote approving choices
Approve(Vec<VoteChoice>), Approve(Vec<VoteChoice>),
@ -64,7 +64,7 @@ pub enum Vote {
} }
/// VoteKind defines the type of the vote being cast /// 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 { pub enum VoteKind {
/// Electorate vote is cast by the voting population identified by governing_token_mint /// Electorate vote is cast by the voting population identified by governing_token_mint
/// Approve, Deny and Abstain votes are Electorate votes /// Approve, Deny and Abstain votes are Electorate votes
@ -83,7 +83,7 @@ pub fn get_vote_kind(vote: &Vote) -> VoteKind {
} }
/// Proposal VoteRecord /// Proposal VoteRecord
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] #[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct VoteRecordV2 { pub struct VoteRecordV2 {
/// Governance account type /// Governance account type
pub account_type: GovernanceAccountType, pub account_type: GovernanceAccountType,

View File

@ -121,6 +121,37 @@ pub fn transfer_spl_tokens<'a>(
Ok(()) 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 /// Transfers SPL Tokens from a token account owned by the provided PDA authority with seeds
pub fn transfer_spl_tokens_signed<'a>( pub fn transfer_spl_tokens_signed<'a>(
source_info: &AccountInfo<'a>, source_info: &AccountInfo<'a>,
@ -170,6 +201,55 @@ pub fn transfer_spl_tokens_signed<'a>(
Ok(()) 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 /// 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> { pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
if account_info.data_is_empty() { if account_info.data_is_empty() {
@ -195,6 +275,11 @@ pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<(
Ok(()) 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 /// 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> { pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), ProgramError> {
if mint_info.data_is_empty() { 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 // 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]; let is_initialized = array_ref![data, 45, 1];
if is_initialized == &[0] { if is_initialized == &[0] {
@ -220,6 +305,11 @@ pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), Pro
Ok(()) 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 /// Computationally cheap method to get mint from a token account
/// It reads mint without deserializing full account data /// It reads mint without deserializing full account data
pub fn get_spl_token_mint(token_account_info: &AccountInfo) -> Result<Pubkey, ProgramError> { pub fn get_spl_token_mint(token_account_info: &AccountInfo) -> Result<Pubkey, ProgramError> {

View File

@ -8,7 +8,10 @@ use solana_program_test::tokio;
use program_test::*; use program_test::*;
use spl_governance::{ use spl_governance::{
error::GovernanceError, error::GovernanceError,
state::enums::{ProposalState, VoteThreshold, VoteTipping}, state::{
enums::{ProposalState, VoteThreshold, VoteTipping},
vote_record::Vote,
},
}; };
#[tokio::test] #[tokio::test]
@ -1243,3 +1246,52 @@ async fn test_cast_council_vote() {
proposal_account.vote_threshold 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());
}

View File

@ -542,3 +542,41 @@ async fn test_create_proposal_with_disabled_community_vote_error() {
GovernanceError::GoverningTokenMintNotAllowedToVote.into() 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());
}

View File

@ -5,12 +5,9 @@ use solana_program_test::*;
mod program_test; mod program_test;
use program_test::*; use program_test::*;
use spl_governance::state::{ use spl_governance::state::{enums::MintMaxVoteWeightSource, realm::get_realm_address};
enums::MintMaxVoteWeightSource,
realm::{get_realm_address, RealmConfigArgs},
};
use self::args::SetRealmConfigArgs; use crate::program_test::args::RealmSetupArgs;
#[tokio::test] #[tokio::test]
async fn test_create_realm() { async fn test_create_realm() {
@ -33,23 +30,16 @@ async fn test_create_realm_with_non_default_config() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_new().await; let mut governance_test = GovernanceProgramTest::start_new().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs {
use_council_mint: false, use_council_mint: false,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(1), community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(1),
min_community_weight_to_create_governance: 10, min_community_weight_to_create_governance: 1,
use_community_voter_weight_addin: false, ..Default::default()
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,
}; };
// Act // Act
let realm_cookie = governance_test let realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
// Assert // Assert

View File

@ -7,7 +7,12 @@ mod program_test;
use program_test::*; use program_test::*;
use solana_sdk::signature::{Keypair, Signer}; 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] #[tokio::test]
async fn test_deposit_initial_community_tokens() { 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
assert_eq!(error, GovernanceError::GoverningTokenOwnerMustSign.into()); 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] #[tokio::test]
async fn test_deposit_community_tokens_with_malicious_holding_account_error() { 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() 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());
}

View File

@ -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);
}

View File

@ -8,10 +8,10 @@ mod program_test;
use program_test::*; use program_test::*;
use spl_governance::{ use spl_governance::{
error::GovernanceError, 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] #[tokio::test]
async fn test_set_realm_config() { 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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs::default();
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,
};
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs::default();
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,
};
// Act // Act
let err = governance_test let err = governance_test
.set_realm_config_using_instruction( .set_realm_config_using_instruction(
&mut realm_cookie, &mut realm_cookie,
&set_realm_config_args, &realm_setup_args,
|i| i.accounts[1].is_signer = false, |i| i.accounts[1].is_signer = false,
Some(&[]), 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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs::default();
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,
};
governance_test governance_test
.set_realm_authority(&realm_cookie, None) .set_realm_authority(&realm_cookie, None)
@ -121,7 +82,7 @@ async fn test_set_realm_config_with_no_authority_error() {
let err = governance_test let err = governance_test
.set_realm_config_using_instruction( .set_realm_config_using_instruction(
&mut realm_cookie, &mut realm_cookie,
&set_realm_config_args, &realm_setup_args,
|i| i.accounts[1].is_signer = false, |i| i.accounts[1].is_signer = false,
Some(&[]), 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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs::default();
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_cookie2 = governance_test.with_realm().await; let realm_cookie2 = governance_test.with_realm().await;
@ -163,7 +111,7 @@ async fn test_set_realm_config_with_invalid_authority_error() {
// Act // Act
let err = governance_test let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.err() .err()
.unwrap(); .unwrap();
@ -179,24 +127,12 @@ async fn test_set_realm_config_with_remove_council() {
let mut realm_cookie = governance_test.with_realm().await; let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let mut realm_setup_args = RealmSetupArgs::default();
use_council_mint: false, realm_setup_args.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,
};
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let realm_setup_args = RealmSetupArgs::default();
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,
};
// Try to replace council mint // Try to replace council mint
realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique()); realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique());
// Act // Act
let err = governance_test let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.err() .err()
.unwrap(); .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 mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs { let mut realm_setup_args = RealmSetupArgs::default();
use_council_mint: false, realm_setup_args.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,
};
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Try to restore council mint after removing it // 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()); realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique());
// Act // Act
let err = governance_test let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.err() .err()
.unwrap(); .unwrap();
@ -292,3 +203,134 @@ async fn test_set_realm_config_with_council_restore_error() {
GovernanceError::RealmCouncilMintChangeIsNotSupported.into() 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
);
}

View File

@ -9,10 +9,13 @@ use program_test::*;
use solana_sdk::signature::Signer; use solana_sdk::signature::Signer;
use spl_governance::{ use spl_governance::{
error::GovernanceError, instruction::withdraw_governing_tokens, error::GovernanceError,
state::token_owner_record::get_token_owner_record_address, instruction::withdraw_governing_tokens,
state::{realm_config::GoverningTokenType, token_owner_record::get_token_owner_record_address},
}; };
use crate::program_test::args::RealmSetupArgs;
#[tokio::test] #[tokio::test]
async fn test_withdraw_community_tokens() { async fn test_withdraw_community_tokens() {
// Arrange // Arrange
@ -420,3 +423,65 @@ async fn test_withdraw_governing_tokens_after_proposal_cancelled() {
source_account.amount 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);
}

View File

@ -1,28 +1,70 @@
use solana_program::pubkey::Pubkey; use spl_governance::state::{
use spl_governance::state::{enums::MintMaxVoteWeightSource, realm::RealmConfigArgs}; enums::MintMaxVoteWeightSource, realm::GoverningTokenConfigAccountArgs,
#[derive(Clone, Debug, PartialEq)]
pub struct SetRealmConfigArgs {
pub realm_config_args: RealmConfigArgs,
pub community_voter_weight_addin: Option<Pubkey>,
pub max_community_voter_weight_addin: Option<Pubkey>,
}
impl Default for SetRealmConfigArgs {
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,
}; };
#[derive(Clone, Debug, PartialEq)]
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 RealmSetupArgs {
fn default() -> Self {
Self { Self {
realm_config_args, use_council_mint: true,
community_voter_weight_addin: None, community_token_config_args: GoverningTokenConfigAccountArgs::default(),
max_community_voter_weight_addin: None, 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,
};
}

View File

@ -32,7 +32,7 @@ pub struct RealmCookie {
pub realm_authority: Option<Keypair>, pub realm_authority: Option<Keypair>,
pub realm_config: Option<RealmConfigCookie>, pub realm_config: RealmConfigCookie,
} }
#[derive(Debug)] #[derive(Debug)]

View File

@ -20,9 +20,9 @@ use spl_governance::{
create_native_treasury, create_program_governance, create_proposal, create_realm, create_native_treasury, create_program_governance, create_proposal, create_realm,
create_token_governance, create_token_owner_record, deposit_governing_tokens, create_token_governance, create_token_owner_record, deposit_governing_tokens,
execute_transaction, finalize_vote, flag_transaction_error, insert_transaction, execute_transaction, finalize_vote, flag_transaction_error, insert_transaction,
relinquish_vote, remove_signatory, remove_transaction, set_governance_config, relinquish_vote, remove_signatory, remove_transaction, revoke_governing_tokens,
set_governance_delegate, set_realm_authority, set_realm_config, sign_off_proposal, set_governance_config, set_governance_delegate, set_realm_authority, set_realm_config,
upgrade_program_metadata, withdraw_governing_tokens, sign_off_proposal, upgrade_program_metadata, withdraw_governing_tokens,
}, },
processor::process_instruction, processor::process_instruction,
state::{ state::{
@ -41,10 +41,12 @@ use spl_governance::{
get_proposal_transaction_address, InstructionData, ProposalTransactionV2, get_proposal_transaction_address, InstructionData, ProposalTransactionV2,
}, },
realm::{ realm::{
get_governing_token_holding_address, get_realm_address, RealmConfig, RealmConfigArgs, get_governing_token_holding_address, get_realm_address,
RealmV2, SetRealmAuthorityAction, 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}, signatory_record::{get_signatory_record_address, SignatoryRecordV2},
token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2}, token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2},
vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2}, vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2},
@ -73,8 +75,8 @@ use spl_governance_test_sdk::{
ProgramTestBench, ProgramTestBench,
}; };
use self::{ use crate::{
args::SetRealmConfigArgs, args::{PluginSetupArgs, RealmSetupArgs},
cookies::{ cookies::{
GovernanceCookie, GovernedAccountCookie, GovernedMintCookie, GovernedProgramCookie, GovernanceCookie, GovernedAccountCookie, GovernedMintCookie, GovernedProgramCookie,
GovernedTokenCookie, MaxVoterWeightRecordCookie, NativeTreasuryCookie, 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)] #[allow(dead_code)]
pub async fn with_realm(&mut self) -> RealmCookie { pub async fn with_realm(&mut self) -> RealmCookie {
let set_realm_config_args = self.get_default_set_realm_config_args(); let realm_setup_args = RealmSetupArgs::default();
self.with_realm_using_config_args(&set_realm_config_args) self.with_realm_using_args(&realm_setup_args).await
.await
} }
#[allow(dead_code)] #[allow(dead_code)]
pub async fn with_realm_using_config_args( pub async fn with_realm_using_addins(
&mut self, &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 { ) -> RealmCookie {
let name = format!("Realm #{}", self.next_realm_id).to_string(); let name = format!("Realm #{}", self.next_realm_id).to_string();
self.next_realm_id += 1; self.next_realm_id += 1;
@ -246,7 +253,7 @@ impl GovernanceProgramTest {
council_token_mint_pubkey, council_token_mint_pubkey,
council_token_holding_address, council_token_holding_address,
council_token_mint_authority, 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_keypair = Keypair::new();
let council_token_mint_authority = Keypair::new(); let council_token_mint_authority = Keypair::new();
@ -275,20 +282,43 @@ impl GovernanceProgramTest {
let realm_authority = Keypair::new(); 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( let create_realm_ix = create_realm(
&self.program_id, &self.program_id,
&realm_authority.pubkey(), &realm_authority.pubkey(),
&community_token_mint_keypair.pubkey(), &community_token_mint_keypair.pubkey(),
&self.bench.payer.pubkey(), &self.bench.payer.pubkey(),
council_token_mint_pubkey, council_token_mint_pubkey,
set_realm_config_args.community_voter_weight_addin, Some(community_token_args),
set_realm_config_args.max_community_voter_weight_addin, Some(council_token_args),
name.clone(), name.clone(),
set_realm_config_args realm_setup_args.min_community_weight_to_create_governance,
.realm_config_args realm_setup_args
.min_community_weight_to_create_governance,
set_realm_config_args
.realm_config_args
.community_mint_max_vote_weight_source .community_mint_max_vote_weight_source
.clone(), .clone(),
); );
@ -309,41 +339,51 @@ impl GovernanceProgramTest {
council_mint: council_token_mint_pubkey, council_mint: council_token_mint_pubkey,
reserved: [0; 6], reserved: [0; 6],
min_community_weight_to_create_governance: set_realm_config_args min_community_weight_to_create_governance: realm_setup_args
.realm_config_args
.min_community_weight_to_create_governance, .min_community_weight_to_create_governance,
community_mint_max_vote_weight_source: set_realm_config_args community_mint_max_vote_weight_source: realm_setup_args
.realm_config_args
.community_mint_max_vote_weight_source .community_mint_max_vote_weight_source
.clone(), .clone(),
use_community_voter_weight_addin: false, legacy1: 0,
use_max_community_voter_weight_addin: false, legacy2: 0,
}, },
voting_proposal_count: 0, voting_proposal_count: 0,
reserved_v2: [0; 128], reserved_v2: [0; 128],
}; };
let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some() let realm_config_cookie = RealmConfigCookie {
|| set_realm_config_args
.max_community_voter_weight_addin
.is_some()
{
Some(RealmConfigCookie {
address: get_realm_config_address(&self.program_id, &realm_address), address: get_realm_config_address(&self.program_id, &realm_address),
account: RealmConfigAccount { account: RealmConfigAccount {
account_type: GovernanceAccountType::RealmConfig, account_type: GovernanceAccountType::RealmConfig,
realm: realm_address, realm: realm_address,
community_voter_weight_addin: set_realm_config_args reserved: Reserved110::default(),
.community_voter_weight_addin, community_token_config: GoverningTokenConfig {
max_community_voter_weight_addin: set_realm_config_args voter_weight_addin: realm_setup_args
.max_community_voter_weight_addin, .community_token_config_args
council_voter_weight_addin: None, .voter_weight_addin,
council_max_vote_weight_addin: None, max_voter_weight_addin: realm_setup_args
reserved: [0; 128], .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],
},
}, },
})
} else {
None
}; };
RealmCookie { RealmCookie {
@ -405,8 +445,8 @@ impl GovernanceProgramTest {
community_mint_max_vote_weight_source: community_mint_max_vote_weight_source:
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION, MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
min_community_weight_to_create_governance, min_community_weight_to_create_governance,
use_community_voter_weight_addin: false, legacy1: 0,
use_max_community_voter_weight_addin: false, legacy2: 0,
}, },
voting_proposal_count: 0, voting_proposal_count: 0,
reserved_v2: [0; 128], reserved_v2: [0; 128],
@ -421,6 +461,17 @@ impl GovernanceProgramTest {
let council_token_holding_address = let council_token_holding_address =
get_governing_token_holding_address(&self.program_id, &realm_address, &council_mint); 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 { RealmCookie {
address: realm_address, address: realm_address,
account, account,
@ -433,7 +484,7 @@ impl GovernanceProgramTest {
realm_cookie.council_mint_authority.as_ref().unwrap(), realm_cookie.council_mint_authority.as_ref().unwrap(),
)), )),
realm_authority: Some(realm_authority), 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( pub async fn with_community_token_owner_record(
&mut self, &mut self,
realm_cookie: &RealmCookie, 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 { ) -> TokenOwnerRecordCookie {
let token_owner = Keypair::new(); let token_owner = Keypair::new();
@ -464,7 +537,7 @@ impl GovernanceProgramTest {
&self.program_id, &self.program_id,
&realm_cookie.address, &realm_cookie.address,
&token_owner.pubkey(), &token_owner.pubkey(),
&realm_cookie.account.community_mint, governing_token_mint,
&self.bench.payer.pubkey(), &self.bench.payer.pubkey(),
); );
@ -476,7 +549,7 @@ impl GovernanceProgramTest {
let account = TokenOwnerRecordV2 { let account = TokenOwnerRecordV2 {
account_type: GovernanceAccountType::TokenOwnerRecordV2, account_type: GovernanceAccountType::TokenOwnerRecordV2,
realm: realm_cookie.address, 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_owner: token_owner.pubkey(),
governing_token_deposit_amount: 0, governing_token_deposit_amount: 0,
governance_delegate: None, governance_delegate: None,
@ -490,7 +563,7 @@ impl GovernanceProgramTest {
let token_owner_record_address = get_token_owner_record_address( let token_owner_record_address = get_token_owner_record_address(
&self.program_id, &self.program_id,
&realm_cookie.address, &realm_cookie.address,
&realm_cookie.account.community_mint, governing_token_mint,
&token_owner.pubkey(), &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<Keypair>,
) -> Result<TokenOwnerRecordCookie, ProgramError> {
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)] #[allow(dead_code)]
pub async fn mint_community_tokens(&mut self, realm_cookie: &RealmCookie, amount: u64) { pub async fn mint_community_tokens(&mut self, realm_cookie: &RealmCookie, amount: u64) {
let token_account_keypair = Keypair::new(); let token_account_keypair = Keypair::new();
@ -974,14 +1114,9 @@ impl GovernanceProgramTest {
pub async fn set_realm_config( pub async fn set_realm_config(
&mut self, &mut self,
realm_cookie: &mut RealmCookie, realm_cookie: &mut RealmCookie,
set_realm_config_args: &SetRealmConfigArgs, realm_setup_args: &RealmSetupArgs,
) -> Result<(), ProgramError> { ) -> Result<(), ProgramError> {
self.set_realm_config_using_instruction( self.set_realm_config_using_instruction(realm_cookie, realm_setup_args, NopOverride, None)
realm_cookie,
set_realm_config_args,
NopOverride,
None,
)
.await .await
} }
@ -989,47 +1124,26 @@ impl GovernanceProgramTest {
pub async fn set_realm_config_using_instruction<F: Fn(&mut Instruction)>( pub async fn set_realm_config_using_instruction<F: Fn(&mut Instruction)>(
&mut self, &mut self,
realm_cookie: &mut RealmCookie, realm_cookie: &mut RealmCookie,
set_realm_config_args: &SetRealmConfigArgs, realm_setup_args: &RealmSetupArgs,
instruction_override: F, instruction_override: F,
signers_override: Option<&[&Keypair]>, signers_override: Option<&[&Keypair]>,
) -> Result<(), ProgramError> { ) -> 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 realm_cookie.account.config.council_mint
} else { } else {
None 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( let mut set_realm_config_ix = set_realm_config(
&self.program_id, &self.program_id,
&realm_cookie.address, &realm_cookie.address,
&realm_cookie.realm_authority.as_ref().unwrap().pubkey(), &realm_cookie.realm_authority.as_ref().unwrap().pubkey(),
council_token_mint, council_token_mint,
&self.bench.payer.pubkey(), &self.bench.payer.pubkey(),
community_voter_weight_addin, Some(realm_setup_args.community_token_config_args.clone()),
max_community_voter_weight_addin, Some(realm_setup_args.council_token_config_args.clone()),
set_realm_config_args realm_setup_args.min_community_weight_to_create_governance,
.realm_config_args realm_setup_args
.min_community_weight_to_create_governance,
set_realm_config_args
.realm_config_args
.community_mint_max_vote_weight_source .community_mint_max_vote_weight_source
.clone(), .clone(),
); );
@ -1043,32 +1157,44 @@ impl GovernanceProgramTest {
realm_cookie realm_cookie
.account .account
.config .config
.community_mint_max_vote_weight_source = set_realm_config_args .community_mint_max_vote_weight_source = realm_setup_args
.realm_config_args
.community_mint_max_vote_weight_source .community_mint_max_vote_weight_source
.clone(); .clone();
if set_realm_config_args realm_cookie.realm_config = RealmConfigCookie {
.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), address: get_realm_config_address(&self.program_id, &realm_cookie.address),
account: RealmConfigAccount { account: RealmConfigAccount {
account_type: GovernanceAccountType::RealmConfig, account_type: GovernanceAccountType::RealmConfig,
realm: realm_cookie.address, realm: realm_cookie.address,
community_voter_weight_addin, reserved: Reserved110::default(),
max_community_voter_weight_addin, community_token_config: GoverningTokenConfig {
council_voter_weight_addin: None, voter_weight_addin: realm_setup_args
council_max_vote_weight_addin: None, .community_token_config_args
reserved: [0; 128], .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 self.bench
.process_transaction(&[set_realm_config_ix], Some(signers)) .process_transaction(&[set_realm_config_ix], Some(signers))
.await .await
@ -1129,6 +1255,73 @@ impl GovernanceProgramTest {
.await .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<F: Fn(&mut 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)] #[allow(dead_code)]
pub async fn with_governed_account(&mut self) -> GovernedAccountCookie { pub async fn with_governed_account(&mut self) -> GovernedAccountCookie {
GovernedAccountCookie { GovernedAccountCookie {
@ -2157,13 +2350,31 @@ impl GovernanceProgramTest {
self.with_cast_vote(proposal_cookie, token_owner_record_cookie, vote) self.with_cast_vote(proposal_cookie, token_owner_record_cookie, vote)
.await .await
} }
#[allow(dead_code)] #[allow(dead_code)]
pub async fn with_cast_vote( pub async fn with_cast_vote(
&mut self, &mut self,
proposal_cookie: &ProposalCookie, proposal_cookie: &ProposalCookie,
token_owner_record_cookie: &TokenOwnerRecordCookie, token_owner_record_cookie: &TokenOwnerRecordCookie,
vote: Vote, vote: Vote,
) -> Result<VoteRecordCookie, ProgramError> {
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<F: Fn(&mut Instruction)>(
&mut self,
proposal_cookie: &ProposalCookie,
token_owner_record_cookie: &TokenOwnerRecordCookie,
vote: Vote,
instruction_override: F,
signers_override: Option<&[&Keypair]>,
) -> Result<VoteRecordCookie, ProgramError> { ) -> Result<VoteRecordCookie, ProgramError> {
let voter_weight_record = let voter_weight_record =
if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record { if let Some(voter_weight_record) = &token_owner_record_cookie.voter_weight_record {
@ -2180,7 +2391,7 @@ impl GovernanceProgramTest {
None None
}; };
let cast_vote_ix = cast_vote( let mut cast_vote_ix = cast_vote(
&self.program_id, &self.program_id,
&token_owner_record_cookie.account.realm, &token_owner_record_cookie.account.realm,
&proposal_cookie.account.governance, &proposal_cookie.account.governance,
@ -2195,11 +2406,13 @@ impl GovernanceProgramTest {
vote.clone(), 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 self.bench
.process_transaction( .process_transaction(&[cast_vote_ix], Some(signers))
&[cast_vote_ix],
Some(&[&token_owner_record_cookie.token_owner]),
)
.await?; .await?;
let vote_amount = token_owner_record_cookie let vote_amount = token_owner_record_cookie
@ -2628,7 +2841,7 @@ impl GovernanceProgramTest {
} }
#[allow(dead_code)] #[allow(dead_code)]
pub async fn get_realm_config_data( pub async fn get_realm_config_account(
&mut self, &mut self,
realm_config_address: &Pubkey, realm_config_address: &Pubkey,
) -> RealmConfigAccount { ) -> RealmConfigAccount {

View File

@ -7,36 +7,46 @@ mod program_test;
use program_test::*; use program_test::*;
use crate::program_test::args::RealmSetupArgs;
#[tokio::test] #[tokio::test]
async fn test_create_realm_with_all_addins() { async fn test_create_realm_with_all_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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 // Act
let realm_cookie = governance_test.with_realm().await; let realm_cookie = governance_test
.with_realm_using_args(&realm_setup_args)
.await;
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -44,56 +54,44 @@ async fn test_set_all_addins_for_realm_without_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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_max_community_voter_weight_addin = false;
set_realm_config_args
.realm_config_args
.use_community_voter_weight_addin = false;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = true; .voter_weight_addin = governance_test.voter_weight_addin_id;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -101,58 +99,46 @@ async fn test_set_all_addin_for_realm_without_council_and_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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_setup_args.use_council_mint = false;
.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;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .voter_weight_addin = governance_test.voter_weight_addin_id;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[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 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_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .voter_weight_addin = governance_test.voter_weight_addin_id;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
let max_community_voter_weight_addin_address = Pubkey::new_unique(); let max_community_voter_weight_addin_address = Pubkey::new_unique();
set_realm_config_args.max_community_voter_weight_addin = realm_setup_args
Some(max_community_voter_weight_addin_address); .community_token_config_args
.max_voter_weight_addin = Some(max_community_voter_weight_addin_address);
let community_voter_weight_addin_address = Pubkey::new_unique(); 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 // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .await;
assert_eq!(realm_config_cookie.account, realm_config_data); assert_eq!(realm_cookie.realm_config.account, realm_config_data);
assert_eq!( 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) Some(max_community_voter_weight_addin_address)
); );
assert_eq!( assert_eq!(
realm_config_data.community_voter_weight_addin, realm_config_data.community_token_config.voter_weight_addin,
Some(community_voter_weight_addin_address) 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] #[tokio::test]
@ -223,48 +210,42 @@ async fn test_set_realm_config_without_addins_for_realm_without_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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_max_community_voter_weight_addin = false;
set_realm_config_args
.realm_config_args
.use_community_voter_weight_addin = false;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = false; .max_voter_weight_addin = None;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = false; .voter_weight_addin = None;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // Assert
let realm_account_data = governance_test let realm_config_data = governance_test
.get_realm_account(&realm_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .await;
assert!( assert!(realm_config_data
!realm_account_data .community_token_config
.config .voter_weight_addin
.use_max_community_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] #[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 governance_test = GovernanceProgramTest::start_with_all_addins().await;
let mut realm_cookie = governance_test.with_realm().await; let mut realm_cookie = governance_test.with_realm().await;
let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); let realm_setup_args = RealmSetupArgs::default();
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;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 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; .await;
assert!(realm_config_data.max_community_voter_weight_addin.is_none()); assert!(realm_config_data
assert!(realm_config_data.community_voter_weight_addin.is_none()); .community_token_config
.max_voter_weight_addin
.is_none());
assert!(realm_config_data
.community_token_config
.voter_weight_addin
.is_none());
} }

View File

@ -7,34 +7,37 @@ mod program_test;
use program_test::*; use program_test::*;
use crate::program_test::args::RealmSetupArgs;
#[tokio::test] #[tokio::test]
async fn test_create_realm_with_max_voter_weight_addin() { async fn test_create_realm_with_max_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; 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 // Act
let realm_cookie = governance_test.with_realm().await; let realm_cookie = governance_test
.with_realm_using_args(&realm_setup_args)
.await;
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -42,50 +45,35 @@ async fn test_set_realm_max_voter_weight_addin_for_realm_without_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; 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_max_community_voter_weight_addin = false;
set_realm_config_args
.realm_config_args
.use_community_voter_weight_addin = false;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -93,52 +81,37 @@ async fn test_set_realm_max_voter_weight_addin_for_realm_without_council_and_add
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; 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_setup_args.use_council_mint = false;
.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;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[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 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_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = true; .max_voter_weight_addin = governance_test.max_voter_weight_addin_id;
let max_community_voter_weight_addin_address = Pubkey::new_unique(); let max_community_voter_weight_addin_address = Pubkey::new_unique();
set_realm_config_args.max_community_voter_weight_addin = realm_setup_args
Some(max_community_voter_weight_addin_address); .community_token_config_args
.max_voter_weight_addin = Some(max_community_voter_weight_addin_address);
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .await;
assert_eq!(realm_config_cookie.account, realm_config_data); assert_eq!(realm_cookie.realm_config.account, realm_config_data);
assert_eq!( 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) Some(max_community_voter_weight_addin_address)
); );
assert!(realm_config_data
.community_token_config
.max_voter_weight_addin
.is_some());
} }
#[tokio::test] #[tokio::test]
@ -195,38 +164,33 @@ async fn test_set_realm_config_with_no_max_voter_weight_addin_for_realm_without_
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; 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_max_community_voter_weight_addin = false;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_max_community_voter_weight_addin = false; .max_voter_weight_addin = None;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // Assert
let realm_account_data = governance_test let realm_config_data = governance_test
.get_realm_account(&realm_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .await;
assert!( assert!(realm_config_data
!realm_account_data .community_token_config
.config .max_voter_weight_addin
.use_max_community_voter_weight_addin .is_none());
);
} }
#[tokio::test] #[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 governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let mut realm_cookie = governance_test.with_realm().await; let mut realm_cookie = governance_test.with_realm().await;
let mut set_realm_config_args = governance_test.get_default_set_realm_config_args(); let realm_setup_args = RealmSetupArgs::default();
set_realm_config_args
.realm_config_args
.use_max_community_voter_weight_addin = false;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 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; .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());
} }

View File

@ -5,6 +5,7 @@ use solana_program_test::*;
mod program_test; mod program_test;
use crate::program_test::args::RealmSetupArgs;
use program_test::*; use program_test::*;
#[tokio::test] #[tokio::test]
@ -12,25 +13,30 @@ async fn test_create_realm_with_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; 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 // Act
let realm_cookie = governance_test.with_realm().await; let realm_cookie = governance_test
.with_realm_using_args(&realm_setup_args)
.await;
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -38,41 +44,38 @@ async fn test_set_realm_voter_weight_addin_for_realm_without_addins() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; 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(); let mut realm_setup_args = RealmSetupArgs::default();
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = false; .voter_weight_addin = None;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = true; .voter_weight_addin = governance_test.voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -80,42 +83,37 @@ async fn test_set_realm_voter_weight_addin_for_realm_without_council_and_addins(
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; 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(); let mut realm_setup_args = RealmSetupArgs::default();
set_realm_config_args
.realm_config_args realm_setup_args.use_council_mint = false;
.use_community_voter_weight_addin = false;
set_realm_config_args.realm_config_args.use_council_mint = false;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&set_realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
set_realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = true; .voter_weight_addin = governance_test.voter_weight_addin_id;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[tokio::test]
@ -123,43 +121,44 @@ async fn test_set_realm_voter_weight_addin_for_realm_with_existing_voter_weight_
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; 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 let mut realm_cookie = governance_test
.realm_config_args .with_realm_using_args(&realm_setup_args)
.use_community_voter_weight_addin = true; .await;
let community_voter_weight_addin_address = Pubkey::new_unique(); 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 // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 let realm_config_data = governance_test
.get_realm_config_data(&realm_config_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .await;
assert_eq!(realm_config_cookie.account, realm_config_data); assert_eq!(realm_cookie.realm_config.account, realm_config_data);
assert_eq!( assert_eq!(
realm_config_data.community_voter_weight_addin, realm_config_data.community_token_config.voter_weight_addin,
Some(community_voter_weight_addin_address) Some(community_voter_weight_addin_address)
); );
assert!(realm_config_data
.community_token_config
.voter_weight_addin
.is_some());
} }
#[tokio::test] #[tokio::test]
@ -167,33 +166,37 @@ async fn test_set_realm_config_with_no_voter_weight_addin_for_realm_without_addi
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let mut realm_config_args = governance_test.get_default_set_realm_config_args(); let mut realm_setup_args = RealmSetupArgs::default();
realm_config_args
.realm_config_args realm_setup_args
.use_community_voter_weight_addin = false; .community_token_config_args
.voter_weight_addin = None;
let mut realm_cookie = governance_test let mut realm_cookie = governance_test
.with_realm_using_config_args(&realm_config_args) .with_realm_using_args(&realm_setup_args)
.await; .await;
realm_config_args realm_setup_args
.realm_config_args .community_token_config_args
.use_community_voter_weight_addin = false; .voter_weight_addin = None;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // Assert
let realm_account_data = governance_test let realm_config_data = governance_test
.get_realm_account(&realm_cookie.address) .get_realm_config_account(&realm_cookie.realm_config.address)
.await; .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] #[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 governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let mut realm_cookie = governance_test.with_realm().await; let mut realm_cookie = governance_test.with_realm().await;
let mut realm_config_args = governance_test.get_default_set_realm_config_args(); let realm_setup_args = RealmSetupArgs::default();
realm_config_args
.realm_config_args
.use_community_voter_weight_addin = false;
// Act // Act
governance_test governance_test
.set_realm_config(&mut realm_cookie, &realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();
// Assert // 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 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; .await;
assert!(realm_config_data.community_voter_weight_addin.is_none()); assert!(realm_config_data
.community_token_config
.voter_weight_addin
.is_none());
} }

View File

@ -4,15 +4,18 @@ use solana_program_test::*;
mod program_test; mod program_test;
use program_test::args::*;
use program_test::*; use program_test::*;
use spl_governance::state::enums::ProposalState; use spl_governance::state::enums::ProposalState;
#[tokio::test] #[tokio::test]
async fn test_cast_vote_with_all_addin() { async fn test_cast_community_vote_with_all_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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) 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] #[tokio::test]
async fn test_tip_vote_with_all_addin() { async fn test_tip_vote_with_all_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .with_community_token_owner_record(&realm_cookie)
@ -132,7 +199,9 @@ async fn test_finalize_vote_with_all_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await; 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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .with_community_token_owner_record(&realm_cookie)

View File

@ -4,16 +4,19 @@ use solana_program_test::*;
mod program_test; mod program_test;
use program_test::args::*;
use program_test::*; use program_test::*;
use spl_governance::{error::GovernanceError, state::enums::ProposalState}; use spl_governance::{error::GovernanceError, state::enums::ProposalState};
#[tokio::test] #[tokio::test]
async fn test_cast_vote_with_max_voter_weight_addin() { async fn test_cast_vote_with_community_max_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test 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) 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] #[tokio::test]
async fn test_tip_vote_with_max_voter_weight_addin() { async fn test_tip_vote_with_max_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await; let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 180
let mut token_owner_record_cookie = governance_test 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 mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test 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 mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test 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 mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test 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 mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 // TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test let mut token_owner_record_cookie = governance_test

View File

@ -5,7 +5,9 @@ use solana_program_test::*;
mod program_test; mod program_test;
use program_test::args::*;
use program_test::*; use program_test::*;
use spl_governance::{ use spl_governance::{
error::GovernanceError, error::GovernanceError,
state::vote_record::{Vote, VoteChoice}, state::vote_record::{Vote, VoteChoice},
@ -13,12 +15,14 @@ use spl_governance::{
use spl_governance_addin_api::voter_weight::VoterWeightAction; use spl_governance_addin_api::voter_weight::VoterWeightAction;
#[tokio::test] #[tokio::test]
async fn test_create_governance_with_voter_weight_addin() { async fn test_create_governance_with_community_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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); 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] #[tokio::test]
async fn test_create_proposal_with_voter_weight_addin() { async fn test_create_proposal_with_voter_weight_addin() {
// Arrange // Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await; let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_token_cookie = governance_test.with_governed_token().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_mint_cookie = governance_test.with_governed_mint().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_program_cookie = governance_test.with_governed_program().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .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 mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().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 let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie) .with_community_token_owner_record(&realm_cookie)

View File

@ -15,7 +15,7 @@ use spl_governance::{
}; };
use spl_governance_test_sdk::tools::clone_keypair; use spl_governance_test_sdk::tools::clone_keypair;
use self::args::SetRealmConfigArgs; use crate::program_test::args::RealmSetupArgs;
#[tokio::test] #[tokio::test]
async fn test_cast_veto_vote() { async fn test_cast_veto_vote() {
@ -431,11 +431,11 @@ async fn test_cast_veto_vote_with_no_council_error() {
.unwrap(); .unwrap();
// Remove Council // Remove Council
let mut set_realm_config_args = SetRealmConfigArgs::default(); let mut realm_setup_args = RealmSetupArgs::default();
set_realm_config_args.realm_config_args.use_council_mint = false; realm_setup_args.use_council_mint = false;
governance_test governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args) .set_realm_config(&mut realm_cookie, &realm_setup_args)
.await .await
.unwrap(); .unwrap();