Governance: Assert tokens to create governance (#2206)
* feat: assert owner has enough tokens to create proposal * chore: test min tokens to create governance * chore: bump version
This commit is contained in:
parent
7a593f5da2
commit
2e4b49b72c
|
@ -3776,7 +3776,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spl-governance"
|
name = "spl-governance"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"arrayref",
|
||||||
"assert_matches",
|
"assert_matches",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "spl-governance"
|
name = "spl-governance"
|
||||||
version = "1.0.8"
|
version = "1.0.9"
|
||||||
description = "Solana Program Library Governance Program"
|
description = "Solana Program Library Governance Program"
|
||||||
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
repository = "https://github.com/solana-labs/solana-program-library"
|
repository = "https://github.com/solana-labs/solana-program-library"
|
||||||
|
|
|
@ -311,6 +311,10 @@ pub enum GovernanceError {
|
||||||
/// Invalid max vote weight supply fraction
|
/// Invalid max vote weight supply fraction
|
||||||
#[error("Invalid max vote weight supply fraction")]
|
#[error("Invalid max vote weight supply fraction")]
|
||||||
InvalidMaxVoteWeightSupplyFraction,
|
InvalidMaxVoteWeightSupplyFraction,
|
||||||
|
|
||||||
|
/// Owner doesn't have enough governing tokens to create Governance
|
||||||
|
#[error("Owner doesn't have enough governing tokens to create Governance")]
|
||||||
|
NotEnoughTokensToCreateGovernance,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrintProgramError for GovernanceError {
|
impl PrintProgramError for GovernanceError {
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
||||||
assert_valid_create_governance_args, get_account_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_account_governance_address_seeds, Governance,
|
||||||
GovernanceConfig,
|
GovernanceConfig,
|
||||||
},
|
},
|
||||||
|
realm::get_realm_data,
|
||||||
|
token_owner_record::get_token_owner_record_data_for_realm,
|
||||||
},
|
},
|
||||||
tools::account::create_and_serialize_account_signed,
|
tools::account::create_and_serialize_account_signed,
|
||||||
};
|
};
|
||||||
|
@ -30,7 +32,7 @@ pub fn process_create_account_governance(
|
||||||
let account_governance_info = next_account_info(account_info_iter)?; // 1
|
let account_governance_info = next_account_info(account_info_iter)?; // 1
|
||||||
let governed_account_info = next_account_info(account_info_iter)?; // 2
|
let governed_account_info = next_account_info(account_info_iter)?; // 2
|
||||||
|
|
||||||
let _token_owner_record_info = next_account_info(account_info_iter)?; // 3
|
let token_owner_record_info = next_account_info(account_info_iter)?; // 3
|
||||||
|
|
||||||
let payer_info = next_account_info(account_info_iter)?; // 4
|
let payer_info = next_account_info(account_info_iter)?; // 4
|
||||||
let system_info = next_account_info(account_info_iter)?; // 5
|
let system_info = next_account_info(account_info_iter)?; // 5
|
||||||
|
@ -40,6 +42,12 @@ pub fn process_create_account_governance(
|
||||||
|
|
||||||
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
||||||
|
|
||||||
|
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||||
|
let token_owner_record_data =
|
||||||
|
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
|
||||||
|
|
||||||
|
token_owner_record_data.assert_can_create_governance(&realm_data)?;
|
||||||
|
|
||||||
let account_governance_data = Governance {
|
let account_governance_data = Governance {
|
||||||
account_type: GovernanceAccountType::AccountGovernance,
|
account_type: GovernanceAccountType::AccountGovernance,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
||||||
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
|
||||||
GovernanceConfig,
|
GovernanceConfig,
|
||||||
},
|
},
|
||||||
|
realm::get_realm_data,
|
||||||
|
token_owner_record::get_token_owner_record_data_for_realm,
|
||||||
},
|
},
|
||||||
tools::{
|
tools::{
|
||||||
account::create_and_serialize_account_signed,
|
account::create_and_serialize_account_signed,
|
||||||
|
@ -36,7 +38,7 @@ pub fn process_create_mint_governance(
|
||||||
let governed_mint_info = next_account_info(account_info_iter)?; // 2
|
let governed_mint_info = next_account_info(account_info_iter)?; // 2
|
||||||
let governed_mint_authority_info = next_account_info(account_info_iter)?; // 3
|
let governed_mint_authority_info = next_account_info(account_info_iter)?; // 3
|
||||||
|
|
||||||
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
||||||
|
|
||||||
let payer_info = next_account_info(account_info_iter)?; // 5
|
let payer_info = next_account_info(account_info_iter)?; // 5
|
||||||
let spl_token_info = next_account_info(account_info_iter)?; // 6
|
let spl_token_info = next_account_info(account_info_iter)?; // 6
|
||||||
|
@ -48,6 +50,12 @@ pub fn process_create_mint_governance(
|
||||||
|
|
||||||
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
||||||
|
|
||||||
|
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||||
|
let token_owner_record_data =
|
||||||
|
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
|
||||||
|
|
||||||
|
token_owner_record_data.assert_can_create_governance(&realm_data)?;
|
||||||
|
|
||||||
let mint_governance_data = Governance {
|
let mint_governance_data = Governance {
|
||||||
account_type: GovernanceAccountType::MintGovernance,
|
account_type: GovernanceAccountType::MintGovernance,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
|
|
|
@ -8,6 +8,8 @@ use crate::{
|
||||||
assert_valid_create_governance_args, get_program_governance_address_seeds,
|
assert_valid_create_governance_args, get_program_governance_address_seeds,
|
||||||
GovernanceConfig,
|
GovernanceConfig,
|
||||||
},
|
},
|
||||||
|
realm::get_realm_data,
|
||||||
|
token_owner_record::get_token_owner_record_data_for_realm,
|
||||||
},
|
},
|
||||||
tools::{
|
tools::{
|
||||||
account::create_and_serialize_account_signed,
|
account::create_and_serialize_account_signed,
|
||||||
|
@ -40,7 +42,7 @@ pub fn process_create_program_governance(
|
||||||
let governed_program_data_info = next_account_info(account_info_iter)?; // 2
|
let governed_program_data_info = next_account_info(account_info_iter)?; // 2
|
||||||
let governed_program_upgrade_authority_info = next_account_info(account_info_iter)?; // 3
|
let governed_program_upgrade_authority_info = next_account_info(account_info_iter)?; // 3
|
||||||
|
|
||||||
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
||||||
|
|
||||||
let payer_info = next_account_info(account_info_iter)?; // 5
|
let payer_info = next_account_info(account_info_iter)?; // 5
|
||||||
let bpf_upgrade_loader_info = next_account_info(account_info_iter)?; // 6
|
let bpf_upgrade_loader_info = next_account_info(account_info_iter)?; // 6
|
||||||
|
@ -52,6 +54,12 @@ pub fn process_create_program_governance(
|
||||||
|
|
||||||
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
||||||
|
|
||||||
|
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||||
|
let token_owner_record_data =
|
||||||
|
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
|
||||||
|
|
||||||
|
token_owner_record_data.assert_can_create_governance(&realm_data)?;
|
||||||
|
|
||||||
let program_governance_data = Governance {
|
let program_governance_data = Governance {
|
||||||
account_type: GovernanceAccountType::ProgramGovernance,
|
account_type: GovernanceAccountType::ProgramGovernance,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
||||||
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
|
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
|
||||||
GovernanceConfig,
|
GovernanceConfig,
|
||||||
},
|
},
|
||||||
|
realm::get_realm_data,
|
||||||
|
token_owner_record::get_token_owner_record_data_for_realm,
|
||||||
},
|
},
|
||||||
tools::{
|
tools::{
|
||||||
account::create_and_serialize_account_signed,
|
account::create_and_serialize_account_signed,
|
||||||
|
@ -36,7 +38,7 @@ pub fn process_create_token_governance(
|
||||||
let governed_token_info = next_account_info(account_info_iter)?; // 2
|
let governed_token_info = next_account_info(account_info_iter)?; // 2
|
||||||
let governed_token_owner_info = next_account_info(account_info_iter)?; // 3
|
let governed_token_owner_info = next_account_info(account_info_iter)?; // 3
|
||||||
|
|
||||||
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
|
||||||
|
|
||||||
let payer_info = next_account_info(account_info_iter)?; // 5
|
let payer_info = next_account_info(account_info_iter)?; // 5
|
||||||
let spl_token_info = next_account_info(account_info_iter)?; // 6
|
let spl_token_info = next_account_info(account_info_iter)?; // 6
|
||||||
|
@ -48,6 +50,12 @@ pub fn process_create_token_governance(
|
||||||
|
|
||||||
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
assert_valid_create_governance_args(program_id, &config, realm_info)?;
|
||||||
|
|
||||||
|
let realm_data = get_realm_data(program_id, realm_info)?;
|
||||||
|
let token_owner_record_data =
|
||||||
|
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
|
||||||
|
|
||||||
|
token_owner_record_data.assert_can_create_governance(&realm_data)?;
|
||||||
|
|
||||||
let token_governance_data = Governance {
|
let token_governance_data = Governance {
|
||||||
account_type: GovernanceAccountType::TokenGovernance,
|
account_type: GovernanceAccountType::TokenGovernance,
|
||||||
realm: *realm_info.key,
|
realm: *realm_info.key,
|
||||||
|
|
|
@ -105,6 +105,25 @@ impl TokenOwnerRecord {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Asserts TokenOwner has enough tokens to be allowed to create governance
|
||||||
|
pub fn assert_can_create_governance(&self, realm_data: &Realm) -> Result<(), ProgramError> {
|
||||||
|
let min_tokens_to_create_governance =
|
||||||
|
if self.governing_token_mint == realm_data.community_mint {
|
||||||
|
realm_data.config.min_community_tokens_to_create_governance
|
||||||
|
} else if Some(self.governing_token_mint) == realm_data.config.council_mint {
|
||||||
|
// For council tokens it's enough to be in possession of any number of tokens
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
return Err(GovernanceError::InvalidGoverningTokenMint.into());
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.governing_token_deposit_amount < min_tokens_to_create_governance {
|
||||||
|
return Err(GovernanceError::NotEnoughTokensToCreateGovernance.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns TokenOwnerRecord PDA address
|
/// Returns TokenOwnerRecord PDA address
|
||||||
|
|
|
@ -130,3 +130,69 @@ async fn test_create_account_governance_with_invalid_config_error() {
|
||||||
|
|
||||||
assert_eq!(err, GovernanceError::InvalidVoteThresholdPercentage.into());
|
assert_eq!(err, GovernanceError::InvalidVoteThresholdPercentage.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_create_account_governance_with_not_enough_community_tokens_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;
|
||||||
|
|
||||||
|
// Set token deposit amount below the required threshold
|
||||||
|
let token_amount = 4;
|
||||||
|
|
||||||
|
let token_owner_record_cookie = governance_test
|
||||||
|
.with_community_token_deposit_amount(&realm_cookie, token_amount)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let err = governance_test
|
||||||
|
.with_account_governance(
|
||||||
|
&realm_cookie,
|
||||||
|
&governed_account_cookie,
|
||||||
|
&token_owner_record_cookie,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
GovernanceError::NotEnoughTokensToCreateGovernance.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_create_account_governance_with_not_enough_council_tokens_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;
|
||||||
|
|
||||||
|
// Set token deposit amount below the required threshold
|
||||||
|
let token_amount: u64 = 0;
|
||||||
|
|
||||||
|
let token_owner_record_cookie = governance_test
|
||||||
|
.with_council_token_deposit_amount(&realm_cookie, token_amount)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
let err = governance_test
|
||||||
|
.with_account_governance(
|
||||||
|
&realm_cookie,
|
||||||
|
&governed_account_cookie,
|
||||||
|
&token_owner_record_cookie,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.err()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
assert_eq!(
|
||||||
|
err,
|
||||||
|
GovernanceError::NotEnoughTokensToCreateGovernance.into()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -202,25 +202,29 @@ async fn test_create_proposal_with_not_enough_community_tokens_error() {
|
||||||
let realm_cookie = governance_test.with_realm().await;
|
let realm_cookie = governance_test.with_realm().await;
|
||||||
let governed_account_cookie = governance_test.with_governed_account().await;
|
let governed_account_cookie = governance_test.with_governed_account().await;
|
||||||
|
|
||||||
// Set token deposit amount below the required threshold
|
let token_owner_record_cookie1 = governance_test
|
||||||
let token_amount = 4;
|
.with_community_token_deposit(&realm_cookie)
|
||||||
|
|
||||||
let token_owner_record_cookie = governance_test
|
|
||||||
.with_community_token_deposit_amount(&realm_cookie, token_amount)
|
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let mut account_governance_cookie = governance_test
|
let mut account_governance_cookie = governance_test
|
||||||
.with_account_governance(
|
.with_account_governance(
|
||||||
&realm_cookie,
|
&realm_cookie,
|
||||||
&governed_account_cookie,
|
&governed_account_cookie,
|
||||||
&token_owner_record_cookie,
|
&token_owner_record_cookie1,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// Set token deposit amount below the required threshold
|
||||||
|
let token_amount = 4;
|
||||||
|
|
||||||
|
let token_owner_record_cookie2 = governance_test
|
||||||
|
.with_community_token_deposit_amount(&realm_cookie, token_amount)
|
||||||
|
.await;
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
let err = governance_test
|
let err = governance_test
|
||||||
.with_proposal(&token_owner_record_cookie, &mut account_governance_cookie)
|
.with_proposal(&token_owner_record_cookie2, &mut account_governance_cookie)
|
||||||
.await
|
.await
|
||||||
.err()
|
.err()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
Loading…
Reference in New Issue