multiversx_sc_modules/governance/
governance_configurable.rs

1multiversx_sc::imports!();
2
3/// # MultiversX smart contract module - Governance
4///
5/// This is a standard smart contract module, that when added to a smart contract offers governance features:
6/// - proposing actions
7/// - voting/downvoting a certain proposal
8/// - after a voting period, either putting the action in a queue (if it reached quorum), or canceling
9///
10/// Voting can only be done by depositing a certain token, decided upon first time setup.  
11///
12/// The module provides the following configurable parameters:  
13/// - `quorum` - the minimum number of (`votes` minus `downvotes`) at the end of voting period  
14/// - `minTokenBalanceForProposing` - Minimum numbers of tokens the proposer has to deposit. These automatically count as `votes` as well  
15/// - `maxActionsPerProposal` - Maximum number of actions (transfers and/or smart contract calls) that a proposal may have  
16/// - `votingDelayInBlocks` - Number of blocks to wait after a block is proposed before being able to vote/downvote that proposal
17/// - `votingPeriodInBlocks` - Number of blocks the voting period lasts (voting delay does not count towards this)  
18/// - `lockTimeAfterVotingEndsInBlocks` - Number of blocks to wait before a successful proposal can be executed  
19///
20/// The module also provides events for most actions that happen:
21/// - `proposalCreated` - triggers when a proposal is created. Also provoides all the relevant information, like proposer, actions etc.  
22/// - `voteCast` - user voted on a proposal  
23/// - `downvoteCast` - user downvoted a proposal  
24/// - `proposalCanceled`, `proposalQueued` and `proposalExecuted` - provides the ID of the specific proposal  
25/// - `userDeposit` - a user deposited some tokens needed for a future payable action  
26///
27/// Please note that although the main contract can modify the module's storage directly, it is not recommended to do so,
28/// as that defeats the whole purpose of having governance. These parameters should only be modified through actions.
29///
30#[multiversx_sc::module]
31pub trait GovernanceConfigurablePropertiesModule {
32    // endpoints - owner-only
33
34    /// The module can't protect its storage from the main SC, so it's the developers responsibility
35    /// to not modify parameters manually
36    fn init_governance_module(
37        &self,
38        governance_token_id: TokenIdentifier,
39        quorum: BigUint,
40        min_token_balance_for_proposal: BigUint,
41        voting_delay_in_blocks: u64,
42        voting_period_in_blocks: u64,
43        lock_time_after_voting_ends_in_blocks: u64,
44    ) {
45        require!(
46            governance_token_id.is_valid_esdt_identifier(),
47            "Invalid ESDT token ID provided for governance_token_id"
48        );
49
50        self.governance_token_id()
51            .set_if_empty(&governance_token_id);
52
53        self.try_change_quorum(quorum);
54        self.try_change_min_token_balance_for_proposing(min_token_balance_for_proposal);
55        self.try_change_voting_delay_in_blocks(voting_delay_in_blocks);
56        self.try_change_voting_period_in_blocks(voting_period_in_blocks);
57        self.try_change_lock_time_after_voting_ends_in_blocks(
58            lock_time_after_voting_ends_in_blocks,
59        );
60    }
61
62    // endpoints - these can only be called by the SC itself.
63    // i.e. only by proposing and executing an action with the SC as dest and the respective func name
64
65    #[endpoint(changeQuorum)]
66    fn change_quorum(&self, new_value: BigUint) {
67        self.require_caller_self();
68
69        self.try_change_quorum(new_value);
70    }
71
72    #[endpoint(changeMinTokenBalanceForProposing)]
73    fn change_min_token_balance_for_proposing(&self, new_value: BigUint) {
74        self.require_caller_self();
75
76        self.try_change_min_token_balance_for_proposing(new_value);
77    }
78
79    #[endpoint(changeVotingDelayInBlocks)]
80    fn change_voting_delay_in_blocks(&self, new_value: u64) {
81        self.require_caller_self();
82
83        self.try_change_voting_delay_in_blocks(new_value);
84    }
85
86    #[endpoint(changeVotingPeriodInBlocks)]
87    fn change_voting_period_in_blocks(&self, new_value: u64) {
88        self.require_caller_self();
89
90        self.try_change_voting_period_in_blocks(new_value);
91    }
92
93    #[endpoint(changeLockTimeAfterVotingEndsInBlocks)]
94    fn change_lock_time_after_voting_ends_in_blocks(&self, new_value: u64) {
95        self.require_caller_self();
96
97        self.try_change_lock_time_after_voting_ends_in_blocks(new_value);
98    }
99
100    // private
101
102    fn require_caller_self(&self) {
103        let caller = self.blockchain().get_caller();
104        let sc_address = self.blockchain().get_sc_address();
105
106        require!(
107            caller == sc_address,
108            "Only the SC itself may call this function"
109        );
110    }
111
112    fn try_change_quorum(&self, new_value: BigUint) {
113        require!(new_value != 0, "Quorum can't be set to 0");
114
115        self.quorum().set(&new_value);
116    }
117
118    fn try_change_min_token_balance_for_proposing(&self, new_value: BigUint) {
119        require!(
120            new_value != 0,
121            "Min token balance for proposing can't be set to 0"
122        );
123
124        self.min_token_balance_for_proposing().set(&new_value);
125    }
126
127    fn try_change_voting_delay_in_blocks(&self, new_value: u64) {
128        require!(new_value != 0, "Voting delay in blocks can't be set to 0");
129
130        self.voting_delay_in_blocks().set(new_value);
131    }
132
133    fn try_change_voting_period_in_blocks(&self, new_value: u64) {
134        require!(
135            new_value != 0,
136            "Voting period (in blocks) can't be set to 0"
137        );
138
139        self.voting_period_in_blocks().set(new_value);
140    }
141
142    fn try_change_lock_time_after_voting_ends_in_blocks(&self, new_value: u64) {
143        require!(
144            new_value != 0,
145            "Lock time after voting ends (in blocks) can't be set to 0"
146        );
147
148        self.lock_time_after_voting_ends_in_blocks().set(new_value);
149    }
150
151    // storage - fixed parameters
152
153    #[view(getGovernanceTokenId)]
154    #[storage_mapper("governance:governanceTokenId")]
155    fn governance_token_id(&self) -> SingleValueMapper<TokenIdentifier>;
156
157    // storage - configurable parameters
158
159    #[view(getQuorum)]
160    #[storage_mapper("governance:quorum")]
161    fn quorum(&self) -> SingleValueMapper<BigUint>;
162
163    #[view(getMinFeeForPropose)]
164    #[storage_mapper("minFeeForPropose")]
165    fn min_fee_for_propose(&self) -> SingleValueMapper<BigUint>;
166
167    #[view(getMinTokenBalanceForProposing)]
168    #[storage_mapper("governance:minTokenBalanceForProposing")]
169    fn min_token_balance_for_proposing(&self) -> SingleValueMapper<BigUint>;
170
171    #[view(getVotingDelayInBlocks)]
172    #[storage_mapper("governance:votingDelayInBlocks")]
173    fn voting_delay_in_blocks(&self) -> SingleValueMapper<u64>;
174
175    #[view(getVotingPeriodInBlocks)]
176    #[storage_mapper("governance:votingPeriodInBlocks")]
177    fn voting_period_in_blocks(&self) -> SingleValueMapper<u64>;
178
179    #[view(getLockTimeAfterVotingEndsInBlocks)]
180    #[storage_mapper("governance:lockTimeAfterVotingEndsInBlocks")]
181    fn lock_time_after_voting_ends_in_blocks(&self) -> SingleValueMapper<u64>;
182}