Skip to content

Commit

Permalink
Upgraded to new rust and soroban sdk.
Browse files Browse the repository at this point in the history
Implemented voting
  • Loading branch information
nalnir committed Sep 18, 2023
1 parent 39e9fed commit 8599b31
Show file tree
Hide file tree
Showing 15 changed files with 937 additions and 1,271 deletions.
560 changes: 245 additions & 315 deletions dao/Cargo.lock

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions dao/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 7,11 @@ edition = "2021"
crate-type = ["cdylib"]

[dependencies]
soroban-sdk = { version = "0.9.2" }
soroban-sdk = { version = "20.0.0-rc1" }
soroban-token-sdk = { version = "20.0.0-rc1" }

[dev_dependencies]
soroban-sdk = { version = "0.9.2", features = ["testutils"] }
soroban-sdk = { version = "20.0.0-rc1", features = ["testutils"] }

[profile.release]
opt-level = "z"
Expand Down
67 changes: 49 additions & 18 deletions dao/src/contract.rs
Original file line number Diff line number Diff line change
@@ -1,11 1,18 @@
use crate::proposal::{Proposal, check_min_duration, check_min_prop_power, add_proposal};
use crate::proposal::{
add_abstain_votes, add_against_votes, add_for_votes, add_proposal, check_min_duration,
check_min_vote_power, check_voted, get_proposal, set_min_vote_power, Proposal,
};
use crate::storage::core::CoreState;

use crate::utils::core::{can_init_contract, set_core_state};
use crate::utils::core::{can_init_contract, get_core_state, set_core_state};

use soroban_sdk::{
contractimpl, vec, Address, Bytes, BytesN, Env, IntoVal, Val, Symbol, Vec, contract, symbol_short, String, Map
contract, contractimpl, panic_with_error, symbol_short, vec, Address, Bytes, BytesN, Env,
IntoVal, Map, String, Symbol, Val, Vec,
};

use crate::errors::VoteError;

pub trait DaoContractTrait {
fn init(
env: Env,
Expand All @@ -16,12 23,9 @@ pub trait DaoContractTrait {
voting_power: u32,
proposal_power: u32,
shareholders: Map<Address, i128>,
) -> Address ;
fn create_proposal(
env: Env,
from: Address,
proposal: Proposal
) -> u32;
) -> Address;
fn create_proposal(env: Env, from: Address, proposal: Proposal) -> u32;
fn vote(env: Env, from: Address, prop_id: u32, power: u32, vote: u32);
}

#[contract]
Expand All @@ -41,16 45,19 @@ impl DaoContractTrait for DaoContract {
) -> Address {
can_init_contract(&env);
// Deploy the contract using the installed WASM code with given hash.
let id = env.deployer().with_current_contract(gov_token_salt.to_val()).deploy(token_wasm_hash.to_val());

let id = env
.deployer()
.with_current_contract(gov_token_salt.to_val())
.deploy(token_wasm_hash.to_val());

let init_fn: Symbol = Symbol::new(&env, "initialize");
let admin: Val = env.current_contract_address().to_val();
let init_args: Vec<Val> = vec![
&env,
admin,
18u32.into(),
gov_token_name.into(),
gov_token_symbol.into()
gov_token_symbol.into(),
] as Vec<Val>;

// Invoke the init function with the given arguments.
Expand All @@ -59,26 66,25 @@ impl DaoContractTrait for DaoContract {
let mint_fn: Symbol = symbol_short!("mint");
let authorize_fn: Symbol = symbol_short!("set_auth");

let set_proposal_power_fn: Symbol = symbol_short!("set_p_pow");
let set_voting_power_fn: Symbol = symbol_short!("set_v_pow");
set_min_vote_power(&env, voting_power);

let proposal_power_res: Val = env.invoke_contract(&id, &set_proposal_power_fn, vec![&env, proposal_power.into_val(&env)] as Vec<Val>);
let voting_power_res: Val = env.invoke_contract(&id, &set_voting_power_fn, vec![&env, voting_power.into_val(&env)] as Vec<Val>);
let mut shareholdersVector: Vec<Address> = Vec::new(&env);
for (shareholder_address, amount) in shareholders.iter() {
let shareholder_address_raw: Val = shareholder_address.to_val();

shareholdersVector.push_front(shareholder_address);
let auth_args: Vec<Val> = vec![&env, shareholder_address_raw, true.into_val(&env)];
let auth_res: Val = env.invoke_contract(&id, &authorize_fn, auth_args);

let mint_args: Vec<Val> =
vec![&env, shareholder_address_raw, amount.into_val(&env)] as Vec<Val>;
let mint_res: Val = env.invoke_contract(&id, &mint_fn, mint_args);
}

set_core_state(
&env,
&CoreState {
governance_token: id.clone(),
shareholders: shareholdersVector,
},
);

Expand All @@ -94,4 100,29 @@ impl DaoContractTrait for DaoContract {
//todo, store the token supply at this point
add_proposal(&env, proposal)
}

fn vote(env: Env, from: Address, prop_id: u32, power: u32, vote: u32) {
// 1. Check if DAO member
let core_state = get_core_state(&env);
if !core_state.shareholders.contains(from.clone()) {
panic_with_error!(env, VoteError::NotAMember)
}
// 2. Check if already voted
check_voted(&env, prop_id, from.clone());
// 3. Check if has enough voting power to vote
check_min_vote_power(&env, power);
// 4. Check deadline
let proposal = get_proposal(&env, prop_id);
check_min_duration(&env, &proposal);
// 5. Vote
if vote == 0 {
add_against_votes(&env, prop_id, 1)
} else if vote == 1 {
add_for_votes(&env, prop_id, 1)
} else if vote == 2 {
add_abstain_votes(&env, prop_id, 1)
} else {
panic_with_error!(env, VoteError::WrongVoteParam)
}
}
}
10 changes: 9 additions & 1 deletion dao/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 22,13 @@ pub enum ContractError {
TooEarlyToExecute = 7,
AllreadyExecuted = 8,
ForVotesLessThanAgainstVotes = 9,
PropDeadlinePassed = 10
PropDeadlinePassed = 10,
}

#[contracterror]
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum VoteError {
NotAMember = 0,
ProposalExpired = 1,
WrongVoteParam = 2,
}
70 changes: 51 additions & 19 deletions dao/src/proposal.rs
Original file line number Diff line number Diff line change
@@ -1,9 1,9 @@
use soroban_sdk::{
contracttype, panic_with_error, Address, BytesN, Env, Val, Symbol,
Vec, String
};
use soroban_sdk::{contracttype, panic_with_error, Address, BytesN, Env, String, Symbol, Val, Vec};

use crate::{storage::proposal_storage::ProposalStorageKey, errors::ContractError, settings::get_min_prop_duration};
use crate::{
errors::ContractError, settings::get_min_prop_duration,
storage::proposal_storage::ProposalStorageKey,
};

#[contracttype]
#[derive(Clone, Debug)]
Expand All @@ -26,8 26,7 @@ pub struct ProposalInstr {
pub struct Proposal {
pub end_time: u64,
// instrunctions will be executed in sequence
pub url: String
// pub instr: Vec<ProposalInstr>,
pub url: String, // pub instr: Vec<ProposalInstr>,
}

#[contracttype]
Expand All @@ -40,8 39,9 @@ pub struct VotesCount {
// add prop and return its id
pub fn add_proposal(env: &Env, proposal: Proposal) -> u32 {
let prop_id = get_and_inc_prop_id(env);

env.storage().persistent().set(&ProposalStorageKey::Proposal(prop_id), &proposal);
env.storage()
.persistent()
.set(&ProposalStorageKey::Proposal(prop_id), &proposal);
set_prop_start_ledger(env, prop_id, env.ledger().sequence());

prop_id
Expand All @@ -61,7 61,9 @@ fn get_and_inc_prop_id(env: &Env) -> u32 {
.get(&ProposalStorageKey::ProposalId)
.unwrap_or(0u32);

env.storage().persistent().set(&ProposalStorageKey::ProposalId, &(prev 1));
env.storage()
.persistent()
.set(&ProposalStorageKey::ProposalId, &(prev 1));
prev
}

Expand All @@ -73,9 75,10 @@ pub fn check_min_duration(env: &Env, proposal: &Proposal) {
}

pub fn set_voted(env: &Env, prop_id: u32, voter: Address) {
env.storage()
.persistent()
.set(&ProposalStorageKey::Voted(ProposalVoted { voter, prop_id }), &true)
env.storage().persistent().set(
&ProposalStorageKey::Voted(ProposalVoted { voter, prop_id }),
&true,
)
}

pub fn get_voted(env: &Env, prop_id: u32, voter: Address) -> bool {
Expand Down Expand Up @@ -112,7 115,9 @@ pub fn get_for_votes(env: &Env, prop_id: u32) -> i128 {
}

fn set_for_votes(env: &Env, prop_id: u32, amount: i128) {
env.storage().persistent().set(&ProposalStorageKey::ForVotes(prop_id), &amount)
env.storage()
.persistent()
.set(&ProposalStorageKey::ForVotes(prop_id), &amount)
}

pub fn add_for_votes(env: &Env, prop_id: u32, amount: i128) {
Expand All @@ -132,7 137,9 @@ pub fn get_against_votes(env: &Env, prop_id: u32) -> i128 {
}

fn set_against_votes(env: &Env, prop_id: u32, amount: i128) {
env.storage().persistent().set(&ProposalStorageKey::AgainstV(prop_id), &amount)
env.storage()
.persistent()
.set(&ProposalStorageKey::AgainstV(prop_id), &amount)
}

pub fn add_against_votes(env: &Env, prop_id: u32, amount: i128) {
Expand All @@ -152,7 159,9 @@ pub fn get_abstain_votes(env: &Env, prop_id: u32) -> i128 {
}

fn set_abstain_votes(env: &Env, prop_id: u32, amount: i128) {
env.storage().persistent().set(&ProposalStorageKey::AbstainV(prop_id), &amount)
env.storage()
.persistent()
.set(&ProposalStorageKey::AbstainV(prop_id), &amount)
}

pub fn add_abstain_votes(env: &Env, prop_id: u32, amount: i128) {
Expand All @@ -165,7 174,15 @@ pub fn add_abstain_votes(env: &Env, prop_id: u32, amount: i128) {
}

pub fn set_min_proposal_power(env: &Env, min_power: i128) {
env.storage().persistent().set(&ProposalStorageKey::MinPropP, &min_power)
env.storage()
.persistent()
.set(&ProposalStorageKey::MinPropP, &min_power)
}

pub fn set_min_vote_power(env: &Env, min_power: u32) {
env.storage()
.persistent()
.set(&ProposalStorageKey::MinVoteP, &min_power)
}

pub fn get_min_proposal_power(env: &Env) -> i128 {
Expand All @@ -175,12 192,25 @@ pub fn get_min_proposal_power(env: &Env) -> i128 {
.unwrap_or(0)
}

pub fn get_min_vote_power(env: &Env) -> u32 {
env.storage()
.persistent()
.get(&ProposalStorageKey::MinVoteP)
.unwrap_or(0)
}

pub fn check_min_prop_power(env: &Env, power: i128) {
if get_min_proposal_power(env) > power {
panic_with_error!(env, ContractError::NotEnoughPower)
}
}

pub fn check_min_vote_power(env: &Env, power: u32) {
if get_min_vote_power(env) > power {
panic_with_error!(env, ContractError::NotEnoughPower)
}
}

pub fn votes_counts(env: &Env, prop_id: u32) -> VotesCount {
let for_votes = get_for_votes(&env, prop_id);
let against_votes = get_against_votes(&env, prop_id);
Expand All @@ -194,12 224,14 @@ pub fn votes_counts(env: &Env, prop_id: u32) -> VotesCount {
}

pub fn set_executed(env: &Env, prop_id: u32) {
env.storage().persistent().set(&ProposalStorageKey::Executed(prop_id), &true)
env.storage()
.persistent()
.set(&ProposalStorageKey::Executed(prop_id), &true)
}

pub fn executed(env: &Env, prop_id: u32) -> bool {
env.storage()
.persistent()
.get(&ProposalStorageKey::Executed(prop_id))
.unwrap_or(false)
}
}
3 changes: 2 additions & 1 deletion dao/src/storage/core.rs
Original file line number Diff line number Diff line change
@@ -1,8 1,9 @@
use soroban_sdk::{contracttype, Address};
use soroban_sdk::{contracttype, Address, Vec};

#[contracttype]
pub struct CoreState {
pub governance_token: Address,
pub shareholders: Vec<Address>
}

#[contracttype]
Expand Down
2 changes: 2 additions & 0 deletions dao/src/storage/proposal_storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 27,8 @@ pub enum ProposalStorageKey {
Nonce(Address),
// min power to propose
MinPropP,
// min power to vote
MinVoteP,
//whether a proposal has been executedd
Executed(u32),
}
16 changes: 3 additions & 13 deletions dao/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
#![cfg(test)]

use crate::{contract::DaoContract, DaoContractClient};
use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, IntoVal, Vec, String, Map};
use soroban_sdk::{testutils::Address as _, Address, BytesN, Env, IntoVal, Map, String, Vec};

// The contract that will be deployed by the deployer contract.
mod contract {
Expand Down Expand Up @@ -40,15 40,5 @@ fn test() {
&shareholders,
);

assert_eq!(val, voting_power)

// assert!(init_result.is_void());
//
// // Invoke contract to check that it is initialized.
// let client = contract::Client::new(&env, &contract_id);
// let sum = client.balance(&admin1);
// assert_eq!(sum, 200000);
//
// let name_of_token = client.name();
// assert_eq!(name_of_token, "name".into_val(&env));
}
// assert_eq!(val)
}
Loading

0 comments on commit 8599b31

Please sign in to comment.