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
/// The account is used as an api interface to provide max voting power to the governance program from external addin contracts
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct MaxVoterWeightRecord {
/// VoterWeightRecord discriminator sha256("account:MaxVoterWeightRecord")[..8]
/// Note: The discriminator size must match the addin implementing program discriminator size

View File

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

View File

@ -11,7 +11,7 @@ use spl_governance_addin_api::voter_weight::VoterWeightAction;
/// Instructions supported by the VoterWeight addin program
/// This program is a mock program used by spl-governance for testing and not real addin
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[allow(clippy::large_enum_variant)]
pub enum VoterWeightAddinInstruction {
/// Sets up VoterWeightRecord owned by the program

View File

@ -11,7 +11,7 @@ use spl_governance::instruction::with_realm_config_accounts;
use crate::state::MessageBody;
/// Instructions supported by the GovernanceChat program
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[allow(clippy::large_enum_variant)]
pub enum GovernanceChatInstruction {
/// Posts a message with a comment for a Proposal

View File

@ -18,7 +18,8 @@ use solana_program::{
};
use spl_governance::state::{
governance::get_governance_data_for_realm, proposal::get_proposal_data_for_governance,
realm::get_realm_data, token_owner_record::get_token_owner_record_data_for_realm,
realm::get_realm_data, realm_config::get_realm_config_data_for_realm,
token_owner_record::get_token_owner_record_data_for_realm,
};
use spl_governance_addin_api::voter_weight::VoterWeightAction;
use spl_governance_tools::account::create_and_serialize_account;
@ -92,14 +93,15 @@ pub fn process_post_message(
governance_info.key,
)?;
let realm_config_info = next_account_info(account_info_iter)?; //10
let realm_config_info = next_account_info(account_info_iter)?; // 10
let realm_config_data =
get_realm_config_data_for_realm(governance_program_id, realm_config_info, realm_info.key)?;
let voter_weight = token_owner_record_data.resolve_voter_weight(
governance_program_id,
realm_config_info,
account_info_iter, // 11
realm_info.key,
account_info_iter, // voter_weight_record *11
&realm_data,
&realm_config_data,
VoterWeightAction::CommentProposal,
proposal_info.key,
)?;

View File

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

View File

@ -13,7 +13,8 @@ use spl_governance::{
enums::{MintMaxVoteWeightSource, VoteThreshold},
governance::{get_governance_address, GovernanceConfig},
proposal::{get_proposal_address, VoteType},
realm::get_realm_address,
realm::{get_realm_address, GoverningTokenConfigAccountArgs},
realm_config::GoverningTokenType,
token_owner_record::get_token_owner_record_address,
},
};
@ -108,13 +109,19 @@ impl GovernanceChatProgramTest {
let realm_authority = Keypair::new();
let community_token_config_args = GoverningTokenConfigAccountArgs {
voter_weight_addin: self.voter_weight_addin_id,
max_voter_weight_addin: None,
token_type: GoverningTokenType::default(),
};
let create_realm_ix = create_realm(
&self.governance_program_id,
&realm_authority.pubkey(),
&governing_token_mint_keypair.pubkey(),
&self.bench.payer.pubkey(),
None,
self.voter_weight_addin_id,
Some(community_token_config_args),
None,
name.clone(),
1,

View File

@ -261,23 +261,23 @@ pub enum GovernanceError {
/// Governance PDA must sign
#[error("Governance PDA must sign")]
GovernancePdaMustSign,
GovernancePdaMustSign, // 561
/// Transaction already flagged with error
#[error("Transaction already flagged with error")]
TransactionAlreadyFlaggedWithError,
TransactionAlreadyFlaggedWithError, // 562
/// Invalid Realm for Governance
#[error("Invalid Realm for Governance")]
InvalidRealmForGovernance,
InvalidRealmForGovernance, // 563
/// Invalid Authority for Realm
#[error("Invalid Authority for Realm")]
InvalidAuthorityForRealm,
InvalidAuthorityForRealm, // 564
/// Realm has no authority
#[error("Realm has no authority")]
RealmHasNoAuthority,
RealmHasNoAuthority, // 565
/// Realm authority must sign
#[error("Realm authority must sign")]
@ -285,7 +285,7 @@ pub enum GovernanceError {
/// Invalid governing token holding account
#[error("Invalid governing token holding account")]
InvalidGoverningTokenHoldingAccount,
InvalidGoverningTokenHoldingAccount, // 567
/// Realm council mint change is not supported
#[error("Realm council mint change is not supported")]
@ -406,6 +406,34 @@ pub enum GovernanceError {
/// Cannot Relinquish in Finalizing state
#[error("Cannot Relinquish in Finalizing state")]
CannotRelinquishInFinalizingState,
/// Invalid RealmConfig account address
#[error("Invalid RealmConfig account address")]
InvalidRealmConfigAddress,
/// Cannot deposit dormant tokens
#[error("Cannot deposit dormant tokens")]
CannotDepositDormantTokens, // 599
/// Cannot withdraw membership tokens
#[error("Cannot withdraw membership tokens")]
CannotWithdrawMembershipTokens, // 600
/// Cannot revoke GoverningTokens
#[error("Cannot revoke GoverningTokens")]
CannotRevokeGoverningTokens, // 601
/// Invalid Revoke amount
#[error("Invalid Revoke amount")]
InvalidRevokeAmount, // 602
/// Invalid GoverningToken source
#[error("Invalid GoverningToken source")]
InvalidGoverningTokenSource, // 603
/// Cannot change community TokenType to Memebership
#[error("Cannot change community TokenType to Memebership")]
CannotChangeCommunityTokenTypeToMemebership, // 604
}
impl PrintProgramError for GovernanceError {

View File

@ -11,8 +11,11 @@ use crate::{
program_metadata::get_program_metadata_address,
proposal::{get_proposal_address, VoteType},
proposal_transaction::{get_proposal_transaction_address, InstructionData},
realm::SetRealmAuthorityAction,
realm::{get_governing_token_holding_address, get_realm_address, RealmConfigArgs},
realm::{
get_governing_token_holding_address, get_realm_address,
GoverningTokenConfigAccountArgs, RealmConfigArgs,
},
realm::{GoverningTokenConfigArgs, SetRealmAuthorityAction},
realm_config::get_realm_config_address,
signatory_record::get_signatory_record_address,
token_owner_record::get_token_owner_record_address,
@ -29,7 +32,7 @@ use solana_program::{
};
/// Instructions supported by the Governance program
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[allow(clippy::large_enum_variant)]
pub enum GovernanceInstruction {
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
@ -48,9 +51,13 @@ pub enum GovernanceInstruction {
/// 9. `[writable]` Council Token Holding account - optional unless council is used. PDA seeds: ['governance',realm,council_mint]
/// The account will be created with the Realm PDA as its owner
/// 10. `[]` Optional Community Voter Weight Addin Program Id
/// 11. `[]` Optional Max Community Voter Weight Addin Program Id
/// 12. `[writable]` Optional RealmConfig account. PDA seeds: ['realm-config', realm]
/// 10. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Community Voter Weight Addin Program Id
/// 12. `[]` Optional Max Community Voter Weight Addin Program Id
///
/// 13. `[]` Optional Council Voter Weight Addin Program Id
/// 14. `[]` Optional Max Council Voter Weight Addin Program Id
CreateRealm {
#[allow(dead_code)]
/// UTF-8 encoded Governance Realm name
@ -65,16 +72,18 @@ pub enum GovernanceInstruction {
/// Note: If subsequent (top up) deposit is made and there are active votes for the Voter then the vote weights won't be updated automatically
/// It can be done by relinquishing votes on active Proposals and voting again with the new weight
///
/// 0. `[]` Governance Realm account
/// 0. `[]` Realm account
/// 1. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint]
/// 2. `[writable]` Governing Token Source account. All tokens from the account will be transferred to the Holding account
/// 2. `[writable]` Governing Token Source account. It can be either spl-token TokenAccount or MintAccount
/// Tokens will be transferred or minted to the Holding account
/// 3. `[signer]` Governing Token Owner account
/// 4. `[signer]` Governing Token Transfer authority
/// 5. `[writable]` Token Owner Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 4. `[signer]` Governing Token Source account authority
/// It should be owner for TokenAccount and mint_auhtority for MintAccount
/// 5. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 6. `[signer]` Payer
/// 7. `[]` System
/// 8. `[]` SPL Token
/// 9. `[]` Sysvar Rent
/// 8. `[]` SPL Token program
/// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
DepositGoverningTokens {
/// The amount to deposit into the realm
#[allow(dead_code)]
@ -85,12 +94,13 @@ pub enum GovernanceInstruction {
/// Note: It's only possible to withdraw tokens if the Voter doesn't have any outstanding active votes
/// If there are any outstanding votes then they must be relinquished before tokens could be withdrawn
///
/// 0. `[]` Governance Realm account
/// 0. `[]` Realm account
/// 1. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint]
/// 2. `[writable]` Governing Token Destination account. All tokens will be transferred to this account
/// 3. `[signer]` Governing Token Owner account
/// 4. `[writable]` Token Owner Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 5. `[]` SPL Token
/// 4. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 5. `[]` SPL Token program
/// 6. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
WithdrawGoverningTokens {},
/// Sets Governance Delegate for the given Realm and Governing Token Mint (Community or Council)
@ -117,7 +127,7 @@ pub enum GovernanceInstruction {
/// 5. `[]` System program
/// 6. `[]` Sysvar Rent
/// 7. `[signer]` Governance authority
/// 8. `[]` Realm Config
/// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 9. `[]` Optional Voter Weight Record
CreateGovernance {
/// Governance config
@ -138,7 +148,7 @@ pub enum GovernanceInstruction {
/// 8. `[]` System program
/// 9. `[]` Sysvar Rent
/// 10. `[signer]` Governance authority
/// 11. `[]` Realm Config
/// 11. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 12. `[]` Optional Voter Weight Record
CreateProgramGovernance {
/// Governance config
@ -162,7 +172,7 @@ pub enum GovernanceInstruction {
/// 5. `[signer]` Governance Authority (Token Owner or Governance Delegate)
/// 6. `[signer]` Payer
/// 7. `[]` System program
/// 8. `[]` Realm Config
/// 8. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 9. `[]` Optional Voter Weight Record
CreateProposal {
#[allow(dead_code)]
@ -295,7 +305,7 @@ pub enum GovernanceInstruction {
/// Note: In the current version only Council veto is supported
/// 8. `[signer]` Payer
/// 9. `[]` System program
/// 10. `[]` Realm Config
/// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Voter Weight Record
/// 12. `[]` Optional Max Voter Weight Record
CastVote {
@ -311,7 +321,7 @@ pub enum GovernanceInstruction {
/// 2. `[writable]` Proposal account
/// 3. `[writable]` TokenOwnerRecord of the Proposal owner
/// 4. `[]` Governing Token Mint
/// 5. `[]` Realm Config
/// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 6. `[]` Optional Max Voter Weight Record
FinalizeVote {},
@ -355,7 +365,7 @@ pub enum GovernanceInstruction {
/// 7. `[]` System program
/// 8. `[]` Sysvar Rent
/// 8. `[signer]` Governance authority
/// 9. `[]` Realm Config
/// 9. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 10. `[]` Optional Voter Weight Record
CreateMintGovernance {
#[allow(dead_code)]
@ -381,7 +391,7 @@ pub enum GovernanceInstruction {
/// 7. `[]` System program
/// 8. `[]` Sysvar Rent
/// 9. `[signer]` Governance authority
/// 10. `[]` Realm Config
/// 10. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 11. `[]` Optional Voter Weight Record
CreateTokenGovernance {
#[allow(dead_code)]
@ -438,10 +448,14 @@ pub enum GovernanceInstruction {
/// The account will be created with the Realm PDA as its owner
/// 4. `[]` System
/// 5. `[writable]` RealmConfig account. PDA seeds: ['realm-config', realm]
///
/// 6. `[]` Optional Community Voter Weight Addin Program Id
/// 7. `[]` Optional Max Community Voter Weight Addin Program Id
/// 8. `[signer]` Optional Payer
///
/// 8. `[]` Optional Council Voter Weight Addin Program Id
/// 9. `[]` Optional Max Council Voter Weight Addin Program Id
///
/// 10. `[signer]` Optional Payer. Required if RealmConfig doesn't exist and needs to be created
SetRealmConfig {
#[allow(dead_code)]
/// Realm config args
@ -475,6 +489,22 @@ pub enum GovernanceInstruction {
/// 2. `[signer]` Payer
/// 3. `[]` System
CreateNativeTreasury,
/// Revokes (burns) membership governing tokens for the given TokenOwnerRecord and hence takes away governance power from the TokenOwner
/// Note: If there are active votes for the TokenOwner then the vote weights won't be updated automatically
///
/// 0. `[]` Realm account
/// 1. `[signer]` Realm authority
/// 2. `[writable]` Governing Token Holding account. PDA seeds: ['governance',realm, governing_token_mint]
/// 3. `[writable]` TokenOwnerRecord account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 4. `[writable]` GoverningTokenMint
/// 5. `[]` RealmConfig account. PDA seeds: ['realm-config', realm]
/// 6. `[]` SPL Token program
RevokeGoverningTokens {
/// The amount to revoke
#[allow(dead_code)]
amount: u64,
},
}
/// Creates CreateRealm instruction
@ -486,8 +516,9 @@ pub fn create_realm(
community_token_mint: &Pubkey,
payer: &Pubkey,
council_token_mint: Option<Pubkey>,
community_voter_weight_addin: Option<Pubkey>,
max_community_voter_weight_addin: Option<Pubkey>,
// Accounts Args
community_token_config_args: Option<GoverningTokenConfigAccountArgs>,
council_token_config_args: Option<GoverningTokenConfigAccountArgs>,
// Args
name: String,
min_community_weight_to_create_governance: u64,
@ -519,40 +550,22 @@ pub fn create_realm(
false
};
let use_community_voter_weight_addin =
if let Some(community_voter_weight_addin) = community_voter_weight_addin {
accounts.push(AccountMeta::new_readonly(
community_voter_weight_addin,
false,
));
true
} else {
false
};
let 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);
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 {
config_args: RealmConfigArgs {
use_council_mint,
min_community_weight_to_create_governance,
community_mint_max_vote_weight_source,
use_community_voter_weight_addin,
use_max_community_voter_weight_addin,
community_token_config_args,
council_token_config_args,
},
name,
};
@ -572,7 +585,7 @@ pub fn deposit_governing_tokens(
realm: &Pubkey,
governing_token_source: &Pubkey,
governing_token_owner: &Pubkey,
governing_token_transfer_authority: &Pubkey,
governing_token_source_authority: &Pubkey,
payer: &Pubkey,
// Args
amount: u64,
@ -588,16 +601,19 @@ pub fn deposit_governing_tokens(
let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint);
let realm_config_address = get_realm_config_address(program_id, realm);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, false),
AccountMeta::new(*governing_token_source, false),
AccountMeta::new_readonly(*governing_token_owner, true),
AccountMeta::new_readonly(*governing_token_transfer_authority, true),
AccountMeta::new_readonly(*governing_token_source_authority, true),
AccountMeta::new(token_owner_record_address, false),
AccountMeta::new(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(realm_config_address, false),
];
let instruction = GovernanceInstruction::DepositGoverningTokens { amount };
@ -629,6 +645,8 @@ pub fn withdraw_governing_tokens(
let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint);
let realm_config_address = get_realm_config_address(program_id, realm);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new(governing_token_holding_address, false),
@ -636,6 +654,7 @@ pub fn withdraw_governing_tokens(
AccountMeta::new_readonly(*governing_token_owner, true),
AccountMeta::new(token_owner_record_address, false),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(realm_config_address, false),
];
let instruction = GovernanceInstruction::WithdrawGoverningTokens {};
@ -1355,8 +1374,9 @@ pub fn set_realm_config(
realm_authority: &Pubkey,
council_token_mint: Option<Pubkey>,
payer: &Pubkey,
community_voter_weight_addin: Option<Pubkey>,
max_community_voter_weight_addin: Option<Pubkey>,
// Accounts Args
community_token_config_args: Option<GoverningTokenConfigAccountArgs>,
council_token_config_args: Option<GoverningTokenConfigAccountArgs>,
// Args
min_community_weight_to_create_governance: u64,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
@ -1384,39 +1404,21 @@ pub fn set_realm_config(
let realm_config_address = get_realm_config_address(program_id, realm);
accounts.push(AccountMeta::new(realm_config_address, false));
let use_community_voter_weight_addin =
if let Some(community_voter_weight_addin) = community_voter_weight_addin {
accounts.push(AccountMeta::new_readonly(
community_voter_weight_addin,
false,
));
true
} else {
false
};
let community_token_config_args =
with_governing_token_config_args(&mut accounts, community_token_config_args);
let use_max_community_voter_weight_addin =
if let Some(max_community_voter_weight_addin) = max_community_voter_weight_addin {
accounts.push(AccountMeta::new_readonly(
max_community_voter_weight_addin,
false,
));
true
} else {
false
};
let council_token_config_args =
with_governing_token_config_args(&mut accounts, council_token_config_args);
if use_community_voter_weight_addin || use_max_community_voter_weight_addin {
accounts.push(AccountMeta::new(*payer, true));
}
let instruction = GovernanceInstruction::SetRealmConfig {
config_args: RealmConfigArgs {
use_council_mint,
min_community_weight_to_create_governance,
community_mint_max_vote_weight_source,
use_community_voter_weight_addin,
use_max_community_voter_weight_addin,
community_token_config_args,
council_token_config_args,
},
};
@ -1536,3 +1538,77 @@ pub fn create_native_treasury(
data: instruction.try_to_vec().unwrap(),
}
}
/// Creates RevokeGoverningTokens instruction
#[allow(clippy::too_many_arguments)]
pub fn revoke_governing_tokens(
program_id: &Pubkey,
// Accounts
realm: &Pubkey,
realm_authority: &Pubkey,
governing_token_owner: &Pubkey,
governing_token_mint: &Pubkey,
// Args
amount: u64,
) -> Instruction {
let token_owner_record_address = get_token_owner_record_address(
program_id,
realm,
governing_token_mint,
governing_token_owner,
);
let governing_token_holding_address =
get_governing_token_holding_address(program_id, realm, governing_token_mint);
let realm_config_address = get_realm_config_address(program_id, realm);
let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new_readonly(*realm_authority, true),
AccountMeta::new(governing_token_holding_address, false),
AccountMeta::new(token_owner_record_address, false),
AccountMeta::new(*governing_token_mint, false),
AccountMeta::new_readonly(realm_config_address, false),
AccountMeta::new_readonly(spl_token::id(), false),
];
let instruction = GovernanceInstruction::RevokeGoverningTokens { amount };
Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
/// Adds accounts specified by GoverningTokenConfigAccountArgs
/// and returns GoverningTokenConfigArgs
pub fn with_governing_token_config_args(
accounts: &mut Vec<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_remove_signatory;
mod process_remove_transaction;
mod process_revoke_governing_tokens;
mod process_set_governance_config;
mod process_set_governance_delegate;
mod process_set_realm_authority;
@ -48,6 +49,7 @@ use process_insert_transaction::*;
use process_relinquish_vote::*;
use process_remove_signatory::*;
use process_remove_transaction::*;
use process_revoke_governing_tokens::*;
use process_set_governance_config::*;
use process_set_governance_delegate::*;
use process_set_realm_authority::*;
@ -213,5 +215,9 @@ pub fn process_instruction(
GovernanceInstruction::CreateNativeTreasury {} => {
process_create_native_treasury(program_id, accounts)
}
GovernanceInstruction::RevokeGoverningTokens { amount } => {
process_revoke_governing_tokens(program_id, accounts, amount)
}
}
}

View File

@ -18,6 +18,7 @@ use crate::{
governance::get_governance_data_for_realm,
proposal::get_proposal_data_for_governance_and_governing_mint,
realm::get_realm_data_for_governing_token_mint,
realm_config::get_realm_config_data_for_realm,
token_owner_record::{
get_token_owner_record_data_for_proposal_owner,
get_token_owner_record_data_for_realm_and_governing_mint,
@ -104,17 +105,14 @@ pub fn process_cast_vote(
.checked_add(1)
.unwrap();
// Note: When both voter_weight and max_voter_weight addins are used the realm_config will be deserialized twice in resolve_voter_weight() and resolve_max_voter_weight()
// It can't be deserialized eagerly because some realms won't have the config if they don't use any of the advanced options
// This extra deserialisation should be acceptable to keep things simple and encapsulated.
let realm_config_info = next_account_info(account_info_iter)?; //9
let realm_config_info = next_account_info(account_info_iter)?; // 9
let realm_config_data =
get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?;
let voter_weight = voter_token_owner_record_data.resolve_voter_weight(
program_id,
realm_config_info,
account_info_iter, // voter_weight_record 10
realm_info.key,
account_info_iter, // voter_weight_record *10
&realm_data,
&realm_config_data,
VoterWeightAction::CastVote,
proposal_info.key,
)?;
@ -152,12 +150,11 @@ pub fn process_cast_vote(
}
let max_voter_weight = proposal_data.resolve_max_voter_weight(
program_id,
realm_config_info,
vote_governing_token_mint_info,
account_info_iter, // max_voter_weight_record 11
realm_info.key,
&realm_data,
&realm_config_data,
vote_governing_token_mint_info,
&vote_kind,
)?;

View File

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

View File

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

View File

@ -14,12 +14,16 @@ use crate::{
state::{
enums::GovernanceAccountType,
realm::get_realm_data,
realm_config::get_realm_config_data_for_realm,
token_owner_record::{
get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds,
TokenOwnerRecordV2,
},
},
tools::spl_token::{get_spl_token_mint, get_spl_token_owner, transfer_spl_tokens},
tools::spl_token::{
get_spl_token_mint, is_spl_token_account, is_spl_token_mint, mint_spl_tokens_to,
transfer_spl_tokens,
},
};
/// Processes DepositGoverningTokens instruction
@ -34,11 +38,12 @@ pub fn process_deposit_governing_tokens(
let governing_token_holding_info = next_account_info(account_info_iter)?; // 1
let governing_token_source_info = next_account_info(account_info_iter)?; // 2
let governing_token_owner_info = next_account_info(account_info_iter)?; // 3
let governing_token_transfer_authority_info = next_account_info(account_info_iter)?; // 4
let governing_token_source_authority_info = next_account_info(account_info_iter)?; // 4
let token_owner_record_info = next_account_info(account_info_iter)?; // 5
let payer_info = next_account_info(account_info_iter)?; // 6
let system_info = next_account_info(account_info_iter)?; // 7
let spl_token_info = next_account_info(account_info_iter)?; // 8
let realm_config_info = next_account_info(account_info_iter)?; // 9
let rent = Rent::get()?;
@ -52,13 +57,32 @@ pub fn process_deposit_governing_tokens(
governing_token_holding_info.key,
)?;
let realm_config_data =
get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?;
realm_config_data.assert_can_deposit_governing_token(&realm_data, &governing_token_mint)?;
if is_spl_token_account(governing_token_source_info) {
// If the source is spl-token token account then transfer tokens from it
transfer_spl_tokens(
governing_token_source_info,
governing_token_holding_info,
governing_token_transfer_authority_info,
governing_token_source_authority_info,
amount,
spl_token_info,
)?;
} else if is_spl_token_mint(governing_token_source_info) {
// If it's a mint then mint the tokens
mint_spl_tokens_to(
governing_token_source_info,
governing_token_holding_info,
governing_token_source_authority_info,
amount,
spl_token_info,
)?;
} else {
return Err(GovernanceError::InvalidGoverningTokenSource.into());
}
let token_owner_record_address_seeds = get_token_owner_record_address_seeds(
realm_info.key,
@ -68,11 +92,7 @@ pub fn process_deposit_governing_tokens(
if token_owner_record_info.data_is_empty() {
// Deposited tokens can only be withdrawn by the owner so let's make sure the owner signed the transaction
let governing_token_owner = get_spl_token_owner(governing_token_source_info)?;
if !(governing_token_owner == *governing_token_owner_info.key
&& governing_token_owner_info.is_signer)
{
if !governing_token_owner_info.is_signer {
return Err(GovernanceError::GoverningTokenOwnerMustSign.into());
}

View File

@ -11,7 +11,7 @@ use solana_program::{
use crate::state::{
governance::get_governance_data_for_realm,
proposal::get_proposal_data_for_governance_and_governing_mint,
realm::get_realm_data_for_governing_token_mint,
realm::get_realm_data_for_governing_token_mint, realm_config::get_realm_config_data_for_realm,
token_owner_record::get_token_owner_record_data_for_proposal_owner, vote_record::VoteKind,
};
@ -43,15 +43,16 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P
governing_token_mint_info.key,
)?;
let realm_config_info = next_account_info(account_info_iter)?; // 5
let realm_config_info = next_account_info(account_info_iter)?; //5
let realm_config_data =
get_realm_config_data_for_realm(program_id, realm_config_info, realm_info.key)?;
let max_voter_weight = proposal_data.resolve_max_voter_weight(
program_id,
realm_config_info,
governing_token_mint_info,
account_info_iter, // *6
realm_info.key,
&realm_data,
&realm_config_data,
governing_token_mint_info,
&VoteKind::Electorate,
)?;

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

View File

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

View File

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

View File

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

View File

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

View File

@ -6,7 +6,7 @@ use spl_governance_tools::account::AccountMaxSize;
/// Treasury account
/// The account has no data and can be used as a payer for instruction signed by Governance PDAs or as a native SOL treasury
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct NativeTreasury {}
impl AccountMaxSize for NativeTreasury {

View File

@ -10,7 +10,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize};
use crate::state::enums::GovernanceAccountType;
/// Program metadata account. It stores information about the particular SPL-Governance program instance
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct ProgramMetadata {
/// Governance account type
pub account_type: GovernanceAccountType,

View File

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

View File

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

View File

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

View File

@ -1,4 +1,7 @@
//! RealmConfig account
use std::slice::Iter;
use solana_program::account_info::next_account_info;
use solana_program::{
account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized,
@ -10,9 +13,90 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize};
use crate::{error::GovernanceError, state::enums::GovernanceAccountType};
use crate::state::realm::GoverningTokenConfigArgs;
use crate::state::realm::{RealmConfigArgs, RealmV2};
/// The type of the governing token defines:
/// 1) Who retains the authority over deposited tokens
/// 2) Which token instructions Deposit, Withdraw and Revoke (burn) are allowed
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum GoverningTokenType {
/// Liquid token is a token which is fully liquid and the token owner retains full authority over it
/// Deposit - Yes
/// Withdraw - Yes
/// Revoke - No, Realm authority cannot revoke liquid tokens
Liquid,
/// Membership token is a token controlled by Realm authority
/// Deposit - Yes, membership tokens can be deposited to gain governance power
/// The membership tokens are conventionally minted into the holding account to keep them out of members possesion
/// Withdraw - No, after membership tokens are deposited they are no longer transferable and can't be withdrawn
/// Revoke - Yes, Realm authority can Revoke (burn) membership tokens
Membership,
/// Dormant token is a token which is only a placeholder and its deposits are not accepted and not used for governance power within the Realm
///
/// The Dormant token type is used when only a single voting population is operational. For example a Multisig starter DAO uses Council only
/// and sets Community as Dormant to indicate its not utilised for any governance power.
/// Once the starter DAO decides to decentralise then it can change the Community token to Liquid
///
/// Note: When an external voter weight plugin which takes deposits of the token is used then the type should be set to Dormant
/// to make the intention explicit
///
/// Deposit - No, dormant tokens can't be deposited into the Realm
/// Withdraw - Yes, tokens can still be withdrawn from Realm to support scenario where the config is changed while some tokens are still deposited
/// Revoke - No, Realm authority cannot revoke dormant tokens
Dormant,
}
#[allow(clippy::derivable_impls)]
impl Default for GoverningTokenType {
fn default() -> Self {
GoverningTokenType::Liquid
}
}
/// GoverningTokenConfig specifies configuration for Realm governing token (Community or Council)
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema, Default)]
pub struct GoverningTokenConfig {
/// Plugin providing voter weights for the governing token
pub voter_weight_addin: Option<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
/// The account is an optional extension to RealmConfig stored on Realm account
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
#[derive(Clone, Debug, PartialEq, Eq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct RealmConfigAccount {
/// Governance account type
pub account_type: GovernanceAccountType,
@ -20,23 +104,14 @@ pub struct RealmConfigAccount {
/// The realm the config belong to
pub realm: Pubkey,
/// Addin providing voter weights for community token
pub community_voter_weight_addin: Option<Pubkey>,
/// Community token config
pub community_token_config: GoverningTokenConfig,
/// Addin providing max vote weight for community token
/// Note: This field is not implemented in the current version
pub max_community_voter_weight_addin: Option<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>,
/// Council token config
pub council_token_config: GoverningTokenConfig,
/// Reserved
pub reserved: [u8; 128],
pub reserved: Reserved110,
}
impl AccountMaxSize for RealmConfigAccount {
@ -51,6 +126,98 @@ impl IsInitialized for RealmConfigAccount {
}
}
impl RealmConfigAccount {
/// Returns GoverningTokenConfig for the given governing_token_mint
pub fn get_token_config(
&self,
realm_data: &RealmV2,
governing_token_mint: &Pubkey,
) -> Result<&GoverningTokenConfig, ProgramError> {
let token_config = if *governing_token_mint == realm_data.community_mint {
&self.community_token_config
} else if Some(*governing_token_mint) == realm_data.config.council_mint {
&self.council_token_config
} else {
return Err(GovernanceError::InvalidGoverningTokenMint.into());
};
Ok(token_config)
}
/// Assertes the given governing token can be revoked
pub fn assert_can_revoke_governing_token(
&self,
realm_data: &RealmV2,
governing_token_mint: &Pubkey,
) -> Result<(), ProgramError> {
let governing_token_type = &self
.get_token_config(realm_data, governing_token_mint)?
.token_type;
match governing_token_type {
GoverningTokenType::Membership => Ok(()),
GoverningTokenType::Liquid | GoverningTokenType::Dormant => {
Err(GovernanceError::CannotRevokeGoverningTokens.into())
}
}
}
/// Assertes the given governing token can be deposited
pub fn assert_can_deposit_governing_token(
&self,
realm_data: &RealmV2,
governing_token_mint: &Pubkey,
) -> Result<(), ProgramError> {
let governing_token_type = &self
.get_token_config(realm_data, governing_token_mint)?
.token_type;
match governing_token_type {
GoverningTokenType::Membership | GoverningTokenType::Liquid => Ok(()),
// Note: Preventing deposits of the Dormant type tokens is not a direct security concern
// It only makes the intention of not using deposited tokens as governnace power stronger
GoverningTokenType::Dormant => Err(GovernanceError::CannotDepositDormantTokens.into()),
}
}
/// Assertes the given governing token can be withdrawn
pub fn assert_can_withdraw_governing_token(
&self,
realm_data: &RealmV2,
governing_token_mint: &Pubkey,
) -> Result<(), ProgramError> {
let governing_token_type = &self
.get_token_config(realm_data, governing_token_mint)?
.token_type;
match governing_token_type {
GoverningTokenType::Dormant | GoverningTokenType::Liquid => Ok(()),
GoverningTokenType::Membership => {
Err(GovernanceError::CannotWithdrawMembershipTokens.into())
}
}
}
/// Asserts the given RealmConfigArgs represent a valid Realm configuraiton change
pub fn assert_can_change_config(
&self,
realm_config_args: &RealmConfigArgs,
) -> Result<(), ProgramError> {
// Existing community token type can't be changed to Membership because it would
// give the Realm authority the right to burn members tokens which should not be the case because the tokens belong to the members
// On the other had for the Council token it's acceptable and in fact desired change becuase council tokens denote memebership
// which should be controled by the Realm
if self.community_token_config.token_type != GoverningTokenType::Membership
&& realm_config_args.community_token_config_args.token_type
== GoverningTokenType::Membership
{
return Err(GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into());
}
Ok(())
}
}
/// Deserializes RealmConfig account and checks owner program
pub fn get_realm_config_data(
program_id: &Pubkey,
@ -59,18 +226,40 @@ pub fn get_realm_config_data(
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(
program_id: &Pubkey,
realm_config_info: &AccountInfo,
realm: &Pubkey,
) -> 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)?;
if realm_config_data.realm != *realm {
return Err(GovernanceError::InvalidRealmConfigForRealm.into());
}
realm_config_data
};
Ok(realm_config_data)
}
@ -83,6 +272,32 @@ pub fn get_realm_config_address_seeds(realm: &Pubkey) -> [&[u8]; 2] {
pub fn get_realm_config_address(program_id: &Pubkey, realm: &Pubkey) -> Pubkey {
Pubkey::find_program_address(&get_realm_config_address_seeds(realm), program_id).0
}
/// Resolves GoverningTokenConfig from GoverningTokenConfigArgs and instruction accounts
pub fn resolve_governing_token_config(
account_info_iter: &mut Iter<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)]
mod test {
@ -94,11 +309,19 @@ mod test {
let realm_config = RealmConfigAccount {
account_type: GovernanceAccountType::RealmV2,
realm: Pubkey::new_unique(),
community_voter_weight_addin: Some(Pubkey::new_unique()),
max_community_voter_weight_addin: Some(Pubkey::new_unique()),
council_voter_weight_addin: Some(Pubkey::new_unique()),
council_max_vote_weight_addin: Some(Pubkey::new_unique()),
reserved: [0; 128],
community_token_config: GoverningTokenConfig {
voter_weight_addin: Some(Pubkey::new_unique()),
max_voter_weight_addin: Some(Pubkey::new_unique()),
token_type: GoverningTokenType::Liquid,
reserved: [0; 8],
},
council_token_config: GoverningTokenConfig {
voter_weight_addin: Some(Pubkey::new_unique()),
max_voter_weight_addin: Some(Pubkey::new_unique()),
token_type: GoverningTokenType::Liquid,
reserved: [0; 8],
},
reserved: Reserved110::default(),
};
let size = realm_config.try_to_vec().unwrap().len();

View File

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

View File

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

View File

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

View File

@ -121,6 +121,37 @@ pub fn transfer_spl_tokens<'a>(
Ok(())
}
/// Mint SPL Tokens
pub fn mint_spl_tokens_to<'a>(
mint_info: &AccountInfo<'a>,
destination_info: &AccountInfo<'a>,
mint_authority_info: &AccountInfo<'a>,
amount: u64,
spl_token_info: &AccountInfo<'a>,
) -> ProgramResult {
let mint_to_ix = spl_token::instruction::mint_to(
&spl_token::id(),
mint_info.key,
destination_info.key,
mint_authority_info.key,
&[],
amount,
)
.unwrap();
invoke(
&mint_to_ix,
&[
spl_token_info.clone(),
mint_authority_info.clone(),
mint_info.clone(),
destination_info.clone(),
],
)?;
Ok(())
}
/// Transfers SPL Tokens from a token account owned by the provided PDA authority with seeds
pub fn transfer_spl_tokens_signed<'a>(
source_info: &AccountInfo<'a>,
@ -170,6 +201,55 @@ pub fn transfer_spl_tokens_signed<'a>(
Ok(())
}
/// Burns SPL Tokens from a token account owned by the provided PDA authority with seeds
pub fn burn_spl_tokens_signed<'a>(
token_account_info: &AccountInfo<'a>,
token_mint_info: &AccountInfo<'a>,
authority_info: &AccountInfo<'a>,
authority_seeds: &[&[u8]],
program_id: &Pubkey,
amount: u64,
spl_token_info: &AccountInfo<'a>,
) -> ProgramResult {
let (authority_address, bump_seed) = Pubkey::find_program_address(authority_seeds, program_id);
if authority_address != *authority_info.key {
msg!(
"Burn SPL Token with Authority PDA: {:?} was requested while PDA: {:?} was expected",
authority_info.key,
authority_address
);
return Err(ProgramError::InvalidSeeds);
}
let burn_ix = spl_token::instruction::burn(
&spl_token::id(),
token_account_info.key,
token_mint_info.key,
authority_info.key,
&[],
amount,
)
.unwrap();
let mut signers_seeds = authority_seeds.to_vec();
let bump = &[bump_seed];
signers_seeds.push(bump);
invoke_signed(
&burn_ix,
&[
spl_token_info.clone(),
token_account_info.clone(),
token_mint_info.clone(),
authority_info.clone(),
],
&[&signers_seeds[..]],
)?;
Ok(())
}
/// Asserts the given account_info represents a valid SPL Token account which is initialized and belongs to spl_token program
pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
if account_info.data_is_empty() {
@ -195,6 +275,11 @@ pub fn assert_is_valid_spl_token_account(account_info: &AccountInfo) -> Result<(
Ok(())
}
/// Checks if the given account_info is spl-token token account
pub fn is_spl_token_account(account_info: &AccountInfo) -> bool {
assert_is_valid_spl_token_account(account_info).is_ok()
}
/// Asserts the given mint_info represents a valid SPL Token Mint account which is initialized and belongs to spl_token program
pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), ProgramError> {
if mint_info.data_is_empty() {
@ -210,7 +295,7 @@ pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), Pro
}
// In token program [36, 8, 1, is_initialized(1), 36] is the layout
let data = mint_info.try_borrow_data().unwrap();
let data = mint_info.try_borrow_data()?;
let is_initialized = array_ref![data, 45, 1];
if is_initialized == &[0] {
@ -220,6 +305,11 @@ pub fn assert_is_valid_spl_token_mint(mint_info: &AccountInfo) -> Result<(), Pro
Ok(())
}
/// Checks if the given account_info is be spl-token mint account
pub fn is_spl_token_mint(mint_info: &AccountInfo) -> bool {
assert_is_valid_spl_token_mint(mint_info).is_ok()
}
/// Computationally cheap method to get mint from a token account
/// It reads mint without deserializing full account data
pub fn get_spl_token_mint(token_account_info: &AccountInfo) -> Result<Pubkey, ProgramError> {

View File

@ -8,7 +8,10 @@ use solana_program_test::tokio;
use program_test::*;
use spl_governance::{
error::GovernanceError,
state::enums::{ProposalState, VoteThreshold, VoteTipping},
state::{
enums::{ProposalState, VoteThreshold, VoteTipping},
vote_record::Vote,
},
};
#[tokio::test]
@ -1243,3 +1246,52 @@ async fn test_cast_council_vote() {
proposal_account.vote_threshold
);
}
#[tokio::test]
async fn test_cast_vote_with_invalid_realm_config_account_address_error() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let realm_cookie = governance_test.with_realm().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let token_owner_record_cookie = governance_test
.with_community_token_deposit(&realm_cookie)
.await
.unwrap();
let mut governance_cookie = governance_test
.with_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.unwrap();
let proposal_cookie = governance_test
.with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie)
.await
.unwrap();
// Try bypass config check by using none existing config account
let realm_config_address = Pubkey::new_unique();
// Act
let err = governance_test
.with_cast_vote_using_instruction(
&proposal_cookie,
&token_owner_record_cookie,
Vote::Deny,
|i| {
i.accounts[10].pubkey = realm_config_address; // realm_config_address
},
None,
)
.await
.err()
.unwrap();
// Assert
assert_eq!(err, GovernanceError::InvalidRealmConfigAddress.into());
}

View File

@ -542,3 +542,41 @@ async fn test_create_proposal_with_disabled_community_vote_error() {
GovernanceError::GoverningTokenMintNotAllowedToVote.into()
);
}
#[tokio::test]
async fn test_create_proposal_with_invalid_realm_config_account_address_error() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let realm_cookie = governance_test.with_realm().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let token_owner_record_cookie = governance_test
.with_community_token_deposit(&realm_cookie)
.await
.unwrap();
let mut governance_cookie = governance_test
.with_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.unwrap();
// Try bypass config check by using none existing config account
let realm_config_address = Pubkey::new_unique();
// Act
let err = governance_test
.with_proposal_using_instruction(&token_owner_record_cookie, &mut governance_cookie, |i| {
i.accounts[8].pubkey = realm_config_address;
})
.await
.err()
.unwrap();
// Assert
assert_eq!(err, GovernanceError::InvalidRealmConfigAddress.into());
}

View File

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

View File

@ -7,7 +7,12 @@ mod program_test;
use program_test::*;
use solana_sdk::signature::{Keypair, Signer};
use spl_governance::{error::GovernanceError, instruction::deposit_governing_tokens};
use spl_governance::{
error::GovernanceError, instruction::deposit_governing_tokens,
state::realm_config::GoverningTokenType,
};
use crate::program_test::args::*;
#[tokio::test]
async fn test_deposit_initial_community_tokens() {
@ -235,55 +240,6 @@ async fn test_deposit_initial_community_tokens_with_owner_must_sign_error() {
// Assert
assert_eq!(error, GovernanceError::GoverningTokenOwnerMustSign.into());
}
#[tokio::test]
async fn test_deposit_initial_community_tokens_with_invalid_owner_error() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let realm_cookie = governance_test.with_realm().await;
let token_owner = Keypair::new();
let transfer_authority = Keypair::new();
let token_source = Keypair::new();
let invalid_owner = Keypair::new();
let amount = 10;
governance_test
.bench
.create_token_account_with_transfer_authority(
&token_source,
&realm_cookie.account.community_mint,
&realm_cookie.community_mint_authority,
amount,
&token_owner,
&transfer_authority.pubkey(),
)
.await;
let deposit_ix = deposit_governing_tokens(
&governance_test.program_id,
&realm_cookie.address,
&token_source.pubkey(),
&invalid_owner.pubkey(),
&transfer_authority.pubkey(),
&governance_test.bench.context.payer.pubkey(),
amount,
&realm_cookie.account.community_mint,
);
// // Act
let error = governance_test
.bench
.process_transaction(&[deposit_ix], Some(&[&transfer_authority, &invalid_owner]))
.await
.err()
.unwrap();
// Assert
assert_eq!(error, GovernanceError::GoverningTokenOwnerMustSign.into());
}
#[tokio::test]
async fn test_deposit_community_tokens_with_malicious_holding_account_error() {
@ -340,3 +296,62 @@ async fn test_deposit_community_tokens_with_malicious_holding_account_error() {
GovernanceError::InvalidGoverningTokenHoldingAccount.into()
);
}
#[tokio::test]
async fn test_deposit_community_tokens_using_mint() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let realm_cookie = governance_test.with_realm().await;
// Act
let token_owner_record_cookie = governance_test
.with_initial_governing_token_deposit_using_mint(
&realm_cookie.address,
&realm_cookie.account.community_mint,
&realm_cookie.community_mint_authority,
10,
None,
)
.await
.unwrap();
// Assert
let token_owner_record = governance_test
.get_token_owner_record_account(&token_owner_record_cookie.address)
.await;
assert_eq!(token_owner_record_cookie.account, token_owner_record);
let holding_account = governance_test
.get_token_account(&realm_cookie.community_token_holding_account)
.await;
assert_eq!(
token_owner_record.governing_token_deposit_amount,
holding_account.amount
);
}
#[tokio::test]
async fn test_deposit_comunity_tokens_with_cannot_deposit_dormant_tokens_error() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_config_args = RealmSetupArgs::default();
realm_config_args.council_token_config_args.token_type = GoverningTokenType::Dormant;
let realm_cookie = governance_test
.with_realm_using_args(&realm_config_args)
.await;
// Act
let err = governance_test
.with_council_token_deposit(&realm_cookie)
.await
.err()
.unwrap();
// Assert
assert_eq!(err, GovernanceError::CannotDepositDormantTokens.into());
}

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 spl_governance::{
error::GovernanceError,
state::{enums::MintMaxVoteWeightSource, realm::RealmConfigArgs},
state::{realm::GoverningTokenConfigAccountArgs, realm_config::GoverningTokenType},
};
use self::args::SetRealmConfigArgs;
use crate::program_test::args::RealmSetupArgs;
#[tokio::test]
async fn test_set_realm_config() {
@ -20,25 +20,12 @@ async fn test_set_realm_config() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let realm_setup_args = RealmSetupArgs::default();
// Act
governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
@ -57,27 +44,14 @@ async fn test_set_realm_config_with_authority_must_sign_error() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let realm_setup_args = RealmSetupArgs::default();
// Act
let err = governance_test
.set_realm_config_using_instruction(
&mut realm_cookie,
&set_realm_config_args,
&realm_setup_args,
|i| i.accounts[1].is_signer = false,
Some(&[]),
)
@ -96,20 +70,7 @@ async fn test_set_realm_config_with_no_authority_error() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let realm_setup_args = RealmSetupArgs::default();
governance_test
.set_realm_authority(&realm_cookie, None)
@ -121,7 +82,7 @@ async fn test_set_realm_config_with_no_authority_error() {
let err = governance_test
.set_realm_config_using_instruction(
&mut realm_cookie,
&set_realm_config_args,
&realm_setup_args,
|i| i.accounts[1].is_signer = false,
Some(&[]),
)
@ -140,20 +101,7 @@ async fn test_set_realm_config_with_invalid_authority_error() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let realm_setup_args = RealmSetupArgs::default();
let realm_cookie2 = governance_test.with_realm().await;
@ -163,7 +111,7 @@ async fn test_set_realm_config_with_invalid_authority_error() {
// Act
let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.err()
.unwrap();
@ -179,24 +127,12 @@ async fn test_set_realm_config_with_remove_council() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: false,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let mut realm_setup_args = RealmSetupArgs::default();
realm_setup_args.use_council_mint = false;
// Act
governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
@ -216,27 +152,14 @@ async fn test_set_realm_config_with_council_change_error() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let realm_setup_args = RealmSetupArgs::default();
// Try to replace council mint
realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique());
// Act
let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.err()
.unwrap();
@ -255,33 +178,21 @@ async fn test_set_realm_config_with_council_restore_error() {
let mut realm_cookie = governance_test.with_realm().await;
let realm_config_args = RealmConfigArgs {
use_council_mint: false,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
min_community_weight_to_create_governance: 10,
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
};
let mut set_realm_config_args = SetRealmConfigArgs {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
};
let mut realm_setup_args = RealmSetupArgs::default();
realm_setup_args.use_council_mint = false;
governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
// Try to restore council mint after removing it
set_realm_config_args.realm_config_args.use_council_mint = true;
realm_setup_args.use_council_mint = true;
realm_cookie.account.config.council_mint = serde::__private::Some(Pubkey::new_unique());
// Act
let err = governance_test
.set_realm_config(&mut realm_cookie, &set_realm_config_args)
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.err()
.unwrap();
@ -292,3 +203,134 @@ async fn test_set_realm_config_with_council_restore_error() {
GovernanceError::RealmCouncilMintChangeIsNotSupported.into()
);
}
#[tokio::test]
async fn test_set_realm_config_with_liquid_community_token_cannot_be_changed_to_memebership_error()
{
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_cookie = governance_test.with_realm().await;
let mut realm_setup_args = RealmSetupArgs::default();
// Try to change Community token type to Membership
realm_setup_args.community_token_config_args.token_type = GoverningTokenType::Membership;
// Act
let err = governance_test
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.err()
.unwrap();
// Assert
assert_eq!(
err,
GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into()
);
}
#[tokio::test]
async fn test_set_realm_config_for_community_token_config() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_cookie = governance_test.with_realm().await;
let mut realm_setup_args = RealmSetupArgs::default();
// Change Community token type to Dormant and set plugins
realm_setup_args.community_token_config_args = GoverningTokenConfigAccountArgs {
voter_weight_addin: Some(Pubkey::new_unique()),
max_voter_weight_addin: Some(Pubkey::new_unique()),
token_type: GoverningTokenType::Dormant,
};
// Act
governance_test
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
// Assert
let realm_config_account = governance_test
.get_realm_config_account(&realm_cookie.realm_config.address)
.await;
assert_eq!(
realm_config_account.community_token_config.token_type,
GoverningTokenType::Dormant
);
assert_eq!(
realm_config_account
.community_token_config
.voter_weight_addin,
realm_setup_args
.community_token_config_args
.voter_weight_addin
);
assert_eq!(
realm_config_account
.community_token_config
.max_voter_weight_addin,
realm_setup_args
.community_token_config_args
.max_voter_weight_addin
);
}
#[tokio::test]
async fn test_set_realm_config_for_council_token_config() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_cookie = governance_test.with_realm().await;
let mut realm_setup_args = RealmSetupArgs::default();
// Change Council token type to Membership and set plugins
realm_setup_args.council_token_config_args = GoverningTokenConfigAccountArgs {
voter_weight_addin: Some(Pubkey::new_unique()),
max_voter_weight_addin: Some(Pubkey::new_unique()),
token_type: GoverningTokenType::Membership,
};
// Act
governance_test
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
// Assert
let realm_config_account = governance_test
.get_realm_config_account(&realm_cookie.realm_config.address)
.await;
assert_eq!(
realm_config_account.council_token_config.token_type,
GoverningTokenType::Membership
);
assert_eq!(
realm_config_account.council_token_config.voter_weight_addin,
realm_setup_args
.council_token_config_args
.voter_weight_addin
);
assert_eq!(
realm_config_account
.council_token_config
.max_voter_weight_addin,
realm_setup_args
.council_token_config_args
.max_voter_weight_addin
);
}

View File

@ -9,10 +9,13 @@ use program_test::*;
use solana_sdk::signature::Signer;
use spl_governance::{
error::GovernanceError, instruction::withdraw_governing_tokens,
state::token_owner_record::get_token_owner_record_address,
error::GovernanceError,
instruction::withdraw_governing_tokens,
state::{realm_config::GoverningTokenType, token_owner_record::get_token_owner_record_address},
};
use crate::program_test::args::RealmSetupArgs;
#[tokio::test]
async fn test_withdraw_community_tokens() {
// Arrange
@ -420,3 +423,65 @@ async fn test_withdraw_governing_tokens_after_proposal_cancelled() {
source_account.amount
);
}
#[tokio::test]
async fn test_withdraw_council_tokens_with_cannot_withdraw_membership_tokens_error() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_config_args = RealmSetupArgs::default();
realm_config_args.council_token_config_args.token_type = GoverningTokenType::Membership;
let realm_cookie = governance_test
.with_realm_using_args(&realm_config_args)
.await;
let token_owner_record_cookie = governance_test
.with_council_token_deposit(&realm_cookie)
.await
.unwrap();
// Act
let err = governance_test
.withdraw_council_tokens(&realm_cookie, &token_owner_record_cookie)
.await
.err()
.unwrap();
// Assert
assert_eq!(err, GovernanceError::CannotWithdrawMembershipTokens.into());
}
#[tokio::test]
async fn test_withdraw_dormant_community_tokens() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_new().await;
let mut realm_cookie = governance_test.with_realm().await;
let token_owner_record_cookie = governance_test
.with_community_token_deposit(&realm_cookie)
.await
.unwrap();
let mut realm_setup_args = RealmSetupArgs::default();
realm_setup_args.community_token_config_args.token_type = GoverningTokenType::Dormant;
governance_test
.set_realm_config(&mut realm_cookie, &realm_setup_args)
.await
.unwrap();
// Act
governance_test
.withdraw_community_tokens(&realm_cookie, &token_owner_record_cookie)
.await
.unwrap();
// Assert
let token_owner_record = governance_test
.get_token_owner_record_account(&token_owner_record_cookie.address)
.await;
assert_eq!(0, token_owner_record.governing_token_deposit_amount);
}

View File

@ -1,28 +1,70 @@
use solana_program::pubkey::Pubkey;
use spl_governance::state::{enums::MintMaxVoteWeightSource, realm::RealmConfigArgs};
use spl_governance::state::{
enums::MintMaxVoteWeightSource, realm::GoverningTokenConfigAccountArgs,
};
#[derive(Clone, Debug, PartialEq)]
pub struct SetRealmConfigArgs {
pub realm_config_args: RealmConfigArgs,
pub community_voter_weight_addin: Option<Pubkey>,
pub max_community_voter_weight_addin: Option<Pubkey>,
pub struct RealmSetupArgs {
pub use_council_mint: bool,
pub min_community_weight_to_create_governance: u64,
pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource,
pub community_token_config_args: GoverningTokenConfigAccountArgs,
pub council_token_config_args: GoverningTokenConfigAccountArgs,
}
impl Default for SetRealmConfigArgs {
impl Default for RealmSetupArgs {
fn default() -> Self {
let realm_config_args = RealmConfigArgs {
Self {
use_council_mint: true,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::SupplyFraction(100),
community_token_config_args: GoverningTokenConfigAccountArgs::default(),
council_token_config_args: GoverningTokenConfigAccountArgs::default(),
min_community_weight_to_create_governance: 10,
community_mint_max_vote_weight_source: MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct PluginSetupArgs {
pub use_community_voter_weight_addin: bool,
pub use_max_community_voter_weight_addin: bool,
pub use_council_voter_weight_addin: bool,
pub use_max_council_voter_weight_addin: bool,
}
impl PluginSetupArgs {
#[allow(dead_code)]
pub const COMMUNITY_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs {
use_community_voter_weight_addin: true,
use_max_community_voter_weight_addin: false,
use_council_voter_weight_addin: false,
use_max_council_voter_weight_addin: false,
};
#[allow(dead_code)]
pub const COMMUNITY_MAX_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs {
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: true,
use_council_voter_weight_addin: false,
use_max_council_voter_weight_addin: false,
};
#[allow(dead_code)]
pub const COUNCIL_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs {
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
use_council_voter_weight_addin: true,
use_max_council_voter_weight_addin: false,
};
#[allow(dead_code)]
pub const COUNCIL_MAX_VOTER_WEIGHT: PluginSetupArgs = PluginSetupArgs {
use_community_voter_weight_addin: false,
use_max_community_voter_weight_addin: false,
use_council_voter_weight_addin: false,
use_max_council_voter_weight_addin: true,
};
#[allow(dead_code)]
pub const ALL: PluginSetupArgs = PluginSetupArgs {
use_community_voter_weight_addin: true,
use_max_community_voter_weight_addin: true,
use_council_voter_weight_addin: true,
use_max_council_voter_weight_addin: true,
};
Self {
realm_config_args,
community_voter_weight_addin: None,
max_community_voter_weight_addin: None,
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,15 +4,18 @@ use solana_program_test::*;
mod program_test;
use program_test::args::*;
use program_test::*;
use spl_governance::state::enums::ProposalState;
#[tokio::test]
async fn test_cast_vote_with_all_addin() {
async fn test_cast_community_vote_with_all_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::ALL)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -67,12 +70,76 @@ async fn test_cast_vote_with_all_addin() {
assert_eq!(proposal_account.state, ProposalState::Voting)
}
#[tokio::test]
async fn test_cast_council_vote_with_all_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::ALL)
.await;
let mut token_owner_record_cookie = governance_test
.with_council_token_owner_record(&realm_cookie)
.await;
// voter weight 120
governance_test
.with_voter_weight_addin_record(&mut token_owner_record_cookie)
.await
.unwrap();
// max voter weight 250
governance_test
.with_max_voter_weight_addin_record_impl(&mut token_owner_record_cookie, 250, None)
.await
.unwrap();
let governed_account_cookie = governance_test.with_governed_account().await;
let mut governance_cookie = governance_test
.with_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.unwrap();
let proposal_cookie = governance_test
.with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie)
.await
.unwrap();
// Act
let vote_record_cookie = governance_test
.with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes)
.await
.unwrap();
// Assert
let vote_record_account = governance_test
.get_vote_record_account(&vote_record_cookie.address)
.await;
assert_eq!(120, vote_record_account.voter_weight);
let proposal_account = governance_test
.get_proposal_account(&proposal_cookie.address)
.await;
assert_eq!(proposal_account.state, ProposalState::Voting)
}
#[tokio::test]
async fn test_tip_vote_with_all_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::ALL)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -132,7 +199,9 @@ async fn test_finalize_vote_with_all_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_all_addins().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::ALL)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)

View File

@ -4,16 +4,19 @@ use solana_program_test::*;
mod program_test;
use program_test::args::*;
use program_test::*;
use spl_governance::{error::GovernanceError, state::enums::ProposalState};
#[tokio::test]
async fn test_cast_vote_with_max_voter_weight_addin() {
async fn test_cast_vote_with_community_max_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test
@ -56,13 +59,66 @@ async fn test_cast_vote_with_max_voter_weight_addin() {
assert_eq!(proposal_account.state, ProposalState::Voting)
}
#[tokio::test]
async fn test_cast_vote_with_council_max_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COUNCIL_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test
.with_council_token_deposit(&realm_cookie)
.await
.unwrap();
// Bump MaxVoterWeight to 200
governance_test
.with_max_voter_weight_addin_record(&mut token_owner_record_cookie)
.await
.unwrap();
let mut governance_cookie = governance_test
.with_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.unwrap();
let proposal_cookie = governance_test
.with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie)
.await
.unwrap();
// Act
governance_test
.with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes)
.await
.unwrap();
// Assert
let proposal_account = governance_test
.get_proposal_account(&proposal_cookie.address)
.await;
assert_eq!(proposal_account.state, ProposalState::Voting)
}
#[tokio::test]
async fn test_tip_vote_with_max_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 180
let mut token_owner_record_cookie = governance_test
@ -112,7 +168,9 @@ async fn test_tip_vote_with_max_voter_weight_addin_and_max_below_total_cast_vote
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test
@ -162,7 +220,9 @@ async fn test_finalize_vote_with_max_voter_weight_addin() {
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test
@ -233,7 +293,9 @@ async fn test_finalize_vote_with_max_voter_weight_addin_and_max_below_total_cast
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test
@ -304,7 +366,9 @@ async fn test_cast_vote_with_max_voter_weight_addin_and_expired_record_error() {
let mut governance_test = GovernanceProgramTest::start_with_max_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_MAX_VOTER_WEIGHT)
.await;
// TokenOwnerRecord with voting power of 100
let mut token_owner_record_cookie = governance_test

View File

@ -5,7 +5,9 @@ use solana_program_test::*;
mod program_test;
use program_test::args::*;
use program_test::*;
use spl_governance::{
error::GovernanceError,
state::vote_record::{Vote, VoteChoice},
@ -13,12 +15,14 @@ use spl_governance::{
use spl_governance_addin_api::voter_weight::VoterWeightAction;
#[tokio::test]
async fn test_create_governance_with_voter_weight_addin() {
async fn test_create_governance_with_community_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -47,13 +51,52 @@ async fn test_create_governance_with_voter_weight_addin() {
assert_eq!(governance_cookie.account, governance_account);
}
#[tokio::test]
async fn test_create_governance_with_council_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COUNCIL_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_council_token_owner_record(&realm_cookie)
.await;
governance_test
.with_voter_weight_addin_record(&mut token_owner_record_cookie)
.await
.unwrap();
// Act
let governance_cookie = governance_test
.with_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.unwrap();
// // Assert
let governance_account = governance_test
.get_governance_account(&governance_cookie.address)
.await;
assert_eq!(governance_cookie.account, governance_account);
}
#[tokio::test]
async fn test_create_proposal_with_voter_weight_addin() {
// Arrange
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -93,7 +136,9 @@ async fn test_cast_vote_with_voter_weight_addin() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -152,7 +197,9 @@ async fn test_create_token_governance_with_voter_weight_addin() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_token_cookie = governance_test.with_governed_token().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -187,7 +234,9 @@ async fn test_create_mint_governance_with_voter_weight_addin() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_mint_cookie = governance_test.with_governed_mint().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -222,7 +271,9 @@ async fn test_create_program_governance_with_voter_weight_addin() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_program_cookie = governance_test.with_governed_program().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -260,7 +311,9 @@ async fn test_create_governance_with_voter_weight_action_error() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -298,7 +351,9 @@ async fn test_create_governance_with_voter_weight_expiry_error() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -338,7 +393,9 @@ async fn test_cast_vote_with_voter_weight_action_error() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -394,7 +451,9 @@ async fn test_create_governance_with_voter_weight_action_target_error() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -437,7 +496,9 @@ async fn test_create_proposal_with_voter_weight_action_error() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)
@ -481,7 +542,9 @@ async fn test_create_governance_with_voter_weight_record() {
let mut governance_test = GovernanceProgramTest::start_with_voter_weight_addin().await;
let governed_account_cookie = governance_test.with_governed_account().await;
let realm_cookie = governance_test.with_realm().await;
let realm_cookie = governance_test
.with_realm_using_addins(PluginSetupArgs::COMMUNITY_VOTER_WEIGHT)
.await;
let mut token_owner_record_cookie = governance_test
.with_community_token_owner_record(&realm_cookie)

View File

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