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