1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
use odra::prelude::collections::BTreeMap;

use crate::modules::AccessControl;
use odra::{
    contract_env,
    prelude::vec::Vec,
    types::{Address, Balance},
};

use super::{
    agg::{AggregatedBalance, BalanceAggregates},
    balances::BalanceStorage,
    stakes::StakesStorage,
};

/// Implementation of the Reputation Contract.
#[odra::module]
pub struct ReputationContract {
    #[odra(using = "access_control")]
    reputation_storage: BalanceStorage,
    #[odra(using = "access_control")]
    passive_reputation_storage: BalanceStorage,
    #[odra(using = "access_control, reputation_storage")]
    stakes_storage: StakesStorage,
    #[odra(using = "reputation_storage")]
    aggregates: BalanceAggregates,
    access_control: AccessControl,
}

#[odra::module]
impl ReputationContract {
    delegate! {
        to self.access_control {
            /// Changes ownership of the contract. Transfer the ownership to the `owner`. Only the current owner
            /// is permitted to call this method.
            ///
            /// See [AccessControl](AccessControl::propose_new_owner())
            pub fn propose_new_owner(&mut self, owner: Address);
            /// Accepts the new owner proposition. This can be called only by the proposed owner.
            /// See [AccessControl](AccessControl::accept_new_owner())
            pub fn accept_new_owner(&mut self);
            /// Adds a  new address to the whitelist.
            ///
            /// See [AccessControl](AccessControl::add_to_whitelist())
            pub fn add_to_whitelist(&mut self, address: Address);
            /// Removes address from the whitelist.
            ///
            /// See [AccessControl](AccessControl::remove_from_whitelist())
            pub fn remove_from_whitelist(&mut self, address: Address);
            /// Checks whether the given address is added to the whitelist.
            pub fn is_whitelisted(&self, address: Address) -> bool;
            /// Returns the address of the current owner.
            pub fn get_owner(&self) -> Option<Address>;
        }

        to self.reputation_storage {
            /// Mints new tokens. Adds `amount` of new tokens to the balance of the `recipient` and
            /// increments the total supply. Only whitelisted addresses are permitted to call this method.
            ///
            /// # Errors
            /// * [`NotWhitelisted`](crate::utils::Error::NotWhitelisted) if caller
            /// is not whitelisted.
            ///
            /// # Events
            /// * [`Mint`](events::Mint).
            pub fn mint(&mut self, recipient: Address, amount: Balance);
            /// Burns existing tokens. Removes `amount` of existing tokens from the balance of the `owner`
            /// and decrements the total supply. Only whitelisted addresses are permitted to call this
            /// method.
            ///
            /// # Errors
            /// * [`NotWhitelisted`](crate::utils::Error::NotWhitelisted) if caller
            /// is not whitelisted.
            ///
            /// # Events
            /// * [`Burn`](events::Burn) event.
            pub fn burn(&mut self, owner: Address, amount: Balance);
            /// Returns the total token supply.
            pub fn total_supply(&self) -> Balance;
            /// Returns the current token balance of the given address.
            pub fn balance_of(&self, address: Address) -> Balance;
            /// Redistributes the reputation based on the voting summary
            pub fn bulk_mint_burn(&mut self, mints: BTreeMap<Address, Balance>, burns: BTreeMap<Address, Balance>);
            /// Burns all the tokens of the `owner`.
            pub fn burn_all(&mut self, owner: Address);
        }

        to self.stakes_storage {
            pub fn stake(&mut self, voter: Address, stake: Balance);

            pub fn unstake(&mut self, voter: Address, stake: Balance);

            pub fn bulk_unstake(&mut self, stakes: Vec<(Address, Balance)>);

            pub fn get_stake(&self, address: Address) -> Balance;
        }

        to self.aggregates {
            /// Gets balances of all the token holders.
            pub fn all_balances(&self) -> AggregatedBalance;
            /// Gets balances of the given account addresses.
            pub fn partial_balances(&self, addresses: Vec<Address>) -> AggregatedBalance;
        }
    }

    /// Constructor method.
    ///
    /// It initializes contract elements:
    /// * Events dictionary.
    /// * Named keys of [`AccessControl`].
    /// * Set `caller` as the owner of the contract.
    /// * Add `caller` to the whitelist.
    ///
    /// # Events
    /// * [`OwnerChanged`](crate::modules::owner::events::OwnerChanged),
    /// * [`AddedToWhitelist`](crate::modules::whitelist::events::AddedToWhitelist).
    #[odra(init)]
    pub fn init(&mut self) {
        let deployer = contract_env::caller();
        self.access_control.init(deployer);
    }

    /// Increases the balance of the passive reputation of the given address.
    ///
    /// # Errors
    /// * [`NotWhitelisted`](crate::utils::Error::NotWhitelisted) if caller
    /// is not whitelisted.
    pub fn mint_passive(&mut self, recipient: Address, amount: Balance) {
        self.passive_reputation_storage.mint(recipient, amount);
    }

    /// Decreases the balance of the passive reputation of the given address.
    ///
    /// # Errors
    /// * [`NotWhitelisted`](crate::utils::Error::NotWhitelisted) if caller
    /// is not whitelisted.
    /// * [`InsufficientBalance`](crate::utils::Error::InsufficientBalance) if the passed
    /// amount exceeds the balance of the passive reputation of the given address.
    pub fn burn_passive(&mut self, owner: Address, amount: Balance) {
        self.passive_reputation_storage.burn(owner, amount);
    }

    /// Returns the current passive balance of the given address.
    pub fn passive_balance_of(&self, address: Address) -> Balance {
        self.passive_reputation_storage.balance_of(address)
    }
}

pub mod events {
    use odra::{
        types::{Address, Balance},
        Event,
    };

    /// Event emitted when tokens have been burnt.
    #[derive(Debug, PartialEq, Eq, Event)]
    pub struct Burn {
        pub address: Address,
        pub amount: Balance,
    }

    /// Event emitted when tokens have been minted.
    #[derive(Debug, PartialEq, Eq, Event)]
    pub struct Mint {
        pub address: Address,
        pub amount: Balance,
    }
}