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

GLIP-1: Governance Contract (EXAMPLE) #1

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions src/flaw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@ pub enum Flaw {

NotImplemented,
NonGlittrMessage,

// governance
InvalidRatio,
InvalidBlockHeight,
InvalidVetoConfiguration,
InvalidExecutorConfiguration,
}
183 changes: 183 additions & 0 deletions src/transaction/governance_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
use bitcoin::XOnlyPublicKey;
use flaw::Flaw;
use super::*;

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub struct GovernanceContract {
pub governance_token: BlockTxTuple,
pub governance_settings: GovernanceSettings,
pub proposal_settings: ProposalSettings,
pub execution_settings: ExecutionSettings,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct GovernanceSettings {
/// Minimum balance required to create a proposal
pub proposal_threshold: U128,
/// Minimum percentage of total supply that must vote for proposal to be valid
pub quorum_ratio: Ratio,
/// Minimum percentage of votes that must be "yes" for proposal to pass
pub approval_ratio: Ratio,
/// Optional delegation system
pub allow_delegation: bool,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ProposalSettings {
/// How long proposals can be voted on (in blocks)
pub voting_period: BlockHeight,
/// Optional delay before voting starts
pub voting_delay: Option<BlockHeight>,
/// Whether votes can be changed during voting period
pub allow_vote_changes: bool,
/// Optional early execution if quorum and approval threshold met
pub allow_early_execution: bool,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ExecutionSettings {
/// Delay between proposal passing and execution
pub execution_delay: BlockHeight,
/// Time window during which execution must occur
pub execution_window: BlockHeight,
/// Optional list of authorized executors (if None, anyone can execute)
pub executors: Option<Vec<Pubkey>>,
/// Optional veto powers
pub veto_powers: Option<VetoPowers>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct VetoPowers {
pub veto_authorities: Vec<Pubkey>,
/// How many veto authorities need to agree
pub required_vetoes: u32,
/// Time window for veto after proposal passes
pub veto_window: BlockHeight,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum VoteType {
Simple {
options: Vec<String>,
},
Ranked {
options: Vec<String>,
max_ranks: u8,
},
Weighted {
options: Vec<String>,
max_weight_per_option: U128,
},
}

impl GovernanceContract {
pub fn validate(&self) -> Option<Flaw> {
// Validate governance token exists
if self.governance_token.1 == 0 {
return Some(Flaw::InvalidBlockTxPointer);
}

// Validate ratios
if self.governance_settings.quorum_ratio.1 == 0
|| self.governance_settings.approval_ratio.1 == 0 {
return Some(Flaw::DivideByZero);
}

// Validate ratio values are reasonable
if self.governance_settings.quorum_ratio.0 > self.governance_settings.quorum_ratio.1
|| self.governance_settings.approval_ratio.0 > self.governance_settings.approval_ratio.1 {
return Some(Flaw::InvalidRatio);
}

// Validate timing parameters
if self.proposal_settings.voting_period == 0
|| self.execution_settings.execution_window == 0 {
return Some(Flaw::InvalidBlockHeight);
}

// Validate veto settings if present
if let Some(veto_powers) = &self.execution_settings.veto_powers {
if veto_powers.veto_authorities.is_empty()
|| veto_powers.required_vetoes == 0
|| veto_powers.required_vetoes as usize > veto_powers.veto_authorities.len() {
return Some(Flaw::InvalidVetoConfiguration);
}

// Validate veto authority pubkeys
for pubkey in &veto_powers.veto_authorities {
if XOnlyPublicKey::from_slice(pubkey).is_err() {
return Some(Flaw::PubkeyInvalid);
}
}
}

// Validate executors if present
if let Some(executors) = &self.execution_settings.executors {
if executors.is_empty() {
return Some(Flaw::InvalidExecutorConfiguration);
}

for pubkey in executors {
if XOnlyPublicKey::from_slice(pubkey).is_err() {
return Some(Flaw::PubkeyInvalid);
}
}
}

None
}
}

// Add governance-specific call types to CallType enum in message.rs
#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct ProposalCreation {
pub title: String,
pub description: String,
pub vote_type: VoteType,
pub actions: Vec<ProposalAction>,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ProposalAction {
Transfer {
recipient: BitcoinAddress,
amount: U128,
asset: BlockTxTuple,
},
UpdateGovernanceSettings {
new_settings: GovernanceSettings,
},
UpdateProposalSettings {
new_settings: ProposalSettings,
},
UpdateExecutionSettings {
new_settings: ExecutionSettings,
},
Custom {
action_type: String,
parameters: serde_json::Value,
},
}

#[derive(Deserialize, Serialize, Clone, Debug)]
pub struct Vote {
pub proposal_id: BlockTxTuple,
pub vote_cast: VoteCast,
}

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum VoteCast {
Simple {
choice: u32,
},
Ranked {
rankings: Vec<u32>,
},
Weighted {
weights: Vec<U128>,
},
}
8 changes: 8 additions & 0 deletions src/transaction/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ use bitcoin::{
use bitcoincore_rpc::jsonrpc::serde_json::{self, Deserializer};
use constants::OP_RETURN_MAGIC_PREFIX;
use flaw::Flaw;
use governance_contract::{GovernanceContract, ProposalCreation, Vote};

#[derive(Deserialize, Serialize, Clone, Debug)]
#[serde(rename_all = "snake_case")]
pub enum ContractType {
Asset(AssetContract),
Governance(GovernanceContract),
}

#[serde_with::skip_serializing_none]
Expand All @@ -30,6 +32,10 @@ pub enum CallType {
Mint(MintOption),
Burn,
Swap,
CreateProposal(ProposalCreation),
Vote(Vote),
ExecuteProposal(BlockTxTuple),
VetoProposal(BlockTxTuple),
}

#[derive(Deserialize, Serialize, Clone, Debug)]
Expand Down Expand Up @@ -153,6 +159,7 @@ impl OpReturnMessage {
ContractType::Asset(asset_contract) => {
return asset_contract.validate();
}
ContractType::Governance(governance_contract) => todo!(),
}
}

Expand Down Expand Up @@ -242,6 +249,7 @@ mod test {
assert_eq!(free_mint.supply_cap, Some(U128(1000)));
assert_eq!(free_mint.amount_per_mint, U128(10));
}
ContractType::Governance(governance_contract) => todo!(),
}
}
}
Expand Down
1 change: 1 addition & 0 deletions src/transaction/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::*;
pub mod asset_contract;
pub mod message;
pub mod governance_contract;
4 changes: 4 additions & 0 deletions src/updater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,10 @@ impl Updater {
CallType::Swap => {
log::info!("Process call type swap");
}
CallType::CreateProposal(_) => todo!(),
CallType::Vote(_) => todo!(),
CallType::ExecuteProposal(_) => todo!(),
CallType::VetoProposal(_) => todo!(),
}
}
} else {
Expand Down
2 changes: 2 additions & 0 deletions src/updater/mint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,8 @@ impl Updater {
result_purchase
}
}
ContractType::Asset(asset_contract) => todo!(),
ContractType::Governance(_) => todo!(),
},
None => Some(Flaw::ContractNotMatch),
},
Expand Down
1 change: 1 addition & 0 deletions tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ async fn test_raw_btc_to_glittr_asset_burn() {
transfer: None,
contract_creation: None,
};
println!("{}", mint_message);

// prepare btc
let bitcoin_value = 50000;
Expand Down