From 8ffba88278d9dc28683d27120f079e6dd50f3615 Mon Sep 17 00:00:00 2001 From: hawthorne-abendsen Date: Thu, 1 Aug 2024 01:55:37 +0300 Subject: [PATCH] add deposit configuration --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/extensions/env_extensions.rs | 14 +++++++- src/lib.rs | 60 ++++++++++++++++++++++++-------- src/test.rs | 6 ++++ src/types/ballot_category.rs | 13 +++++++ src/types/contract_config.rs | 6 +++- 7 files changed, 85 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d3c1da..bdd4b7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -808,7 +808,7 @@ dependencies = [ [[package]] name = "reflector-dao-contract" -version = "0.1.0" +version = "0.2.0" dependencies = [ "soroban-sdk", ] diff --git a/Cargo.toml b/Cargo.toml index 1eeedac..b49171f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "reflector-dao-contract" -version = "0.1.0" +version = "0.2.0" edition = "2021" [lib] diff --git a/src/extensions/env_extensions.rs b/src/extensions/env_extensions.rs index 0ab282e..4e6a014 100644 --- a/src/extensions/env_extensions.rs +++ b/src/extensions/env_extensions.rs @@ -4,7 +4,7 @@ use soroban_sdk::{panic_with_error, Address, Env}; use crate::types; -use types::{error::Error, ballot::Ballot}; +use types::{error::Error, ballot::Ballot, ballot_category::BallotCategory}; const ADMIN_KEY: &str = "admin"; const LAST_BALLOT_ID: &str = "last_ballot_id"; const LAST_UNLOCK: &str = "last_unlock"; @@ -32,6 +32,10 @@ pub trait EnvExtensions { fn set_ballot(&self, ballot_id: u64, ballot: &Ballot); + fn set_deposit(&self, ballot_category: BallotCategory, amount: i128); + + fn get_deposit(&self, ballot_category: BallotCategory) -> i128; + fn get_dao_balance(&self) -> i128; fn set_dao_balance(&self, balance: i128); @@ -102,6 +106,14 @@ impl EnvExtensions for Env { get_persistent_storage(&self).set(&ballot_id, ballot); } + fn set_deposit(&self, ballot_category: BallotCategory, amount: i128) { + get_instance_storage(&self).set(&ballot_category, &amount); + } + + fn get_deposit(&self, ballot_category: BallotCategory) -> i128 { + get_instance_storage(&self).get(&ballot_category).unwrap() + } + fn get_last_unlock(&self) -> u64 { get_instance_storage(&self).get(&LAST_UNLOCK).unwrap_or(0) } diff --git a/src/lib.rs b/src/lib.rs index 40b086c..e55cb1f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,9 +1,9 @@ #![no_std] use extensions::env_extensions::EnvExtensions; -use soroban_sdk::{contract, contractimpl, token::TokenClient, Address, Env, Vec}; +use soroban_sdk::{contract, contractimpl, token::TokenClient, Address, Env, Map, Vec}; use types::{ - ballot::Ballot, ballot_init_params::BallotInitParams, ballot_status::BallotStatus, - ballot_category::BallotCategory, contract_config::ContractConfig, error::Error, + ballot::Ballot, ballot_category::BallotCategory, ballot_init_params::BallotInitParams, + ballot_status::BallotStatus, contract_config::ContractConfig, error::Error, }; mod extensions; @@ -39,6 +39,8 @@ impl DAOContract { /// # Panics /// /// Panics if the contract has been already initialized + /// Panics if the deposit amounts is invalid + /// Panics if the deposit amount is not set for all categories pub fn config(e: Env, config: ContractConfig) { // check admin permissions config.admin.require_auth(); @@ -54,13 +56,31 @@ impl DAOContract { e.set_admin(&config.admin); e.set_token(&config.token); e.set_last_unlock(e.ledger().timestamp()); - e.set_last_ballot_id(0); + //set deposit params + set_deposit(&e, config.deposit_params); // transfer tokens to the DAO contract token(&e).transfer(&config.admin, &e.current_contract_address(), &config.amount); // set initial DAO balance update_dao_balance(&e, &config.amount.into()); } + /// Sets the deposit amount for each ballot category + /// Requires admin permissions + /// + /// # Arguments + /// + /// * `deposit_params` - Map of deposit amounts for each ballot category + /// + /// # Panics + /// + /// Panics if the caller doesn't match admin address + /// Panics if the deposit amount is invalid + /// Panics if the deposit amount is not set for all categories + pub fn set_deposit(e: Env, deposit_params: Map) { + e.panic_if_not_admin(); + set_deposit(&e, deposit_params); + } + /// Unlocks tokens distributed to the developer organization and operators on a weekly basis /// Requires admin permissions /// @@ -174,14 +194,12 @@ impl DAOContract { // generate new ballot id let ballot_id = e.get_last_ballot_id() + 1; // calculate deposit requirements for the ballot - // TODO: do we want to adjust deposit amounts per category in the future? - let deposit = match params.category { - BallotCategory::AddNode => 50_000_0000000, - BallotCategory::AddPriceFeed => 100_000_0000000, - BallotCategory::AddAsset => 5_000_0000000, - BallotCategory::General => 10_000_0000000 - }; - if params.title.len() < 10 || params.title.len() > 40 || params.description.len() < 10 || params.description.len() > 160 { + let deposit = e.get_deposit(params.category); + if params.title.len() < 10 + || params.title.len() > 40 + || params.description.len() < 10 + || params.description.len() > 160 + { e.panic_with_error(Error::InvalidBallotParams); } // create a ballot object @@ -253,7 +271,7 @@ impl DAOContract { e.panic_with_error(Error::RefundUnavailable); } (ballot.deposit * 125) / 100 - }, + } _ => e.panic_with_error(Error::RefundUnavailable), }; // refund tokens to the initiator address @@ -288,7 +306,11 @@ impl DAOContract { e.panic_with_error(Error::BallotClosed); } // resolve new status - let new_status = if accepted { BallotStatus::Accepted } else { BallotStatus::Rejected }; + let new_status = if accepted { + BallotStatus::Accepted + } else { + BallotStatus::Rejected + }; // calculate the amount of DAO tokens to burn let burn_amount = match new_status { BallotStatus::Rejected => (ballot.deposit * 25) / 100, @@ -305,6 +327,16 @@ impl DAOContract { } } +fn set_deposit(e: &Env, deposit_params: Map) { + for category in BallotCategory::iterator() { + let amount = deposit_params.get(category).unwrap_or(0); + if amount <= 0 { + e.panic_with_error(Error::InvalidAmount); + } + e.set_deposit(category, amount); + } +} + // fetch ballot from the persistent storage fn get_ballot(e: &Env, ballot_id: u64) -> Ballot { // fetch ballot by ID diff --git a/src/test.rs b/src/test.rs index 4668992..db6dd7f 100644 --- a/src/test.rs +++ b/src/test.rs @@ -40,6 +40,12 @@ fn init_contract_with_admin<'a>() -> (Env, DAOContractClient<'a>, ContractConfig admin: admin.clone(), token, amount: amount, + deposit_params: Map::from_array(&env, [ + (BallotCategory::AddNode, 50_000_0000000), + (BallotCategory::AddPriceFeed, 100_000_0000000), + (BallotCategory::AddAsset, 5_000_0000000), + (BallotCategory::General, 10_000_0000000), + ]) }; //set admin diff --git a/src/types/ballot_category.rs b/src/types/ballot_category.rs index 7176d84..6336369 100644 --- a/src/types/ballot_category.rs +++ b/src/types/ballot_category.rs @@ -8,4 +8,17 @@ pub enum BallotCategory { AddPriceFeed = 1, AddAsset = 2, General = 3 +} + +impl BallotCategory { + pub fn iterator() -> impl Iterator { + [ + BallotCategory::AddNode, + BallotCategory::AddPriceFeed, + BallotCategory::AddAsset, + BallotCategory::General, + ] + .iter() + .copied() + } } \ No newline at end of file diff --git a/src/types/contract_config.rs b/src/types/contract_config.rs index 153e056..f252e99 100644 --- a/src/types/contract_config.rs +++ b/src/types/contract_config.rs @@ -1,4 +1,6 @@ -use soroban_sdk::{contracttype, Address}; +use soroban_sdk::{contracttype, Address, Map}; + +use super::ballot_category::BallotCategory; #[contracttype] #[derive(Clone, Debug, Eq, PartialEq)] @@ -11,4 +13,6 @@ pub struct ContractConfig { pub token: Address, /// Initial funding amount pub amount: i128, + /// Initial deposit amounts for each ballot category + pub deposit_params: Map }