Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 2e4b49b

Browse files
authored
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
1 parent 7a593f5 commit 2e4b49b

10 files changed

+138
-13
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

governance/program/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "spl-governance"
3-
version = "1.0.8"
3+
version = "1.0.9"
44
description = "Solana Program Library Governance Program"
55
authors = ["Solana Maintainers <[email protected]>"]
66
repository = "https://github.com/solana-labs/solana-program-library"

governance/program/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,10 @@ pub enum GovernanceError {
311311
/// Invalid max vote weight supply fraction
312312
#[error("Invalid max vote weight supply fraction")]
313313
InvalidMaxVoteWeightSupplyFraction,
314+
315+
/// Owner doesn't have enough governing tokens to create Governance
316+
#[error("Owner doesn't have enough governing tokens to create Governance")]
317+
NotEnoughTokensToCreateGovernance,
314318
}
315319

316320
impl PrintProgramError for GovernanceError {

governance/program/src/processor/process_create_account_governance.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::{
77
assert_valid_create_governance_args, get_account_governance_address_seeds, Governance,
88
GovernanceConfig,
99
},
10+
realm::get_realm_data,
11+
token_owner_record::get_token_owner_record_data_for_realm,
1012
},
1113
tools::account::create_and_serialize_account_signed,
1214
};
@@ -30,7 +32,7 @@ pub fn process_create_account_governance(
3032
let account_governance_info = next_account_info(account_info_iter)?; // 1
3133
let governed_account_info = next_account_info(account_info_iter)?; // 2
3234

33-
let _token_owner_record_info = next_account_info(account_info_iter)?; // 3
35+
let token_owner_record_info = next_account_info(account_info_iter)?; // 3
3436

3537
let payer_info = next_account_info(account_info_iter)?; // 4
3638
let system_info = next_account_info(account_info_iter)?; // 5
@@ -40,6 +42,12 @@ pub fn process_create_account_governance(
4042

4143
assert_valid_create_governance_args(program_id, &config, realm_info)?;
4244

45+
let realm_data = get_realm_data(program_id, realm_info)?;
46+
let token_owner_record_data =
47+
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
48+
49+
token_owner_record_data.assert_can_create_governance(&realm_data)?;
50+
4351
let account_governance_data = Governance {
4452
account_type: GovernanceAccountType::AccountGovernance,
4553
realm: *realm_info.key,

governance/program/src/processor/process_create_mint_governance.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::{
77
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
88
GovernanceConfig,
99
},
10+
realm::get_realm_data,
11+
token_owner_record::get_token_owner_record_data_for_realm,
1012
},
1113
tools::{
1214
account::create_and_serialize_account_signed,
@@ -36,7 +38,7 @@ pub fn process_create_mint_governance(
3638
let governed_mint_info = next_account_info(account_info_iter)?; // 2
3739
let governed_mint_authority_info = next_account_info(account_info_iter)?; // 3
3840

39-
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
41+
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
4042

4143
let payer_info = next_account_info(account_info_iter)?; // 5
4244
let spl_token_info = next_account_info(account_info_iter)?; // 6
@@ -48,6 +50,12 @@ pub fn process_create_mint_governance(
4850

4951
assert_valid_create_governance_args(program_id, &config, realm_info)?;
5052

53+
let realm_data = get_realm_data(program_id, realm_info)?;
54+
let token_owner_record_data =
55+
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
56+
57+
token_owner_record_data.assert_can_create_governance(&realm_data)?;
58+
5159
let mint_governance_data = Governance {
5260
account_type: GovernanceAccountType::MintGovernance,
5361
realm: *realm_info.key,

governance/program/src/processor/process_create_program_governance.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use crate::{
88
assert_valid_create_governance_args, get_program_governance_address_seeds,
99
GovernanceConfig,
1010
},
11+
realm::get_realm_data,
12+
token_owner_record::get_token_owner_record_data_for_realm,
1113
},
1214
tools::{
1315
account::create_and_serialize_account_signed,
@@ -40,7 +42,7 @@ pub fn process_create_program_governance(
4042
let governed_program_data_info = next_account_info(account_info_iter)?; // 2
4143
let governed_program_upgrade_authority_info = next_account_info(account_info_iter)?; // 3
4244

43-
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
45+
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
4446

4547
let payer_info = next_account_info(account_info_iter)?; // 5
4648
let bpf_upgrade_loader_info = next_account_info(account_info_iter)?; // 6
@@ -52,6 +54,12 @@ pub fn process_create_program_governance(
5254

5355
assert_valid_create_governance_args(program_id, &config, realm_info)?;
5456

57+
let realm_data = get_realm_data(program_id, realm_info)?;
58+
let token_owner_record_data =
59+
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
60+
61+
token_owner_record_data.assert_can_create_governance(&realm_data)?;
62+
5563
let program_governance_data = Governance {
5664
account_type: GovernanceAccountType::ProgramGovernance,
5765
realm: *realm_info.key,

governance/program/src/processor/process_create_token_governance.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use crate::{
77
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
88
GovernanceConfig,
99
},
10+
realm::get_realm_data,
11+
token_owner_record::get_token_owner_record_data_for_realm,
1012
},
1113
tools::{
1214
account::create_and_serialize_account_signed,
@@ -36,7 +38,7 @@ pub fn process_create_token_governance(
3638
let governed_token_info = next_account_info(account_info_iter)?; // 2
3739
let governed_token_owner_info = next_account_info(account_info_iter)?; // 3
3840

39-
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
41+
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
4042

4143
let payer_info = next_account_info(account_info_iter)?; // 5
4244
let spl_token_info = next_account_info(account_info_iter)?; // 6
@@ -48,6 +50,12 @@ pub fn process_create_token_governance(
4850

4951
assert_valid_create_governance_args(program_id, &config, realm_info)?;
5052

53+
let realm_data = get_realm_data(program_id, realm_info)?;
54+
let token_owner_record_data =
55+
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
56+
57+
token_owner_record_data.assert_can_create_governance(&realm_data)?;
58+
5159
let token_governance_data = Governance {
5260
account_type: GovernanceAccountType::TokenGovernance,
5361
realm: *realm_info.key,

governance/program/src/state/token_owner_record.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,25 @@ impl TokenOwnerRecord {
105105

106106
Ok(())
107107
}
108+
109+
/// Asserts TokenOwner has enough tokens to be allowed to create governance
110+
pub fn assert_can_create_governance(&self, realm_data: &Realm) -> Result<(), ProgramError> {
111+
let min_tokens_to_create_governance =
112+
if self.governing_token_mint == realm_data.community_mint {
113+
realm_data.config.min_community_tokens_to_create_governance
114+
} else if Some(self.governing_token_mint) == realm_data.config.council_mint {
115+
// For council tokens it's enough to be in possession of any number of tokens
116+
1
117+
} else {
118+
return Err(GovernanceError::InvalidGoverningTokenMint.into());
119+
};
120+
121+
if self.governing_token_deposit_amount < min_tokens_to_create_governance {
122+
return Err(GovernanceError::NotEnoughTokensToCreateGovernance.into());
123+
}
124+
125+
Ok(())
126+
}
108127
}
109128

110129
/// Returns TokenOwnerRecord PDA address

governance/program/tests/process_create_account_governance.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,69 @@ async fn test_create_account_governance_with_invalid_config_error() {
130130

131131
assert_eq!(err, GovernanceError::InvalidVoteThresholdPercentage.into());
132132
}
133+
134+
#[tokio::test]
135+
async fn test_create_account_governance_with_not_enough_community_tokens_error() {
136+
// Arrange
137+
let mut governance_test = GovernanceProgramTest::start_new().await;
138+
139+
let realm_cookie = governance_test.with_realm().await;
140+
let governed_account_cookie = governance_test.with_governed_account().await;
141+
142+
// Set token deposit amount below the required threshold
143+
let token_amount = 4;
144+
145+
let token_owner_record_cookie = governance_test
146+
.with_community_token_deposit_amount(&realm_cookie, token_amount)
147+
.await;
148+
149+
// Act
150+
let err = governance_test
151+
.with_account_governance(
152+
&realm_cookie,
153+
&governed_account_cookie,
154+
&token_owner_record_cookie,
155+
)
156+
.await
157+
.err()
158+
.unwrap();
159+
160+
// Assert
161+
assert_eq!(
162+
err,
163+
GovernanceError::NotEnoughTokensToCreateGovernance.into()
164+
);
165+
}
166+
167+
#[tokio::test]
168+
async fn test_create_account_governance_with_not_enough_council_tokens_error() {
169+
// Arrange
170+
let mut governance_test = GovernanceProgramTest::start_new().await;
171+
172+
let realm_cookie = governance_test.with_realm().await;
173+
let governed_account_cookie = governance_test.with_governed_account().await;
174+
175+
// Set token deposit amount below the required threshold
176+
let token_amount: u64 = 0;
177+
178+
let token_owner_record_cookie = governance_test
179+
.with_council_token_deposit_amount(&realm_cookie, token_amount)
180+
.await;
181+
182+
// Act
183+
let err = governance_test
184+
.with_account_governance(
185+
&realm_cookie,
186+
&governed_account_cookie,
187+
&token_owner_record_cookie,
188+
)
189+
.await
190+
.err()
191+
.unwrap();
192+
193+
// Assert
194+
assert_eq!(
195+
err,
196+
GovernanceError::NotEnoughTokensToCreateGovernance.into()
197+
);
198+
}

governance/program/tests/process_create_proposal.rs

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -202,25 +202,29 @@ async fn test_create_proposal_with_not_enough_community_tokens_error() {
202202
let realm_cookie = governance_test.with_realm().await;
203203
let governed_account_cookie = governance_test.with_governed_account().await;
204204

205-
// Set token deposit amount below the required threshold
206-
let token_amount = 4;
207-
208-
let token_owner_record_cookie = governance_test
209-
.with_community_token_deposit_amount(&realm_cookie, token_amount)
205+
let token_owner_record_cookie1 = governance_test
206+
.with_community_token_deposit(&realm_cookie)
210207
.await;
211208

212209
let mut account_governance_cookie = governance_test
213210
.with_account_governance(
214211
&realm_cookie,
215212
&governed_account_cookie,
216-
&token_owner_record_cookie,
213+
&token_owner_record_cookie1,
217214
)
218215
.await
219216
.unwrap();
220217

218+
// Set token deposit amount below the required threshold
219+
let token_amount = 4;
220+
221+
let token_owner_record_cookie2 = governance_test
222+
.with_community_token_deposit_amount(&realm_cookie, token_amount)
223+
.await;
224+
221225
// Act
222226
let err = governance_test
223-
.with_proposal(&token_owner_record_cookie, &mut account_governance_cookie)
227+
.with_proposal(&token_owner_record_cookie2, &mut account_governance_cookie)
224228
.await
225229
.err()
226230
.unwrap();

0 commit comments

Comments
 (0)