github.com/codingfuture/orig-energi3@v0.8.4/energi/contracts/src/TreasuryV1.sol (about) 1 // Copyright 2019 The Energi Core Authors 2 // This file is part of Energi Core. 3 // 4 // Energi Core is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Energi Core is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Energi Core. If not, see <http://www.gnu.org/licenses/>. 16 17 // Energi Governance system is the fundamental part of Energi Core. 18 19 // NOTE: It's not allowed to change the compiler due to byte-to-byte 20 // match requirement. 21 pragma solidity 0.5.16; 22 //pragma experimental SMTChecker; 23 24 import { GlobalConstants } from "./constants.sol"; 25 import { IGovernedContract, GovernedContract } from "./GovernedContract.sol"; 26 import { IBlockReward } from "./IBlockReward.sol"; 27 import { ITreasury, IBudgetProposal } from "./ITreasury.sol"; 28 import { IGovernedProxy } from "./IGovernedProxy.sol"; 29 import { BudgetProposalV1 } from "./BudgetProposalV1.sol"; 30 import { NonReentrant } from "./NonReentrant.sol"; 31 import { StorageBase } from "./StorageBase.sol"; 32 33 /** 34 * Permanent storage of Treasury V1 data. 35 */ 36 contract StorageTreasuryV1 is 37 StorageBase 38 { 39 mapping(uint => IBudgetProposal) public uuid_proposal; 40 mapping(address => uint) public proposal_uuid; 41 42 function setProposal(uint _uuid, IBudgetProposal _proposal) 43 external 44 requireOwner 45 { 46 uuid_proposal[_uuid] = _proposal; 47 proposal_uuid[address(_proposal)] = _uuid; 48 } 49 50 function deleteProposal(IBudgetProposal _proposal) 51 external 52 requireOwner 53 { 54 uint uuid = proposal_uuid[address(_proposal)]; 55 delete proposal_uuid[address(_proposal)]; 56 delete uuid_proposal[uuid]; 57 } 58 } 59 60 /** 61 * Genesis hardcoded version of Treasury 62 * 63 * NOTE: it MUST NOT change after blockchain launch! 64 */ 65 contract TreasuryV1 is 66 GlobalConstants, 67 GovernedContract, 68 NonReentrant, 69 IBlockReward, 70 ITreasury 71 { 72 // Data for migration 73 //--------------------------------- 74 StorageTreasuryV1 public v1storage; 75 IGovernedProxy public mnregistry_proxy; 76 uint public superblock_cycle; 77 IBudgetProposal[BUDGET_PROPOSAL_MAX] public active_proposals; 78 //--------------------------------- 79 80 constructor(address _proxy, IGovernedProxy _mnregistry_proxy, uint _superblock_cycle) 81 public 82 GovernedContract(_proxy) 83 { 84 v1storage = new StorageTreasuryV1(); 85 mnregistry_proxy = _mnregistry_proxy; 86 superblock_cycle = _superblock_cycle; 87 assert(superblock_cycle > 0); 88 } 89 90 // IGovernedContract 91 //--------------------------------- 92 function _destroy(IGovernedContract _newImpl) internal { 93 v1storage.setOwner(_newImpl); 94 } 95 96 // ITreasury 97 //--------------------------------- 98 function uuid_proposal(uint _ref_uuid) external view returns(IBudgetProposal) { 99 return v1storage.uuid_proposal(_ref_uuid); 100 } 101 102 function proposal_uuid(IBudgetProposal proposal) external view returns(uint) { 103 return v1storage.proposal_uuid(address(proposal)); 104 } 105 106 function propose(uint _amount, uint _ref_uuid, uint _period) 107 external payable 108 noReentry 109 returns(IBudgetProposal proposal) 110 { 111 require(msg.value == FEE_BUDGET_V1, "Invalid fee"); 112 require(_amount >= BUDGET_AMOUNT_MIN, "Too small amount"); 113 require(_amount <= BUDGET_AMOUNT_MAX, "Too large amount"); 114 require(_period >= PERIOD_BUDGET_MIN, "Too small period"); 115 require(_period <= PERIOD_BUDGET_MAX, "Too large period"); 116 117 StorageTreasuryV1 store = v1storage; 118 address payable payout_address = _callerAddress(); 119 120 require(address(store.uuid_proposal(_ref_uuid)) == address(0), "UUID in use"); 121 122 // Find, if proposal slot is available. 123 for (uint i = 0; i < BUDGET_PROPOSAL_MAX; ++i) { 124 if (address(active_proposals[i]) == address(0)) { 125 proposal = new BudgetProposalV1( 126 mnregistry_proxy, 127 payout_address, 128 _ref_uuid, 129 _amount, 130 _period 131 ); 132 133 active_proposals[i] = proposal; 134 break; 135 } 136 } 137 138 require(address(proposal) != address(0), "Too many active proposals"); 139 //--- 140 141 proposal.setFee.value(msg.value)(); 142 store.setProposal(_ref_uuid, proposal); 143 144 // NOTE: it's the only way to retrieve proposal on regular transaction 145 emit BudgetProposal( 146 _ref_uuid, 147 proposal, 148 payout_address, 149 _amount, 150 proposal.deadline() 151 ); 152 153 return proposal; 154 } 155 156 function listProposals() external view returns(IBudgetProposal[] memory proposals) { 157 IBudgetProposal[] memory tmp = new IBudgetProposal[](BUDGET_PROPOSAL_MAX); 158 uint tmp_len = 0; 159 160 for (uint i = 0; i < BUDGET_PROPOSAL_MAX; ++i) { 161 IBudgetProposal p = active_proposals[i]; 162 163 if (address(p) != address(0)) { 164 tmp[tmp_len++] = p; 165 } 166 } 167 168 proposals = new IBudgetProposal[](tmp_len); 169 170 for (uint i = 0; i < tmp_len; ++i) { 171 proposals[i] = tmp[i]; 172 } 173 174 return proposals; 175 } 176 177 function isSuperblock(uint _blockNumber) 178 public view 179 returns(bool) 180 { 181 return (_blockNumber % superblock_cycle) == 0 && (_blockNumber > 0); 182 } 183 184 function contribute() external payable { 185 if (msg.value > 0) { 186 emit Contribution(_callerAddress(), msg.value); 187 } 188 } 189 190 // NOTE: usually Treasury is behind proxy and this one 191 // minimizes possible errors. 192 function balance() 193 external view 194 returns(uint amount) 195 { 196 return address(this).balance; 197 } 198 199 // IBlockReward 200 //--------------------------------- 201 struct AcceptedProposal { 202 IBudgetProposal proposal; 203 uint ref_uuid; 204 uint unpaid; 205 } 206 207 function reward() 208 external payable 209 noReentry 210 { 211 AcceptedProposal[BUDGET_PROPOSAL_MAX] memory accepted; 212 213 uint unpaid_total = _reward_status(accepted); 214 uint curr_balance = address(this).balance; 215 216 if ((curr_balance > 0) && (unpaid_total > 0)) { 217 uint permille = 1000; 218 219 if (unpaid_total > curr_balance) { 220 // Due to lack of floating-point precision, 221 // it may require a few blocks to process 222 // full payouts. 223 permille = curr_balance * 1000 / unpaid_total; 224 } 225 226 _reward_distribute(permille, accepted); 227 } 228 } 229 230 function _reward_status(AcceptedProposal[BUDGET_PROPOSAL_MAX] memory accepted) 231 internal 232 returns(uint unpaid_total) 233 { 234 IBudgetProposal proposal; 235 uint ref_uuid; 236 bool is_accepted; 237 bool is_finished; 238 uint unpaid = 0; 239 240 for (uint i = 0; i < BUDGET_PROPOSAL_MAX; ++i) { 241 proposal = active_proposals[i]; 242 243 if (address(proposal) != address(0)) { 244 (ref_uuid, is_accepted, is_finished, unpaid) = proposal.budgetStatus(); 245 246 if (is_accepted) { 247 if (unpaid > 0) { 248 unpaid_total += unpaid; 249 accepted[i].proposal = proposal; 250 accepted[i].ref_uuid = ref_uuid; 251 accepted[i].unpaid = unpaid; 252 } else { 253 // Fulfilled 254 proposal.destroy(); 255 delete active_proposals[i]; 256 } 257 } else if (is_finished) { 258 // Rejected 259 proposal.collect(); 260 delete active_proposals[i]; 261 } 262 } 263 } 264 } 265 266 function _reward_distribute( 267 uint permille, 268 AcceptedProposal[BUDGET_PROPOSAL_MAX] memory accepted 269 ) 270 internal 271 { 272 IBudgetProposal proposal; 273 274 for (uint i = 0; i < BUDGET_PROPOSAL_MAX; ++i) { 275 proposal = accepted[i].proposal; 276 277 if (address(proposal) != address(0)) { 278 uint amount = accepted[i].unpaid * permille / 1000; 279 assert(amount <= accepted[i].unpaid); 280 proposal.distributePayout.value(amount)(); 281 emit Payout( 282 accepted[i].ref_uuid, 283 proposal, 284 amount 285 ); 286 } 287 } 288 } 289 290 function getReward(uint _blockNumber) 291 external view 292 returns(uint amount) 293 { 294 if (isSuperblock(_blockNumber)) { 295 amount = REWARD_TREASURY_V1; 296 } 297 } 298 299 // Safety 300 //--------------------------------- 301 function () external payable { 302 revert("Not supported"); 303 } 304 } 305