|
| 1 | +pragma solidity ^0.5.4; |
| 2 | + |
| 3 | +import "../interfaces/IBlockReward.sol"; |
| 4 | +import "./SCurveProvider.sol"; |
| 5 | + |
| 6 | +import "../libs/SafeMath.sol"; |
| 7 | + |
| 8 | + |
| 9 | +/// @title Block reward contract |
| 10 | +/// @notice Performs payouts at each new created block. Block authors |
| 11 | +/// are rewarded according to an S-curve, while there is a constant payout for |
| 12 | +/// a community fund for a certain period of time. |
| 13 | +/// @dev Contract is used by the Parity client and its address is |
| 14 | +/// specified in the chainspec |
| 15 | +contract BlockReward is SCurveProvider, IBlockReward { |
| 16 | + using SafeMath for uint256; |
| 17 | + |
| 18 | + // storage variables for logging reward statistics |
| 19 | + uint256 public mintedTotally; |
| 20 | + uint256 public mintedForCommunity; |
| 21 | + mapping(address => uint256) public mintedForCommunityForAccount; |
| 22 | + mapping(address => uint256) public mintedForAccount; |
| 23 | + mapping(uint256 => uint256) public mintedInBlock; |
| 24 | + mapping(address => mapping(uint256 => uint256)) public mintedForAccountInBlock; |
| 25 | + |
| 26 | + // solhint-disable var-name-mixedcase |
| 27 | + /// Parity client SYSTEM_ADDRESS: 2^160 - 2 |
| 28 | + /// This is a constant, but changed only for tests |
| 29 | + address internal SYSTEM_ADDRESS = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; |
| 30 | + /// The constant amount that gets sent to the |
| 31 | + /// community fund with each new block |
| 32 | + uint256 public communityFundAmount; |
| 33 | + /// Address of the community fund. Preferably a multisig wallet |
| 34 | + address public communityFund; |
| 35 | + /// Mapping of addresses and their payout addresses where rewards are minted |
| 36 | + mapping(address => address) public payoutAddresses; |
| 37 | + /// Stores reward amounts |
| 38 | + mapping(bytes32 => uint256) private uintStorage; |
| 39 | + // solhint-enable var-name-mixedcase |
| 40 | + |
| 41 | + constructor(address _communityFundAddress, uint256 _communityFundAmount) |
| 42 | + public |
| 43 | + { |
| 44 | + communityFund = _communityFundAddress; |
| 45 | + communityFundAmount = _communityFundAmount; |
| 46 | + } |
| 47 | + |
| 48 | + /// @notice Sets community fund address. Ideally |
| 49 | + /// it is a multisig wallet address |
| 50 | + /// @param _newFund New community fund address |
| 51 | + function setCommunityFund(address _newFund) |
| 52 | + external |
| 53 | + { |
| 54 | + require( |
| 55 | + msg.sender == communityFund, |
| 56 | + "Caller is not the community fund" |
| 57 | + ); |
| 58 | + communityFund = _newFund; |
| 59 | + } |
| 60 | + |
| 61 | + /// @notice Sets payout address. Every sender can only set its own |
| 62 | + /// payout address. The contract only rewards block authors, but it |
| 63 | + /// is not checking who sets an address for itself. The community fund |
| 64 | + /// can set a payout address too, if desired. |
| 65 | + /// @param _newPayoutAddress The payout address belonging to the sender |
| 66 | + function setPayoutAddress(address _newPayoutAddress) |
| 67 | + external |
| 68 | + { |
| 69 | + payoutAddresses[msg.sender] = _newPayoutAddress; |
| 70 | + } |
| 71 | + |
| 72 | + /// @notice Resets the payout address. If a payout address is reseted/not set, |
| 73 | + /// the minted amounts get sent to the original one. The sender resets its own |
| 74 | + /// payout address |
| 75 | + function resetPayoutAddress() |
| 76 | + external |
| 77 | + { |
| 78 | + delete payoutAddresses[msg.sender]; |
| 79 | + } |
| 80 | + |
| 81 | + /// @notice The function that is called by the client to issue rewards at a new block. The rewards are |
| 82 | + /// minted: the balances of the corresponing addresses are simply increased with the amount |
| 83 | + /// @dev It is a service transaction invoked by system, which doesn't cost anyhting but still can |
| 84 | + /// modify state. Cannot emit events. |
| 85 | + /// @param benefactors List of addresses that can be rewarded |
| 86 | + /// @param kind List of type codes belonging to the benefactors. They determine the category |
| 87 | + /// an address belongs to. 0 is for block authors which we are only interested in |
| 88 | + /// @return List of addreses to be rewarded, and list of corresponding reward amounts in wei |
| 89 | + function reward(address[] calldata benefactors, uint16[] calldata kind) |
| 90 | + external |
| 91 | + returns (address[] memory, uint256[] memory) |
| 92 | + { |
| 93 | + require(msg.sender == SYSTEM_ADDRESS, "Caller is not the system"); |
| 94 | + require(benefactors.length == kind.length, "Benefactors/types list length differs"); |
| 95 | + require(benefactors.length == 1, "Benefactors list length is not 1"); |
| 96 | + require(kind[0] == 0, "Benefactor is not the block author"); |
| 97 | + |
| 98 | + if (benefactors[0] == address(0) || _checkRewardPeriodEnded(block.number)) { |
| 99 | + return (new address[](0), new uint256[](0)); |
| 100 | + } |
| 101 | + |
| 102 | + address[] memory receivers = new address[](2); |
| 103 | + uint256[] memory rewards = new uint256[](receivers.length); |
| 104 | + |
| 105 | + receivers[0] = _getPayoutAddress(benefactors[0]); |
| 106 | + rewards[0] = getBlockReward(block.number); |
| 107 | + |
| 108 | + receivers[1] = _getPayoutAddress(communityFund); |
| 109 | + rewards[1] = communityFundAmount; |
| 110 | + |
| 111 | + _logMinted(receivers[0], rewards[0]); |
| 112 | + _logCommunityMinted(receivers[1], rewards[1]); |
| 113 | + |
| 114 | + return (receivers, rewards); |
| 115 | + } |
| 116 | + |
| 117 | + /// @dev Retrieves the payout address of an account if there is any. If not specified |
| 118 | + /// or resetted, returns the original account |
| 119 | + /// @param _somebody An account address we retrieve the payout address for |
| 120 | + /// @return The payout address |
| 121 | + function _getPayoutAddress(address _somebody) |
| 122 | + private |
| 123 | + view |
| 124 | + returns (address) |
| 125 | + { |
| 126 | + address _payoutAddress = payoutAddresses[_somebody]; |
| 127 | + if (_payoutAddress == address(0)) { |
| 128 | + return _somebody; |
| 129 | + } |
| 130 | + return _payoutAddress; |
| 131 | + } |
| 132 | + |
| 133 | + /// @dev Logs a community-mint event. Calls `_logMinted` after setting the |
| 134 | + /// community specific metrics |
| 135 | + /// @param _account The account address where the tokens are minted |
| 136 | + /// @param _amount The minted amount in wei |
| 137 | + function _logCommunityMinted(address _account, uint256 _amount) |
| 138 | + private |
| 139 | + { |
| 140 | + mintedForCommunity = mintedForCommunity.add(_amount); |
| 141 | + |
| 142 | + mintedForCommunityForAccount[_account] = mintedForCommunityForAccount[_account].add(_amount); |
| 143 | + |
| 144 | + _logMinted(_account, _amount); |
| 145 | + } |
| 146 | + |
| 147 | + /// @dev Logs a mint event, and stores related metrics (counters). |
| 148 | + /// @param _account The account address where the tokens are minted |
| 149 | + /// @param _amount The minted amount in wei |
| 150 | + function _logMinted(address _account, uint256 _amount) |
| 151 | + private |
| 152 | + { |
| 153 | + mintedForAccountInBlock[_account][block.number] = mintedForAccountInBlock[_account][block.number].add(_amount); |
| 154 | + |
| 155 | + mintedForAccount[_account] = mintedForAccount[_account].add(_amount); |
| 156 | + |
| 157 | + mintedInBlock[block.number] = mintedInBlock[block.number].add(_amount); |
| 158 | + |
| 159 | + mintedTotally = mintedTotally.add(_amount); |
| 160 | + } |
| 161 | +} |
0 commit comments