Skip to content

Commit

Permalink
Governance: Voters weights add-in (#2450)
Browse files Browse the repository at this point in the history
* feat: setup and configure voter weight addin

feat: add use_voter_weight_add_in flag to realm config

chore: use spl-governance 1.1.1 version

chore: make clippy happy

chore: add test to deserialise v1 CreateRealm instruction from v2

feat: add voter-weight-addin skeleton project

chore: build voter-weight-addin before governance

fix: temp workaround to make spl_governance_voter_weight_addin available in CI

chore: add tests with voter-weight-addin

feat: implement deposit instruction for voter weight addin

feat: add voter_weight_expiry

fix: set voter_weight_expiry

chore: restore positive execute tests

chore: restore ignored tests

wip: pass voter weight accounts to create_account_governance2

wip: read voter weight account

chore: make clippy happy

wip: add realm and validation to voter_weight deposit

fix: update addin

chore: make clippy happy

chore: fix voter_weight_record names

feat: use voter weight provided by addin when governance created

chore: update addin

chore: remove governance stake pool program

feat: remove time offset from revise

chore: fix build

feat: create RealmAddins account when realm with addin is created

chore: make clippy happy

feat: set voter weight addin using SetRealmConfig instruction

chore: make clippy happy

chore: update comments

chore: reorder SetrealmConfig accounts

chore: infer use_community_voter_weight_addin

chore: infer use_community_voter_weight_addin

chore: update voter weight addin comments

feat: use voter weight addin id from RealmAddins account

* feat: use voter weight addin to create proposal

* feat: use voter weight addin to cast vote

* chore: make clippy happy

* feat: use voter weight addin to create token governance

* feat: use voter weight addin to create mint governance

* feat: use voter weight adding to create program governance

* chore: create assert_can_withdraw_governing_tokens() helper function

* chore: fix compilation

* fix: ensure governance authority signed transaction to create governance

* feat: implement CreateTokenOwnerRecord instruction

* chore: fix chat tests

* chore: update comments

* chore: rename RealmAddins account to RealmConfig account

* chore: add more reserved space to GovernanceConfig account

* chore: update instruction comments

* chore: update comments

* chore: fix compilation

* chore: remove ignore directive for tests

* feat: panic when depositing tokens into a realm with voter weight addin

* chore: rename community_voter_weight to community_voter_weight_addin

* feat: make payer account optional for SetRealmConfig
  • Loading branch information
SebastianBor committed Oct 13, 2021
1 parent 1c417ff commit c99f419
Show file tree
Hide file tree
Showing 64 changed files with 2,259 additions and 237 deletions.
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.8.0"
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.8.0"
solana-sdk = "1.8.0"
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,

/// 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)
}
28 changes: 28 additions & 0 deletions governance/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,34 @@ 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,

/// Governing token deposits not allowed
#[error("Governing token deposits not allowed")]
GoverningTokenDepositsNotAllowed,
}

impl PrintProgramError for GovernanceError {
Expand Down
Loading

0 comments on commit c99f419

Please sign in to comment.