Governance program API design

Co-authored-by: Michael Vines <mvines@gmail.com>
This commit is contained in:
Sebastian Bor 2021-05-17 12:01:20 +01:00 committed by GitHub
parent 1beeb9fd21
commit e5af52d6e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 772 additions and 0 deletions

1
.gitignore vendored
View File

@ -9,3 +9,4 @@ hfuzz_target
hfuzz_workspace
**/*.so
**/.DS_Store
test-ledger

13
Cargo.lock generated
View File

@ -3804,6 +3804,19 @@ dependencies = [
"spl-feature-proposal",
]
[[package]]
name = "spl-governance"
version = "0.1.0"
dependencies = [
"assert_matches",
"num-derive",
"num-traits",
"solana-program",
"solana-program-test",
"solana-sdk",
"thiserror",
]
[[package]]
name = "spl-math"
version = "0.1.0"

View File

@ -9,6 +9,7 @@ members = [
"examples/rust/transfer-lamports",
"feature-proposal/program",
"feature-proposal/cli",
"governance/program",
"libraries/math",
"memo/program",
"name-service/program",

96
governance/README.md Normal file
View File

@ -0,0 +1,96 @@
# Governance
Governance is a program the chief purpose of which is to control the upgrade of other programs through democratic means.
It can also be used as an authority provider for mints and other forms of access control as well where we may want
a voting population to vote on disbursement of access or funds collectively.
## Architecture
### Accounts diagram
![Accounts diagram](./resources/governance-accounts.jpg)
### Governance Realm account
Governance Realm ties Community Token Mint and optional Council Token mint to create a realm
for any governance pertaining to the community of the token holders.
For example a trading protocol can issue a governance token and use it to create its governance realm.
Once a realm is created voters can deposit Governing tokens (Community or Council) to the realm and
use the deposited amount as their voting weight to vote on Proposals within that realm.
### Program Governance account
The basic building block of governance to update programs is the ProgramGovernance account.
It ties a governed Program ID and holds configuration options defining governance rules.
The governed Program ID is used as the seed for a [Program Derived Address](https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses),
and this program derived address is what is used as the address of the Governance account for your Program ID
and the corresponding Governance mint and Council mint (if provided).
What this means is that there can only ever be ONE Governance account for a given Program.
The governance program validates at creation time of the Governance account that the current upgrade authority of the program
taken under governance signed the transaction.
Note: In future versions, once allowed in solana runtime, the governance program will take over the upgrade authority
of the governed program when the Governance account is created.
### How does authority work?
Governance can handle arbitrary executions of code, but it's real power lies in the power to upgrade programs.
It does this through executing commands to the bpf-upgradable-loader program.
Bpf-upgradable-loader allows any signer who has Upgrade authority over a Buffer account and the Program account itself
to upgrade it using its Upgrade command.
Normally, this is the developer who created and deployed the program, and this creation of the Buffer account containing
the new program data and overwriting of the existing Program account's data with it is handled in the background for you
by the Solana program deploy cli command.
However, in order for Governance to be useful, Governance now needs this authority.
### Proposal accounts
A Proposal is an instance of a Governance created to vote on and execute given set of changes.
It is created by someone (Proposal Admin) and tied to a given Governance account
and has a set of executable commands to it, a name and a description.
It goes through various states (draft, voting, executing) and users can vote on it
if they have relevant Community or Council tokens.
It's rules are determined by the Governance account that it is tied to, and when it executes,
it is only eligible to use the [Program Derived Address](https://docs.solana.com/developing/programming-model/calling-between-programs#program-derived-addresses)
authority given by the Governance account.
So a Proposal for Sushi cannot for instance upgrade the Program for Uniswap.
When a Proposal is created by a user then the user becomes Proposal Admin and receives an Admin an Signatory token.
With this power the Admin can add other Signatories to the Proposal.
These Signatories can then add commands to the Proposal and/or sign off on the Proposal.
Once all Signatories have signed off on the Proposal the Proposal leaves Draft state and enters Voting state.
Voting state lasts as long as the Governance has it configured to last, and during this time
people holding Community (or Council) tokens may vote on the Proposal.
Once the Proposal is "tipped" it either enters the Defeated or Executing state.
If Executed, it enters Completed state once all commands have been run.
A command can be run by any one at any time after the `instruction_hold_up_time` length has transpired on the given command.
### SingleSignerInstruction
We only support one kind of executable command right now, and this is the `SingleSignerInstruction` type.
A Proposal can have a certain number of these, and they run independently of each other.
These contain the actual data for a command, and how long after the voting phase a user must wait before they can be executed.
### Voting Dynamics
When a Proposal is created and signed by its Signatories voters can start voting on it using their voting weight,
equal to deposited governing tokens into the realm. A vote is tipped once it passes the defined `vote_threshold` of votes
and enters Succeeded or Defeated state. If Succeeded then Proposal instructions can be executed after they hold_up_time passes.
Users can relinquish their vote any time during Proposal lifetime, but once Proposal it tipped their vote can't be changed.
### Community and Councils governing tokens
Each Governance Realm that gets created has the option to also have a Council mint.
A council mint is simply a separate mint from the Community mint.
What this means is that users can submit Proposals that have a different voting population from a different mint
that can affect the same program. A practical application of this policy may be to have a very large population control
major version bumps of Solana via normal SOL, for instance, but hot fixes be controlled via Council tokens,
of which there may be only 30, and which may be themselves minted and distributed via proposals by the governing population.
### Proposal Workflow
![Proposal Workflow](./resources/governance-workflow.jpg)

View File

@ -0,0 +1,29 @@
[package]
name = "spl-governance"
version = "0.1.0"
description = "Solana Program Library Governance"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana-program-library"
license = "Apache-2.0"
edition = "2018"
[features]
no-entrypoint = []
test-bpf = []
[dependencies]
solana-program = "1.6.7"
thiserror = "1.0"
num-derive = "0.3"
num-traits = "0.2"
[dev-dependencies]
assert_matches = "1.5.0"
solana-program-test = "1.6.7"
solana-sdk = "1.6.7"
[lib]
crate-type = ["cdylib", "lib"]

View File

@ -0,0 +1,2 @@
[target.bpfel-unknown-unknown.dependencies.std]
features = []

View File

@ -0,0 +1,22 @@
//! Program entrypoint definitions
#![cfg(all(target_arch = "bpf", not(feature = "no-entrypoint")))]
use crate::{error::GovernanceError, processor};
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult,
program_error::PrintProgramError, pubkey::Pubkey,
};
entrypoint!(process_instruction);
fn process_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
instruction_data: &[u8],
) -> ProgramResult {
if let Err(error) = processor::process_instruction(program_id, accounts, instruction_data) {
// catch the error so we can print it
error.print::<GovernanceError>();
return Err(error);
}
Ok(())
}

View File

@ -0,0 +1,35 @@
//! Error types
use num_derive::FromPrimitive;
use solana_program::{
decode_error::DecodeError,
msg,
program_error::{PrintProgramError, ProgramError},
};
use thiserror::Error;
/// Errors that may be returned by the Governance program.
#[derive(Clone, Debug, Eq, Error, FromPrimitive, PartialEq)]
pub enum GovernanceError {
/// Invalid instruction passed to program.
#[error("Invalid instruction passed to program")]
InvalidInstruction,
}
impl PrintProgramError for GovernanceError {
fn print<E>(&self) {
msg!(&self.to_string());
}
}
impl From<GovernanceError> for ProgramError {
fn from(e: GovernanceError) -> Self {
ProgramError::Custom(e as u32)
}
}
impl<T> DecodeError<T> for GovernanceError {
fn type_of() -> &'static str {
"Governance Error"
}
}

View File

@ -0,0 +1,245 @@
//! Program instructions
use solana_program::{instruction::Instruction, pubkey::Pubkey};
use crate::state::enums::GoverningTokenType;
/// Yes/No Vote
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum Vote {
/// Yes vote
Yes,
/// No vote
No,
}
/// Instructions supported by the Governance program
#[derive(Clone)]
#[repr(C)]
#[allow(clippy::large_enum_variant)]
pub enum GovernanceInstruction {
/// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint
///
/// 0. `[writable]` Governance Realm account. PDA seeds:['governance',name]
/// 1. `[]` Community Token Mint
/// 2. `[writable]` Community Token Holding account. PDA seeds: ['governance',realm,community_mint]
/// The account will be created with the Realm PDA as its owner
/// 3. `[signer]` Payer
/// 4. `[]` System
/// 5. `[]` SPL Token
/// 6. `[]` Sysvar Rent
/// 7. `[]` Council Token Mint - optional
/// 8. `[writable]` Council Token Holding account - optional. . PDA seeds: ['governance',realm,council_mint]
/// The account will be created with the Realm PDA as its owner
CreateRealm {
/// UTF-8 encoded Governance Realm name
name: String,
},
/// Deposits governing tokens (Community or Council) to Governance Realm and establishes your voter weight to be used for voting within the Realm
/// 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
/// 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
/// 3. `[signer]` Governing Token Owner account
/// 4. `[writable]` Voter Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 5. `[signer]` Payer
/// 6. `[]` System
/// 7. `[]` SPL Token
DepositGoverningTokens {},
/// Withdraws governing tokens (Community or Council) from Governance Realm and downgrades your voter weight within the Realm
/// 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
/// 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]` Voter Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 5. `[]` SPL Token
WithdrawGoverningTokens {},
/// Sets vote authority for the given Realm and Governing Token Mint (Community or Council)
/// The vote authority would have voting rights and could vote on behalf of the Governing Token Owner
///
/// 0. `[signer]` Governing Token Owner
/// 1. `[writable]` Voter Record
SetVoteAuthority {
#[allow(dead_code)]
/// Governance Realm the new vote authority is set for
realm: Pubkey,
#[allow(dead_code)]
/// Governing Token Mint the vote authority is granted over
governing_token_mint: Pubkey,
#[allow(dead_code)]
/// New vote authority
vote_authority: Pubkey,
},
/// Creates Program Governance account which governs an upgradable program
///
/// 0. `[writable]` Governance account. PDA seeds: ['governance', governed_program]
/// 1. `[]` Account of the Program governed by this Governance account
/// 2. `[writable]` Program Data account of the Program governed by this Governance account
/// 3. `[signer]` Current Upgrade Authority account of the Program governed by this Governance account
/// 4. `[]` Governance Realm the Program Governance belongs to
/// 5. `[signer]` Payer
/// 6. `[]` System account
/// 7. `[]` Bpf_upgrade_loader account
CreateProgramGovernance {
/// Voting threshold in % required to tip the vote
/// It's the percentage of tokens out of the entire pool of governance tokens eligible to vote
vote_threshold: u8,
/// Minimum waiting time in slots for an instruction to be executed after proposal is voted on
min_instruction_hold_up_time: u64,
/// Time limit in slots for proposal to be open for voting
max_voting_time: u64,
/// Minimum % of tokens for a governance token owner to be able to create proposal
/// It's the percentage of tokens out of the entire pool of governance tokens eligible to vote
token_threshold_to_create_proposal: u8,
},
/// Create Proposal account for Instructions that will be executed at various slots in the future
/// The instruction also grants Admin and Signatory token to the provided account
///
/// 0. `[writable]` Uninitialized Proposal account
/// 1. `[writable]` Initialized Governance account
/// 2. `[writable]` Initialized Signatory Mint account
/// 3. `[writable]` Initialized Admin Mint account
/// 4. `[writable]` Initialized Admin account for the issued admin token
/// 5. `[writable]` Initialized Signatory account for the issued signatory token
/// 6. '[]` Token program account
/// 7. `[]` Rent sysvar
CreateProposal {
/// Link to gist explaining proposal
description_link: String,
/// UTF-8 encoded name of the proposal
name: String,
/// The Governing token (Community or Council) which will be used for voting on the Proposal
governing_token_type: GoverningTokenType,
},
/// [Requires Admin token]
/// Adds a signatory to the Proposal which means this Proposal can't leave Draft state until yet another Signatory signs
/// As a result of this call the new Signatory will receive a Signatory Token which then can be used to Sign proposal
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Initialized Signatory account
/// 2. `[writable]` Initialized Signatory Mint account
/// 3. `[signer]` Admin account
/// 4. '[]` Token program account
AddSignatory,
/// [Requires Admin token]
/// Removes a Signatory from the Proposal
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Signatory account to remove token from
/// 2. `[writable]` Signatory Mint account
/// 3. `[signer]` Admin account
/// 4. '[]` Token program account
RemoveSignatory,
/// [Requires Admin token]
/// Adds an instruction to the Proposal. Max of 5 of any type. More than 5 will throw error
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Uninitialized Proposal SingleSignerInstruction account
/// 2. `[signer]` Admin account
AddSingleSignerInstruction {
/// Slot waiting time between vote period ending and this being eligible for execution
hold_up_time: u64,
/// Instruction
instruction: Instruction,
/// Position in instruction array
position: u8,
},
/// [Requires Admin token]
/// Remove instruction from the Proposal
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Proposal SingleSignerInstruction account
/// 2. `[signer]` Admin account
RemoveInstruction,
/// [Requires Admin token]
/// Update instruction hold up time in the Proposal
///
/// 0. `[]` Proposal account
/// 1. `[writable]` Proposal SingleSignerInstruction account
/// 2. `[signer]` Admin account
UpdateInstructionHoldUpTime {
/// Minimum waiting time in slots for an instruction to be executed after proposal is voted on
hold_up_time: u64,
},
/// [Requires Admin token]
/// Cancels Proposal and moves it into Canceled
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Admin account
CancelProposal,
/// [Requires Signatory token]
/// Burns signatory token, indicating you approve and sign off on moving this Proposal from Draft state to Voting state
/// The last Signatory token to be burned moves the state to Voting
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Signatory account
/// 2. `[writable]` Signatory Mint account
/// 3. `[]` Token program account
/// 4. `[]` Clock sysvar
SignOffProposal,
/// Uses your voter weight (deposited Community or Council tokens) to cast a vote on a Proposal
/// By doing so you indicate you approve or disapprove of running the Proposal set of instructions
/// If you tip the consensus then the instructions can begin to be run after their hold up time
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Voter Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 2. `[writable]` Proposal Vote Record account. PDA seeds: ['governance',proposal,governing_token_owner]
/// 3. `[signer]` Vote Authority account
/// 4. `[]` Governance account
Vote {
/// Yes/No vote
vote: Vote,
},
/// Relinquish Vote removes voter weight from a Proposal and removes it from voter's active votes
/// If the Proposal is still being voted on then the voter's weight won't count towards the vote outcome
/// If the Proposal is already in decided state then the instruction has no impact on the Proposal
/// and only allows voters to prune their outstanding votes in case they wanted to withdraw Governing tokens from the Realm
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Voter Record account. PDA seeds: ['governance',realm, governing_token_mint, governing_token_owner]
/// 2. `[writable]` Proposal Vote Record account. PDA seeds: ['governance',proposal,governing_token_owner]
/// 3. `[signer]` Vote Authority account
RelinquishVote,
/// Executes an instruction in the Proposal
/// Anybody can execute transaction once Proposal has been voted Yes and transaction_hold_up time has passed
/// The actual instruction being executed will be signed by Governance PDA
/// For example to execute Program upgrade the ProgramGovernance PDA would be used as the singer
///
/// 0. `[writable]` Proposal account
/// 1. `[writable]` Instruction account you wish to execute
/// 2. `[]` Program being invoked account
/// 3. `[]` Governance account (PDA)
/// 4. `[]` Clock sysvar
/// 5+ Any extra accounts that are part of the instruction, in order
Execute,
}

View File

@ -0,0 +1,13 @@
#![deny(missing_docs)]
//! A Governance program for the Solana blockchain.
pub mod entrypoint;
pub mod error;
pub mod instruction;
pub mod processor;
pub mod state;
// Export current sdk types for downstream users building with a different sdk version
pub use solana_program;
solana_program::declare_id!("Governance111111111111111111111111111111111");

View File

@ -0,0 +1,14 @@
//! Instruction processor
use solana_program::{account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey};
use crate::error::GovernanceError;
/// Processes an instruction
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_input: &[u8],
) -> ProgramResult {
Err(GovernanceError::InvalidInstruction.into())
}

View File

@ -0,0 +1,89 @@
//! State enumerations
/// Defines all Governance accounts types
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum GovernanceAccountType {
/// Default uninitialized account state
Uninitialized,
/// Top level aggregation for governances with Community Token (and optional Council Token)
Realm,
/// Voter record for each voter and given governing token type within a Realm
VoterRecord,
/// Program Governance account
ProgramGovernance,
/// Proposal account for Governance account. A single Governance account can have multiple Proposal accounts
Proposal,
/// Vote record account for a given Proposal. Proposal can have 0..n voting records
ProposalVoteRecord,
/// Single Signer Instruction account which holds an instruction to execute for Proposal
SingleSignerInstruction,
}
impl Default for GovernanceAccountType {
fn default() -> Self {
GovernanceAccountType::Uninitialized
}
}
/// Vote with number of votes
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum VoteWeight {
/// Yes vote
Yes(u64),
/// No vote
No(u64),
}
/// Governing Token type
#[repr(C)]
#[derive(Clone)]
pub enum GoverningTokenType {
/// Community token
Community,
/// Council token
Council,
}
/// What state a Proposal is in
#[repr(C)]
#[derive(Clone, Debug, PartialEq)]
pub enum ProposalState {
/// Draft - Proposal enters Draft state when it's created
Draft,
/// Signing - The Proposal is being signed by Signatories. Proposal enters the state when first Signatory Sings and leaves it when last Signatory signs
Signing,
/// Taking votes
Voting,
/// Voting ended with success
Succeeded,
/// Voting completed and now instructions are being execute. Proposal enter this state when first instruction is executed and leaves when the last instruction is executed
Executing,
/// Completed
Completed,
/// Cancelled
Cancelled,
/// Defeated
Defeated,
}
impl Default for ProposalState {
fn default() -> Self {
ProposalState::Draft
}
}

View File

@ -0,0 +1,9 @@
//! Program accounts
pub mod enums;
pub mod program_governance;
pub mod proposal;
pub mod proposal_vote_record;
pub mod realm;
pub mod single_signer_instruction;
pub mod voter_record;

View File

@ -0,0 +1,33 @@
//! Program Governance Account
use solana_program::pubkey::Pubkey;
use super::enums::GovernanceAccountType;
/// Program Governance Account
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ProgramGovernance {
/// Account type
pub account_type: GovernanceAccountType,
/// Voting threshold in % required to tip the vote
/// It's the percentage of tokens out of the entire pool of governance tokens eligible to vote
pub vote_threshold: u8,
/// Minimum % of tokens for a governance token owner to be able to create a proposal
/// It's the percentage of tokens out of the entire pool of governance tokens eligible to vote
pub token_threshold_to_create_proposal: u8,
/// Minimum waiting time in slots for an instruction to be executed after proposal is voted on
pub min_instruction_hold_up_time: u64,
/// Program ID that is governed by this Governance
pub program: Pubkey,
/// Time limit in slots for proposal to be open for voting
pub max_voting_time: u64,
/// Running count of proposals
pub proposal_count: u32,
}

View File

@ -0,0 +1,64 @@
//! Proposal Account
use solana_program::{epoch_schedule::Slot, pubkey::Pubkey};
use super::enums::{GovernanceAccountType, GoverningTokenType, ProposalState};
/// Governance Proposal
#[repr(C)]
#[derive(Clone)]
pub struct Proposal {
/// Governance account type
pub account_type: GovernanceAccountType,
/// Governance account the Proposal belongs to
pub governance: Pubkey,
/// Mint that creates signatory tokens of this Proposal
/// If there are outstanding signatory tokens, then cannot leave draft state. Signatories must burn tokens (ie agree
/// to move instruction to voting state) and bring mint to net 0 tokens outstanding. Each signatory gets 1 (serves as flag)
pub signatory_mint: Pubkey,
/// Admin ownership mint. One token is minted, can be used to grant admin status to a new person
pub admin_mint: Pubkey,
/// Indicates which Governing Token is used to vote on the Proposal
/// Whether the general Community token owners or the Council tokens owners vote on this Proposal
pub voting_token_type: GoverningTokenType,
/// Current state of the proposal
pub state: ProposalState,
/// Total signatory tokens minted, for use comparing to supply remaining during draft period
pub total_signatory_tokens_minted: u64,
/// Link to proposal's description
pub description_link: String,
/// Proposal name
pub name: String,
/// When the Proposal ended voting - this will also be when the set was defeated or began executing naturally
pub voting_ended_at: Option<Slot>,
/// When the Proposal began voting
pub voting_began_at: Option<Slot>,
/// when the Proposal entered draft state
pub created_at: Option<Slot>,
/// when the Proposal entered completed state, also when execution ended naturally.
pub completed_at: Option<Slot>,
/// when the Proposal entered deleted state
pub deleted_at: Option<Slot>,
/// The number of the instructions already executed
pub number_of_executed_instructions: u8,
/// The number of instructions included in the proposal
pub number_of_instructions: u8,
/// Array of pubkeys pointing at Proposal instructions, up to 5
pub instruction: Vec<Pubkey>,
}

View File

@ -0,0 +1,23 @@
//! Proposal Vote Record Account
use solana_program::pubkey::Pubkey;
use super::enums::{GovernanceAccountType, VoteWeight};
/// Proposal Vote Record
#[repr(C)]
#[derive(Clone, Debug, Default, PartialEq)]
pub struct ProposalVoteRecord {
/// Governance account type
pub account_type: GovernanceAccountType,
/// Proposal account
pub proposal: Pubkey,
/// The user who casted this vote
/// This is the Governing Token Owner who deposited governing tokens into the Realm
pub governing_token_owner: Pubkey,
/// Voter's vote: Yes/No and amount
pub vote: Option<VoteWeight>,
}

View File

@ -0,0 +1,22 @@
//! Realm Account
use solana_program::pubkey::Pubkey;
use super::enums::GovernanceAccountType;
/// Governance Realm Account
/// Account PDA seeds" ['governance', name]
#[repr(C)]
pub struct Realm {
/// Governance account type
pub account_type: GovernanceAccountType,
/// Community mint
pub community_mint: Pubkey,
/// Council mint
pub council_mint: Option<Pubkey>,
/// Governance Realm name
pub name: String,
}

View File

@ -0,0 +1,24 @@
//! SingleSignerInstruction Account
use solana_program::instruction::Instruction;
use super::enums::GovernanceAccountType;
/// Account for an instruction to be executed for Proposal
#[repr(C)]
#[derive(Clone)]
pub struct SingleSignerInstruction {
/// Governance Account type
pub account_type: GovernanceAccountType,
/// Minimum waiting time in slots for the instruction to be executed once proposal is voted on
pub hold_up_time: u64,
/// Instruction to execute
/// The instruction will be signed by Governance PDA the Proposal belongs to
// For example for ProgramGovernance the instruction to upgrade program will be signed by ProgramGovernance PDA
pub instruction: Instruction,
/// Executed flag
pub executed: bool,
}

View File

@ -0,0 +1,37 @@
//! Voter Record Account
use solana_program::pubkey::Pubkey;
use super::enums::{GovernanceAccountType, GoverningTokenType};
/// Governance Voter Record
/// Account PDA seeds: ['governance', realm, token_mint, token_owner ]
#[repr(C)]
pub struct VoterRecord {
/// Governance account type
pub account_type: GovernanceAccountType,
/// The Realm the VoterRecord belongs to
pub realm: Pubkey,
/// The type of the Governing Token the VoteRecord is for
pub token_type: GoverningTokenType,
/// The owner (either single or multisig) of the deposited governing SPL Tokens
/// This is who can authorize a withdrawal
pub token_owner: Pubkey,
/// The amount of governing tokens deposited into the Realm
/// This amount is the voter weight used when voting on proposals
pub token_deposit_amount: u64,
/// A single account that is allowed to operate governance with the deposited governing tokens
/// It's delegated to by the token owner
pub vote_authority: Pubkey,
/// The number of active votes cast by voter
pub active_votes_count: u8,
/// The total number of votes cast by the voter
pub total_votes_count: u8,
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB