Contract Name:
Distributor
Contract Source Code:
File 1 of 1 : Distributor
// 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 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 barterPriceCurve( 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 SafeERC20 for IERC20;
/* ====== VARIABLES ====== */
address public immutable USV;
address public immutable treasury;
uint public immutable epochLength;
uint public nextEpochBlock;
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 _usv, uint _epochLength, uint _nextEpochBlock ) {
require( _treasury != address(0) );
treasury = _treasury;
require( _usv != address(0) );
USV = _usv;
epochLength = _epochLength;
nextEpochBlock = _nextEpochBlock;
}
/* ====== PUBLIC FUNCTIONS ====== */
/**
@notice send epoch reward to staking contract
*/
function distribute() external returns ( bool ) {
if ( nextEpochBlock <= block.number ) {
nextEpochBlock = nextEpochBlock.add( epochLength ); // set next epoch block
// 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( USV ).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
});
}
}