diff --git a/README.md b/README.md index c0ca209..8e8d8f1 100644 --- a/README.md +++ b/README.md @@ -94,3 +94,11 @@ OpenGuild is a builder-driven community centered around Polkadot. OpenGuild is b - **Website:** [OpenGuild Website](https://openguild.wtf/) - **Github:** [OpenGuild Labs](https://github.com/openguild-labs) - **Discord**: [Openguild Discord Channel](https://discord.gg/bcjMzxqtD7) + +## Participant Registration + +Add your information to the below list to officially participate in the workshop challenge (This is the first mission of the whole workshop) + +| Emoji | Name | Github Username | Occupations | +| ----- | --------------- | ----------------------------------------------------- | ------------------------ | +| 🦁 | Muhammad Ramadhani | [Dante4rt](https://github.com/dante4rt) | Software Engineer | diff --git a/src/governance.rs b/src/governance.rs index f71f10b..6d08633 100644 --- a/src/governance.rs +++ b/src/governance.rs @@ -2,16 +2,17 @@ use crate::staking::StakingConfig; use crate::system::SystemConfig; use std::collections::HashMap; -pub trait GovernanceConfig: StakingConfig {} +pub trait GovernanceConfig: StakingConfig + SystemConfig {} -pub struct Proposal { +pub struct Proposal { description: String, yes_votes: u32, no_votes: u32, status: ProposalStatus, + creator: T::AccountId, } -#[derive(Clone)] +#[derive(Clone, PartialEq)] pub enum ProposalStatus { Active, Approved, @@ -19,14 +20,18 @@ pub enum ProposalStatus { } pub struct GovernancePallet { - pub proposals: HashMap, + pub proposals: HashMap>, pub votes: HashMap<(T::AccountId, u32), bool>, // (voter, proposal_id) -> vote_type next_proposal_id: u32, } impl GovernancePallet { pub fn new() -> Self { - todo!() + Self { + proposals: HashMap::new(), + votes: HashMap::new(), + next_proposal_id: 0, + } } // Create a new proposal @@ -35,7 +40,20 @@ impl GovernancePallet { creator: T::AccountId, description: String, ) -> Result { - todo!() + let proposal_id = self.next_proposal_id; + self.next_proposal_id += 1; + + let proposal = Proposal { + description, + yes_votes: 0, + no_votes: 0, + status: ProposalStatus::Active, + creator, + }; + + self.proposals.insert(proposal_id, proposal); + + Ok(proposal_id) } // Vote on a proposal (true = yes, false = no) @@ -45,17 +63,62 @@ impl GovernancePallet { proposal_id: u32, vote_type: bool, ) -> Result<(), &'static str> { - todo!() + if let Some(proposal) = self.proposals.get_mut(&proposal_id) { + if proposal.status != ProposalStatus::Active { + return Err("Proposal is not active"); + } + + if self.votes.contains_key(&(voter.clone(), proposal_id)) { + return Err("Voter has already voted"); + } + + self.votes.insert((voter, proposal_id), vote_type); + + if vote_type { + proposal.yes_votes += 1; + } else { + proposal.no_votes += 1; + } + + Ok(()) + } else { + Err("Proposal does not exist") + } } // Get proposal details - pub fn get_proposal(&self, proposal_id: u32) -> Option<&Proposal> { - todo!() + pub fn get_proposal(&self, proposal_id: u32) -> Option<&Proposal> { + self.proposals.get(&proposal_id) } // Finalize a proposal (changes status based on votes) pub fn finalize_proposal(&mut self, proposal_id: u32) -> Result { - todo!() + if let Some(proposal) = self.proposals.get_mut(&proposal_id) { + if proposal.status != ProposalStatus::Active { + return Err("Proposal is not active"); + } + + if proposal.yes_votes > proposal.no_votes { + proposal.status = ProposalStatus::Approved; + } else { + proposal.status = ProposalStatus::Rejected; + } + + Ok(proposal.status.clone()) + } else { + Err("Proposal does not exist") + } + } + + pub fn get_proposal_details( + &self, + proposal_id: u32, + ) -> Result<(String, T::AccountId), &'static str> { + if let Some(proposal) = self.proposals.get(&proposal_id) { + Ok((proposal.description.clone(), proposal.creator.clone())) + } else { + Err("Proposal does not exist") + } } } @@ -77,6 +140,10 @@ mod tests { .create_proposal(alice, "Increase validator rewards".to_string()) .unwrap(); + let (description, creator) = governance.get_proposal_details(proposal_id).unwrap(); + assert_eq!(description, "Increase validator rewards"); + assert_eq!(creator, alice); + // Cast votes governance.vote(alice, proposal_id, true).unwrap(); // Yes vote governance.vote(bob, proposal_id, true).unwrap(); // Yes vote diff --git a/src/staking.rs b/src/staking.rs index 7bd646e..f470dc2 100644 --- a/src/staking.rs +++ b/src/staking.rs @@ -4,7 +4,7 @@ use std::collections::HashMap; pub trait StakingConfig: SystemConfig { // Define the Balance type with ability to perform checked arithmetic operations - type Balance: Zero + CheckedSub + CheckedAdd + Copy; + type Balance: Zero + CheckedSub + CheckedAdd + Copy + PartialOrd; } pub struct StakingPallet { @@ -16,32 +16,73 @@ pub struct StakingPallet { impl StakingPallet { pub fn new() -> Self { - todo!() + Self { + free_balances: HashMap::new(), + staked_balances: HashMap::new(), + } } // Set free balance for an account pub fn set_balance(&mut self, who: T::AccountId, amount: T::Balance) { - todo!() + self.free_balances.insert(who, amount); } // Stake tokens (move from free to staked) pub fn stake(&mut self, who: T::AccountId, amount: T::Balance) -> Result<(), &'static str> { - todo!() + let free_balance = *self.free_balances.get(&who).unwrap_or(&T::Balance::zero()); + if free_balance < amount { + return Err("Not enough free balance to stake"); + } + + let new_free_balance = free_balance.checked_sub(&amount).unwrap(); + let new_staked_balance = self + .staked_balances + .get(&who) + .unwrap_or(&T::Balance::zero()) + .checked_add(&amount) + .unwrap(); + + self.free_balances.insert(who.clone(), new_free_balance); + self.staked_balances.insert(who, new_staked_balance); + + Ok(()) } // Unstake tokens (move from staked to free) pub fn unstake(&mut self, who: T::AccountId, amount: T::Balance) -> Result<(), &'static str> { - todo!() + let staked_balance = *self + .staked_balances + .get(&who) + .unwrap_or(&T::Balance::zero()); + if staked_balance < amount { + return Err("Not enough staked balance to unstake"); + } + + let new_staked_balance = staked_balance.checked_sub(&amount).unwrap(); + let new_free_balance = self + .free_balances + .get(&who) + .unwrap_or(&T::Balance::zero()) + .checked_add(&amount) + .unwrap(); + + self.staked_balances.insert(who.clone(), new_staked_balance); + self.free_balances.insert(who, new_free_balance); + + Ok(()) } // Get free balance for an account pub fn get_free_balance(&self, who: T::AccountId) -> T::Balance { - todo!() + *self.free_balances.get(&who).unwrap_or(&T::Balance::zero()) } // Get staked balance for an account pub fn get_staked_balance(&self, who: T::AccountId) -> T::Balance { - todo!() + *self + .staked_balances + .get(&who) + .unwrap_or(&T::Balance::zero()) } }