From c62fc589a2d44e1c602f8aa4636157ee76c929c1 Mon Sep 17 00:00:00 2001 From: mdepraba Date: Thu, 13 Mar 2025 18:52:55 +0800 Subject: [PATCH 1/4] feat(staking): completed implementation of staking pallet --- src/staking.rs | 61 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/src/staking.rs b/src/staking.rs index 7bd646e..3d3e417 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,79 @@ 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) + .copied() + .unwrap_or(T::Balance::zero()); + if free_balance < amount { + return Err("Insufficient balance"); + } + + self.free_balances + .insert(who.clone(), free_balance.checked_sub(&amount).unwrap()); + let staked_balance = self + .staked_balances + .get(&who) + .copied() + .unwrap_or(T::Balance::zero()); + self.staked_balances + .insert(who, staked_balance.checked_add(&amount).unwrap()); + + 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) + .copied() + .unwrap_or(T::Balance::zero()); + if staked_balance < amount { + return Err("Insufficient staked balance"); + } + + self.staked_balances + .insert(who.clone(), staked_balance.checked_sub(&amount).unwrap()); + let free_balance = self + .free_balances + .get(&who) + .copied() + .unwrap_or(T::Balance::zero()); + self.free_balances + .insert(who, free_balance.checked_add(&amount).unwrap()); + + Ok(()) } // Get free balance for an account pub fn get_free_balance(&self, who: T::AccountId) -> T::Balance { - todo!() + self.free_balances + .get(&who) + .copied() + .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) + .copied() + .unwrap_or(T::Balance::zero()) } } From 222f9a3e05015fa34122cc457e19f48b56cb4515 Mon Sep 17 00:00:00 2001 From: mdepraba Date: Thu, 13 Mar 2025 18:53:57 +0800 Subject: [PATCH 2/4] feat(governance): completed implementation of governance pallet --- src/governance.rs | 68 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 9 deletions(-) diff --git a/src/governance.rs b/src/governance.rs index f71f10b..f3f90b5 100644 --- a/src/governance.rs +++ b/src/governance.rs @@ -1,10 +1,11 @@ use crate::staking::StakingConfig; -use crate::system::SystemConfig; +// use crate::system::SystemConfig; use std::collections::HashMap; pub trait GovernanceConfig: StakingConfig {} -pub struct Proposal { +pub struct Proposal { + creator: T::AccountId, description: String, yes_votes: u32, no_votes: u32, @@ -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,18 @@ impl GovernancePallet { creator: T::AccountId, description: String, ) -> Result { - todo!() + let proposal = Proposal { + creator, + description, + yes_votes: 0, + no_votes: 0, + status: ProposalStatus::Active, + }; + + self.next_proposal_id += 1; + self.proposals.insert(self.next_proposal_id, proposal); + + Ok(self.next_proposal_id) } // Vote on a proposal (true = yes, false = no) @@ -45,17 +61,51 @@ impl GovernancePallet { proposal_id: u32, vote_type: bool, ) -> Result<(), &'static str> { - todo!() + let proposal = self + .proposals + .get_mut(&proposal_id) + .ok_or("Proposal not found")?; + let vote = self + .votes + .get(&(voter, proposal_id)) + .copied() + .unwrap_or(false); + + if vote { + return Err("Already voted"); + } + + if vote_type { + proposal.yes_votes += 1; + } else { + proposal.no_votes += 1; + } + + Ok(()) } // Get proposal details - pub fn get_proposal(&self, proposal_id: u32) -> Option<&Proposal> { - todo!() + pub fn get_proposal(&self, proposal_id: u32) -> Option<&Proposal> { + match self.proposals.get(&proposal_id) { + Some(proposal) => Some(proposal), + None => None, + } } // Finalize a proposal (changes status based on votes) pub fn finalize_proposal(&mut self, proposal_id: u32) -> Result { - todo!() + let proposal = self + .proposals + .get_mut(&proposal_id) + .ok_or("Proposal not found")?; + + if proposal.yes_votes > proposal.no_votes { + proposal.status = ProposalStatus::Approved; + } else { + proposal.status = ProposalStatus::Rejected; + } + + Ok(proposal.status.clone()) } } From 8d29bfb868183d6a9b907ea1a308ae504bac550c Mon Sep 17 00:00:00 2001 From: mdepraba Date: Thu, 13 Mar 2025 18:59:48 +0800 Subject: [PATCH 3/4] docs(README): Add Participant Registration section for OpenGuild FRAME Challenges --- README.md | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index c0ca209..c9d097e 100644 --- a/README.md +++ b/README.md @@ -6,12 +6,12 @@ This project provides a hands-on introduction to Substrate's FRAME development p TLDR: If you are not familiar with Git & Github, follow these steps below to fork and setup your own repository. -- Step 1: Install Git & Github Desktop (optional) on your local device -- Step 2: Fork this repository by click the `Fork button` on Github +- Step 1: Install Git & Github Desktop (optional) on your local device +- Step 2: Fork this repository by click the `Fork button` on Github ![image](https://github.com/openguild-labs/open-hack-rust-starter/assets/56880684/7fa2f01a-b523-4208-92db-d8af7a274d98) -- Step 3: `Clone` the forked repository to your local device using the below command +- Step 3: `Clone` the forked repository to your local device using the below command ```sh git clone https://github.com//frame-challenges.git @@ -23,7 +23,7 @@ Replace the `[name-of-your-account]` with your Github username. For example, if git clone https://github.com/openguild-labs/frame-challenges.git ``` -- Step 4: Edit the `README.md` file to register for official participation +- Step 4: Edit the `README.md` file to register for official participation Go to **Participant Registration** section and register to be the workshop participants. Add the below to the list, replace any placeholder with your personal information. @@ -31,14 +31,14 @@ Go to **Participant Registration** section and register to be the workshop parti | 🦄 | Name | Github username | Your current occupation | ``` -- Step 5: `Commit` your code and push to the forked Github repository +- Step 5: `Commit` your code and push to the forked Github repository ``` git add . git commit -m "Register for OpenGuild FRAME Challenges" ``` -- Step 6: Create a `Pull Request` to merge your changes to this repository and name your PR as `Your name | Register for OpenGuild FRAME Challenges` +- Step 6: Create a `Pull Request` to merge your changes to this repository and name your PR as `Your name | Register for OpenGuild FRAME Challenges` Screenshot 2024-04-19 at 16 23 45 @@ -56,15 +56,16 @@ After learning about Substrate, FRAME, Runtime, and Pallets in Polkadot SDK, thi ### Key Concepts: -- **FRAME Pallets**: Libraries for building Substrate runtime, including core frame components and functional pallets -- **Runtime**: A collection of FRAME pallets that define blockchain logic +- **FRAME Pallets**: Libraries for building Substrate runtime, including core frame components and functional pallets +- **Runtime**: A collection of FRAME pallets that define blockchain logic ### Project Structure This challenge simulates three core components: -- `system.rs`: Foundation module similar to `frame_system` -- `staking.rs`: Token staking module similar to `pallet_staking` -- `governance.rs`: On-chain proposal system similar to `pallet_collective` or `pallet_democracy` + +- `system.rs`: Foundation module similar to `frame_system` +- `staking.rs`: Token staking module similar to `pallet_staking` +- `governance.rs`: On-chain proposal system similar to `pallet_collective` or `pallet_democracy` ### Runtime Configuration @@ -90,7 +91,15 @@ impl GovernanceConfig for Runtime { OpenGuild is a builder-driven community centered around Polkadot. OpenGuild is built by Web3 builders for Web3 builders. Our primary aim is to cater to developers seeking a comprehensive understanding of the Polkadot blockchain, providing curated, in-depth materials with a low-level approach. -- **About us:** [Learn more about us](https://openguild.wtf/about) -- **Website:** [OpenGuild Website](https://openguild.wtf/) -- **Github:** [OpenGuild Labs](https://github.com/openguild-labs) -- **Discord**: [Openguild Discord Channel](https://discord.gg/bcjMzxqtD7) +- **About us:** [Learn more about us](https://openguild.wtf/about) +- **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 | +| :---: | ---------------------- | :-------------------------------------: | ------------------------------ | +| 💀 | Made Praba Jaya Kusuma | [mdepraba](https://github.com/mdepraba) | Information Technology Student | From f0bf545dc9d7962426a6e9090618428d56f7b777 Mon Sep 17 00:00:00 2001 From: mdepraba Date: Fri, 14 Mar 2025 02:10:59 +0800 Subject: [PATCH 4/4] refactor(staking): Use get helper method on stake() and unstake() --- src/staking.rs | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/src/staking.rs b/src/staking.rs index 3d3e417..6591870 100644 --- a/src/staking.rs +++ b/src/staking.rs @@ -29,22 +29,15 @@ impl StakingPallet { // Stake tokens (move from free to staked) pub fn stake(&mut self, who: T::AccountId, amount: T::Balance) -> Result<(), &'static str> { - let free_balance = self - .free_balances - .get(&who) - .copied() - .unwrap_or(T::Balance::zero()); + let free_balance = self.get_free_balance(who.clone()); if free_balance < amount { return Err("Insufficient balance"); } self.free_balances .insert(who.clone(), free_balance.checked_sub(&amount).unwrap()); - let staked_balance = self - .staked_balances - .get(&who) - .copied() - .unwrap_or(T::Balance::zero()); + + let staked_balance = self.get_staked_balance(who.clone()); self.staked_balances .insert(who, staked_balance.checked_add(&amount).unwrap()); @@ -53,22 +46,15 @@ impl StakingPallet { // Unstake tokens (move from staked to free) pub fn unstake(&mut self, who: T::AccountId, amount: T::Balance) -> Result<(), &'static str> { - let staked_balance = self - .staked_balances - .get(&who) - .copied() - .unwrap_or(T::Balance::zero()); + let staked_balance = self.get_staked_balance(who.clone()); if staked_balance < amount { return Err("Insufficient staked balance"); } self.staked_balances .insert(who.clone(), staked_balance.checked_sub(&amount).unwrap()); - let free_balance = self - .free_balances - .get(&who) - .copied() - .unwrap_or(T::Balance::zero()); + + let free_balance = self.get_free_balance(who.clone()); self.free_balances .insert(who, free_balance.checked_add(&amount).unwrap());