use odra::contract_env::revert;
use odra::types::OdraType;
use odra::{
contract_env::caller,
prelude::string::{String, ToString},
types::{event::OdraEvent, Address, Balance, BlockTime, Bytes, CallArgs},
Event,
};
use crate::utils::variable_type::VariableType;
use crate::utils::Error::CouldntDeserializeValueToCorrectType;
use crate::voting_contracts::SlashedVotings;
use crate::{
configuration::ConfigurationBuilder,
modules::{refs::ContractRefs, AccessControl},
utils::{consts, ContractCall},
voting::{
ballot::{Ballot, Choice},
types::VotingId,
voting_engine::{
events::VotingCreatedInfo,
voting_state_machine::{VotingStateMachine, VotingSummary, VotingType},
VotingEngine,
},
},
};
#[odra::module(events = [RepoVotingCreated])]
pub struct RepoVoterContract {
refs: ContractRefs,
#[odra(using = "refs")]
voting_engine: VotingEngine,
access_control: AccessControl,
}
#[odra::module]
impl RepoVoterContract {
delegate! {
to self.voting_engine {
pub fn voting_exists(&self, voting_id: VotingId, voting_type: VotingType) -> bool;
pub fn get_voting(
&self,
voting_id: VotingId,
) -> Option<VotingStateMachine>;
pub fn get_ballot(
&self,
voting_id: VotingId,
voting_type: VotingType,
address: Address,
) -> Option<Ballot>;
pub fn get_voter(&self, voting_id: VotingId, voting_type: VotingType, at: u32) -> Option<Address>;
pub fn finish_voting(&mut self, voting_id: VotingId, voting_type: VotingType) -> VotingSummary;
pub fn cancel_finished_voting(&mut self, voting_id: VotingId);
}
to self.access_control {
pub fn propose_new_owner(&mut self, owner: Address);
pub fn accept_new_owner(&mut self);
pub fn add_to_whitelist(&mut self, address: Address);
pub fn remove_from_whitelist(&mut self, address: Address);
pub fn is_whitelisted(&self, address: Address) -> bool;
pub fn get_owner(&self) -> Option<Address>;
}
to self.refs {
pub fn variable_repository_address(&self) -> Address;
pub fn reputation_token_address(&self) -> Address;
}
}
#[odra(init)]
pub fn init(
&mut self,
variable_repository: Address,
reputation_token: Address,
va_token: Address,
) {
self.refs.set_variable_repository(variable_repository);
self.refs.set_reputation_token(reputation_token);
self.refs.set_va_token(va_token);
self.access_control.init(caller());
}
pub fn create_voting(
&mut self,
variable_repo_to_edit: Address,
key: String,
value: Bytes,
activation_time: Option<u64>,
stake: Balance,
) {
Self::assert_correct_value_type(&key, &value);
let voting_configuration = ConfigurationBuilder::new(
self.refs.va_token().total_supply(),
&self.refs.variable_repository().all_variables(),
)
.contract_call(ContractCall {
address: variable_repo_to_edit,
entry_point: consts::EP_UPDATE_AT.to_string(),
call_args: {
let mut args = CallArgs::new();
args.insert(consts::ARG_KEY.to_string(), key.clone());
args.insert(consts::ARG_VALUE.to_string(), value.clone());
args.insert(consts::ARG_ACTIVATION_TIME.to_string(), activation_time);
args
},
amount: None,
})
.build();
let (info, _) = self
.voting_engine
.create_voting(caller(), stake, voting_configuration);
RepoVotingCreated::new(variable_repo_to_edit, key, value, activation_time, info).emit();
}
pub fn vote(
&mut self,
voting_id: VotingId,
voting_type: VotingType,
choice: Choice,
stake: Balance,
) {
self.voting_engine
.vote(caller(), voting_id, voting_type, choice, stake);
}
pub fn slash_voter(&mut self, voter: Address) -> SlashedVotings {
self.access_control.ensure_whitelisted();
self.voting_engine.slash_voter(voter)
}
}
impl RepoVoterContract {
fn assert_correct_value_type(key: &str, value: &Bytes) {
let result = match VariableType::from_key(key) {
VariableType::Balance => Balance::deserialize(value).is_some(),
VariableType::BlockTime => BlockTime::deserialize(value).is_some(),
VariableType::Address => Address::deserialize(value).is_some(),
VariableType::Bool => bool::deserialize(value).is_some(),
VariableType::Unknown => true,
};
if !result {
revert(CouldntDeserializeValueToCorrectType)
}
}
}
#[derive(Debug, PartialEq, Eq, Event)]
pub struct RepoVotingCreated {
variable_repo_to_edit: Address,
key: String,
value: Bytes,
activation_time: Option<u64>,
creator: Address,
stake: Option<Balance>,
voting_id: VotingId,
config_informal_quorum: u32,
config_informal_voting_time: u64,
config_formal_quorum: u32,
config_formal_voting_time: u64,
config_total_onboarded: Balance,
config_double_time_between_votings: bool,
config_voting_clearness_delta: Balance,
config_time_between_informal_and_formal_voting: BlockTime,
}
impl RepoVotingCreated {
pub fn new(
variable_repo_to_edit: Address,
key: String,
value: Bytes,
activation_time: Option<u64>,
info: VotingCreatedInfo,
) -> Self {
Self {
variable_repo_to_edit,
key,
value,
activation_time,
creator: info.creator,
stake: info.stake,
voting_id: info.voting_id,
config_informal_quorum: info.config_informal_quorum,
config_informal_voting_time: info.config_informal_voting_time,
config_formal_quorum: info.config_formal_quorum,
config_formal_voting_time: info.config_formal_voting_time,
config_total_onboarded: info.config_total_onboarded,
config_double_time_between_votings: info.config_double_time_between_votings,
config_voting_clearness_delta: info.config_voting_clearness_delta,
config_time_between_informal_and_formal_voting: info
.config_time_between_informal_and_formal_voting,
}
}
}