Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Governance: voters weights add-in #2450

Merged
merged 22 commits into from
Oct 13, 2021
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ea8e065
feat: setup and configure voter weight addin
SebastianBor Sep 23, 2021
f24663c
feat: use voter weight addin to create proposal
SebastianBor Oct 1, 2021
a344fe6
feat: use voter weight addin to cast vote
SebastianBor Oct 1, 2021
825551b
chore: make clippy happy
SebastianBor Oct 1, 2021
cf057e0
feat: use voter weight addin to create token governance
SebastianBor Oct 1, 2021
1c2f32b
feat: use voter weight addin to create mint governance
SebastianBor Oct 1, 2021
b320ab4
feat: use voter weight adding to create program governance
SebastianBor Oct 1, 2021
63144cb
chore: create assert_can_withdraw_governing_tokens() helper function
SebastianBor Oct 1, 2021
3988d76
chore: fix compilation
SebastianBor Oct 1, 2021
42a4893
fix: ensure governance authority signed transaction to create governance
SebastianBor Oct 1, 2021
00143ab
feat: implement CreateTokenOwnerRecord instruction
SebastianBor Oct 1, 2021
d4e28b9
chore: fix chat tests
SebastianBor Oct 1, 2021
e4b250f
chore: update comments
SebastianBor Oct 1, 2021
9e086d8
chore: rename RealmAddins account to RealmConfig account
SebastianBor Oct 2, 2021
941225d
chore: add more reserved space to GovernanceConfig account
SebastianBor Oct 2, 2021
dae9d87
chore: update instruction comments
SebastianBor Oct 2, 2021
a9b3ff9
chore: update comments
SebastianBor Oct 2, 2021
a531084
chore: fix compilation
SebastianBor Oct 2, 2021
6a470c0
chore: remove ignore directive for tests
SebastianBor Oct 11, 2021
596be2d
feat: panic when depositing tokens into a realm with voter weight addin
SebastianBor Oct 11, 2021
82a07d0
chore: rename community_voter_weight to community_voter_weight_addin
SebastianBor Oct 11, 2021
2d66e92
feat: make payer account optional for SetRealmConfig
SebastianBor Oct 12, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 44 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ members = [
"examples/rust/transfer-lamports",
"feature-proposal/program",
"feature-proposal/cli",
"governance/voter-weight-addin/program",
"governance/program",
"governance/test-sdk",
"governance/chat/program",
Expand Down
2 changes: 1 addition & 1 deletion governance/chat/program/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ serde = "1.0.127"
serde_derive = "1.0.103"
solana-program = "1.7.11"
spl-token = { version = "3.2", path = "../../../token/program", features = [ "no-entrypoint" ] }
spl-governance= { version = "1.1.0", path ="../../program", features = [ "no-entrypoint" ]}
spl-governance= { version = "2.1.0", path ="../../program", features = [ "no-entrypoint" ]}
thiserror = "1.0"


Expand Down
8 changes: 6 additions & 2 deletions governance/chat/program/tests/program_test/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ impl GovernanceChatProgramTest {
&governing_token_mint_keypair.pubkey(),
&self.bench.payer.pubkey(),
None,
None,
name.clone(),
1,
MintMaxVoteWeightSource::FULL_SUPPLY_FRACTION,
Expand Down Expand Up @@ -155,11 +156,13 @@ impl GovernanceChatProgramTest {
&governed_account_address,
&token_owner_record_address,
&self.bench.payer.pubkey(),
&token_owner.pubkey(),
None,
governance_config,
);

self.bench
.process_transaction(&[create_account_governance_ix], None)
.process_transaction(&[create_account_governance_ix], Some(&[&token_owner]))
.await
.unwrap();

Expand All @@ -173,14 +176,15 @@ impl GovernanceChatProgramTest {

let proposal_name = "Proposal #1".to_string();
let description_link = "Proposal Description".to_string();
let proposal_index = 0;
let proposal_index: u32 = 0;

let create_proposal_ix = create_proposal(
&self.governance_program_id,
&governance_address,
&token_owner_record_address,
&token_owner.pubkey(),
&self.bench.payer.pubkey(),
None,
&realm_address,
proposal_name,
description_link.clone(),
Expand Down
4 changes: 3 additions & 1 deletion governance/program/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "spl-governance"
version = "1.1.1"
version = "2.1.1"
description = "Solana Program Library Governance Program"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana-program-library"
Expand Down Expand Up @@ -30,6 +30,8 @@ proptest = "1.0"
solana-program-test = "1.7.11"
solana-sdk = "1.7.11"
spl-governance-test-sdk = { version = "0.1.0", path ="../test-sdk"}
spl-governance-v1 = {package="spl-governance", version = "1.1.1", features = [ "no-entrypoint" ] }


[lib]
crate-type = ["cdylib", "lib"]
2 changes: 2 additions & 0 deletions governance/program/src/addins/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//! Governance add-ins interfaces
pub mod voter_weight;
110 changes: 110 additions & 0 deletions governance/program/src/addins/voter_weight.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//! VoterWeight Addin interface

use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};
use solana_program::{
account_info::AccountInfo,
clock::{Clock, Slot},
program_error::ProgramError,
program_pack::IsInitialized,
pubkey::Pubkey,
sysvar::Sysvar,
};

use crate::{
error::GovernanceError,
state::token_owner_record::TokenOwnerRecord,
tools::account::{get_account_data, AccountMaxSize},
};

/// VoterWeight account type
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum VoterWeightAccountType {
/// Default uninitialized account state
Uninitialized,

/// Voter Weight Record
VoterWeightRecord,
}

/// VoterWeight Record account
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub struct VoterWeightRecord {
/// VoterWeightRecord account type
pub account_type: VoterWeightAccountType,

/// The Realm the VoterWeightRecord belongs to
pub realm: Pubkey,

/// Governing Token Mint the VoterWeightRecord is associated with
/// Note: The addin can take deposits of any tokens and is not restricted to the community or council tokens only
// The mint here is to link the record to either community or council mint of the realm
pub governing_token_mint: Pubkey,

/// The owner of the governing token and voter
pub governing_token_owner: Pubkey,

/// Voter's weight
pub voter_weight: u64,
SebastianBor marked this conversation as resolved.
Show resolved Hide resolved

/// The slot when the voting weight expires
/// It should be set to None if the weight never expires
/// If the voter weight decays with time, for example for time locked based weights, then the expiry must be set
/// As a common pattern Revise instruction to update the weight should be invoked before governance instruction within the same transaction
/// and the expiry set to the current slot to provide up to date weight
pub voter_weight_expiry: Option<Slot>,
}

impl AccountMaxSize for VoterWeightRecord {}

impl IsInitialized for VoterWeightRecord {
fn is_initialized(&self) -> bool {
self.account_type == VoterWeightAccountType::VoterWeightRecord
}
}

impl VoterWeightRecord {
/// Asserts the VoterWeightRecord hasn't expired
pub fn assert_is_up_to_date(&self) -> Result<(), ProgramError> {
if let Some(voter_weight_expiry) = self.voter_weight_expiry {
let slot = Clock::get().unwrap().slot;

if slot > voter_weight_expiry {
return Err(GovernanceError::VoterWeightRecordExpired.into());
}
}

Ok(())
}
}

/// Deserializes VoterWeightRecord account and checks owner program
pub fn get_voter_weight_record_data(
program_id: &Pubkey,
voter_weight_record_info: &AccountInfo,
) -> Result<VoterWeightRecord, ProgramError> {
get_account_data::<VoterWeightRecord>(voter_weight_record_info, program_id)
}

/// Deserializes VoterWeightRecord account, checks owner program and asserts it's for the same realm, mint and token owner as the provided TokenOwnerRecord
pub fn get_voter_weight_record_data_for_token_owner_record(
program_id: &Pubkey,
voter_weight_record_info: &AccountInfo,
token_owner_record: &TokenOwnerRecord,
) -> Result<VoterWeightRecord, ProgramError> {
let voter_weight_record_data =
get_voter_weight_record_data(program_id, voter_weight_record_info)?;

if voter_weight_record_data.realm != token_owner_record.realm {
return Err(GovernanceError::InvalidVoterWeightRecordForRealm.into());
}

if voter_weight_record_data.governing_token_mint != token_owner_record.governing_token_mint {
return Err(GovernanceError::InvalidVoterWeightRecordForGoverningTokenMint.into());
}

if voter_weight_record_data.governing_token_owner != token_owner_record.governing_token_owner {
return Err(GovernanceError::InvalidVoterWeightRecordForTokenOwner.into());
}

Ok(voter_weight_record_data)
}
24 changes: 24 additions & 0 deletions governance/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,30 @@ pub enum GovernanceError {
/// All proposals must be finalized to withdraw governing tokens
#[error("All proposals must be finalized to withdraw governing tokens")]
AllProposalsMustBeFinalisedToWithdrawGoverningTokens,

/// Invalid VoterWeightRecord for Realm
#[error("Invalid VoterWeightRecord for Realm")]
InvalidVoterWeightRecordForRealm,

/// Invalid VoterWeightRecord for GoverningTokenMint
#[error("Invalid VoterWeightRecord for GoverningTokenMint")]
InvalidVoterWeightRecordForGoverningTokenMint,

/// Invalid VoterWeightRecord for TokenOwner
#[error("Invalid VoterWeightRecord for TokenOwner")]
InvalidVoterWeightRecordForTokenOwner,

/// VoterWeightRecord expired
#[error("VoterWeightRecord expired")]
VoterWeightRecordExpired,

/// Invalid RealmConfig for Realm
#[error("Invalid RealmConfig for Realm")]
InvalidRealmConfigForRealm,

/// TokenOwnerRecord already exists
#[error("TokenOwnerRecord already exists")]
TokenOwnerRecordAlreadyExists,
}

impl PrintProgramError for GovernanceError {
Expand Down
Loading