// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity 0.7.5; library SafeERC20 { using SafeMath for uint256; using Address for address; function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } function safeApprove(IERC20 token, address spender, uint256 value) internal { require((value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender).add(value); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 newAllowance = token.allowance(address(this), spender) .sub(value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function _callOptionalReturn(IERC20 token, bytes memory data) private { bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional // solhint-disable-next-line max-line-length require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } } library SafeMath { function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function add32(uint32 a, uint32 b) internal pure returns (uint32) { uint32 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { return sub(a, b, "SafeMath: subtraction overflow"); } function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); uint256 c = a - b; return c; } function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { return div(a, b, "SafeMath: division by zero"); } function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function mod(uint256 a, uint256 b) internal pure returns (uint256) { return mod(a, b, "SafeMath: modulo by zero"); } function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b != 0, errorMessage); return a % b; } // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) function sqrrt(uint256 a) internal pure returns (uint c) { if (a > 3) { c = a; uint b = add( div( a, 2), 1 ); while (b < c) { c = b; b = div( add( div( a, b ), b), 2 ); } } else if (a != 0) { c = 1; } } function percentageAmount( uint256 total_, uint8 percentage_ ) internal pure returns ( uint256 percentAmount_ ) { return div( mul( total_, percentage_ ), 1000 ); } function substractPercentage( uint256 total_, uint8 percentageToSub_ ) internal pure returns ( uint256 result_ ) { return sub( total_, div( mul( total_, percentageToSub_ ), 1000 ) ); } function percentageOfTotal( uint256 part_, uint256 total_ ) internal pure returns ( uint256 percent_ ) { return div( mul(part_, 100) , total_ ); } function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow, so we distribute return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); } function quadraticPricing( uint256 payment_, uint256 multiplier_ ) internal pure returns (uint256) { return sqrrt( mul( multiplier_, payment_ ) ); } function bondingCurve( uint256 supply_, uint256 multiplier_ ) internal pure returns (uint256) { return mul( multiplier_, supply_ ); } } interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } library Address { function isContract(address account) internal view returns (bool) { // This method relies in extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return _functionCallWithValue(target, data, 0, errorMessage); } function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } function _functionCallWithValue( address target, bytes memory data, uint256 weiValue, string memory errorMessage ) private returns (bytes memory) { require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: weiValue }(data); if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) private pure returns(bytes memory) { if (success) { return returndata; } else { if (returndata.length > 0) { assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } function addressToString(address _address) internal pure returns(string memory) { bytes32 _bytes = bytes32(uint256(_address)); bytes memory HEX = "0123456789abcdef"; bytes memory _addr = new bytes(42); _addr[0] = '0'; _addr[1] = 'x'; for(uint256 i = 0; i < 20; i++) { _addr[2+i*2] = HEX[uint8(_bytes[i + 12] >> 4)]; _addr[3+i*2] = HEX[uint8(_bytes[i + 12] & 0x0f)]; } return string(_addr); } } interface IPolicy { function policy() external view returns (address); function renouncePolicy() external; function pushPolicy( address newPolicy_ ) external; function pullPolicy() external; } contract Policy is IPolicy { address internal _policy; address internal _newPolicy; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); constructor () { _policy = msg.sender; emit OwnershipTransferred( address(0), _policy ); } function policy() public view override returns (address) { return _policy; } modifier onlyPolicy() { require( _policy == msg.sender, "Ownable: caller is not the owner" ); _; } function renouncePolicy() public virtual override onlyPolicy() { emit OwnershipTransferred( _policy, address(0) ); _policy = address(0); } function pushPolicy( address newPolicy_ ) public virtual override onlyPolicy() { require( newPolicy_ != address(0), "Ownable: new owner is the zero address"); _newPolicy = newPolicy_; } function pullPolicy() public virtual override { require( msg.sender == _newPolicy ); emit OwnershipTransferred( _policy, _newPolicy ); _policy = _newPolicy; } } interface ITreasury { function mintRewards( address _recipient, uint _amount ) external; } contract Distributor is Policy { using SafeMath for uint; using SafeMath for uint32; using SafeERC20 for IERC20; /* ====== VARIABLES ====== */ address public immutable OHM; address public immutable treasury; uint32 public immutable epochLength; uint32 public nextEpochTime; mapping( uint => Adjust ) public adjustments; /* ====== STRUCTS ====== */ struct Info { uint rate; // in ten-thousandths ( 5000 = 0.5% ) address recipient; } Info[] public info; struct Adjust { bool add; uint rate; uint target; } /* ====== CONSTRUCTOR ====== */ constructor( address _treasury, address _ohm, uint32 _epochLength, uint32 _nextEpochTime ) { require( _treasury != address(0) ); treasury = _treasury; require( _ohm != address(0) ); OHM = _ohm; epochLength = _epochLength; nextEpochTime = _nextEpochTime; } /* ====== PUBLIC FUNCTIONS ====== */ /** @notice send epoch reward to staking contract */ function distribute() external returns ( bool ) { if ( nextEpochTime <= uint32(block.timestamp) ) { nextEpochTime = nextEpochTime.add32( epochLength ); // set next epoch time // distribute rewards to each recipient for ( uint i = 0; i < info.length; i++ ) { if ( info[ i ].rate > 0 ) { ITreasury( treasury ).mintRewards( // mint and send from treasury info[ i ].recipient, nextRewardAt( info[ i ].rate ) ); adjust( i ); // check for adjustment } } return true; } else { return false; } } /* ====== INTERNAL FUNCTIONS ====== */ /** @notice increment reward rate for collector */ function adjust( uint _index ) internal { Adjust memory adjustment = adjustments[ _index ]; if ( adjustment.rate != 0 ) { if ( adjustment.add ) { // if rate should increase info[ _index ].rate = info[ _index ].rate.add( adjustment.rate ); // raise rate if ( info[ _index ].rate >= adjustment.target ) { // if target met adjustments[ _index ].rate = 0; // turn off adjustment } } else { // if rate should decrease info[ _index ].rate = info[ _index ].rate.sub( adjustment.rate ); // lower rate if ( info[ _index ].rate <= adjustment.target ) { // if target met adjustments[ _index ].rate = 0; // turn off adjustment } } } } /* ====== VIEW FUNCTIONS ====== */ /** @notice view function for next reward at given rate @param _rate uint @return uint */ function nextRewardAt( uint _rate ) public view returns ( uint ) { return IERC20( OHM ).totalSupply().mul( _rate ).div( 1000000 ); } /** @notice view function for next reward for specified address @param _recipient address @return uint */ function nextRewardFor( address _recipient ) public view returns ( uint ) { uint reward; for ( uint i = 0; i < info.length; i++ ) { if ( info[ i ].recipient == _recipient ) { reward = nextRewardAt( info[ i ].rate ); } } return reward; } /* ====== POLICY FUNCTIONS ====== */ /** @notice adds recipient for distributions @param _recipient address @param _rewardRate uint */ function addRecipient( address _recipient, uint _rewardRate ) external onlyPolicy() { require( _recipient != address(0) ); info.push( Info({ recipient: _recipient, rate: _rewardRate })); } /** @notice removes recipient for distributions @param _index uint @param _recipient address */ function removeRecipient( uint _index, address _recipient ) external onlyPolicy() { require( _recipient == info[ _index ].recipient ); info[ _index ].recipient = address(0); info[ _index ].rate = 0; } /** @notice set adjustment info for a collector's reward rate @param _index uint @param _add bool @param _rate uint @param _target uint */ function setAdjustment( uint _index, bool _add, uint _rate, uint _target ) external onlyPolicy() { adjustments[ _index ] = Adjust({ add: _add, rate: _rate, target: _target }); } }