// File: contracts/sol6/IERC20.sol
pragma solidity 0.6.6;
interface IERC20 {
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
function approve(address _spender, uint256 _value) external returns (bool success);
function transfer(address _to, uint256 _value) external returns (bool success);
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
function allowance(address _owner, address _spender) external view returns (uint256 remaining);
function balanceOf(address _owner) external view returns (uint256 balance);
function decimals() external view returns (uint8 digits);
function totalSupply() external view returns (uint256 supply);
}
// to support backward compatible contract name -- so function signature remains same
abstract contract ERC20 is IERC20 {
}
// File: contracts/sol6/utils/Utils5.sol
pragma solidity 0.6.6;
/**
* @title Kyber utility file
* mostly shared constants and rate calculation helpers
* inherited by most of kyber contracts.
* previous utils implementations are for previous solidity versions.
*/
contract Utils5 {
IERC20 internal constant ETH_TOKEN_ADDRESS = IERC20(
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
);
uint256 internal constant PRECISION = (10**18);
uint256 internal constant MAX_QTY = (10**28); // 10B tokens
uint256 internal constant MAX_RATE = (PRECISION * 10**7); // up to 10M tokens per eth
uint256 internal constant MAX_DECIMALS = 18;
uint256 internal constant ETH_DECIMALS = 18;
uint256 constant BPS = 10000; // Basic Price Steps. 1 step = 0.01%
uint256 internal constant MAX_ALLOWANCE = uint256(-1); // token.approve inifinite
mapping(IERC20 => uint256) internal decimals;
function getUpdateDecimals(IERC20 token) internal returns (uint256) {
if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
uint256 tokenDecimals = decimals[token];
// moreover, very possible that old tokens have decimals 0
// these tokens will just have higher gas fees.
if (tokenDecimals == 0) {
tokenDecimals = token.decimals();
decimals[token] = tokenDecimals;
}
return tokenDecimals;
}
function setDecimals(IERC20 token) internal {
if (decimals[token] != 0) return; //already set
if (token == ETH_TOKEN_ADDRESS) {
decimals[token] = ETH_DECIMALS;
} else {
decimals[token] = token.decimals();
}
}
/// @dev get the balance of a user.
/// @param token The token type
/// @return The balance
function getBalance(IERC20 token, address user) internal view returns (uint256) {
if (token == ETH_TOKEN_ADDRESS) {
return user.balance;
} else {
return token.balanceOf(user);
}
}
function getDecimals(IERC20 token) internal view returns (uint256) {
if (token == ETH_TOKEN_ADDRESS) return ETH_DECIMALS; // save storage access
uint256 tokenDecimals = decimals[token];
// moreover, very possible that old tokens have decimals 0
// these tokens will just have higher gas fees.
if (tokenDecimals == 0) return token.decimals();
return tokenDecimals;
}
function calcDestAmount(
IERC20 src,
IERC20 dest,
uint256 srcAmount,
uint256 rate
) internal view returns (uint256) {
return calcDstQty(srcAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcSrcAmount(
IERC20 src,
IERC20 dest,
uint256 destAmount,
uint256 rate
) internal view returns (uint256) {
return calcSrcQty(destAmount, getDecimals(src), getDecimals(dest), rate);
}
function calcDstQty(
uint256 srcQty,
uint256 srcDecimals,
uint256 dstDecimals,
uint256 rate
) internal pure returns (uint256) {
require(srcQty <= MAX_QTY, "srcQty > MAX_QTY");
require(rate <= MAX_RATE, "rate > MAX_RATE");
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS, "dst - src > MAX_DECIMALS");
return (srcQty * rate * (10**(dstDecimals - srcDecimals))) / PRECISION;
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS, "src - dst > MAX_DECIMALS");
return (srcQty * rate) / (PRECISION * (10**(srcDecimals - dstDecimals)));
}
}
function calcSrcQty(
uint256 dstQty,
uint256 srcDecimals,
uint256 dstDecimals,
uint256 rate
) internal pure returns (uint256) {
require(dstQty <= MAX_QTY, "dstQty > MAX_QTY");
require(rate <= MAX_RATE, "rate > MAX_RATE");
//source quantity is rounded up. to avoid dest quantity being too low.
uint256 numerator;
uint256 denominator;
if (srcDecimals >= dstDecimals) {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS, "src - dst > MAX_DECIMALS");
numerator = (PRECISION * dstQty * (10**(srcDecimals - dstDecimals)));
denominator = rate;
} else {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS, "dst - src > MAX_DECIMALS");
numerator = (PRECISION * dstQty);
denominator = (rate * (10**(dstDecimals - srcDecimals)));
}
return (numerator + denominator - 1) / denominator; //avoid rounding down errors
}
function calcRateFromQty(
uint256 srcAmount,
uint256 destAmount,
uint256 srcDecimals,
uint256 dstDecimals
) internal pure returns (uint256) {
require(srcAmount <= MAX_QTY, "srcAmount > MAX_QTY");
require(destAmount <= MAX_QTY, "destAmount > MAX_QTY");
if (dstDecimals >= srcDecimals) {
require((dstDecimals - srcDecimals) <= MAX_DECIMALS, "dst - src > MAX_DECIMALS");
return ((destAmount * PRECISION) / ((10**(dstDecimals - srcDecimals)) * srcAmount));
} else {
require((srcDecimals - dstDecimals) <= MAX_DECIMALS, "src - dst > MAX_DECIMALS");
return ((destAmount * PRECISION * (10**(srcDecimals - dstDecimals))) / srcAmount);
}
}
function minOf(uint256 x, uint256 y) internal pure returns (uint256) {
return x > y ? y : x;
}
}
// File: contracts/sol6/utils/zeppelin/ReentrancyGuard.sol
pragma solidity 0.6.6;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
contract ReentrancyGuard {
bool private _notEntered;
constructor () internal {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
}
// File: contracts/sol6/Dao/IEpochUtils.sol
pragma solidity 0.6.6;
interface IEpochUtils {
function epochPeriodInSeconds() external view returns (uint256);
function firstEpochStartTimestamp() external view returns (uint256);
function getCurrentEpochNumber() external view returns (uint256);
function getEpochNumber(uint256 timestamp) external view returns (uint256);
}
// File: contracts/sol6/IKyberDao.sol
pragma solidity 0.6.6;
interface IKyberDao is IEpochUtils {
event Voted(address indexed staker, uint indexed epoch, uint indexed campaignID, uint option);
function getLatestNetworkFeeDataWithCache()
external
returns (uint256 feeInBps, uint256 expiryTimestamp);
function getLatestBRRDataWithCache()
external
returns (
uint256 burnInBps,
uint256 rewardInBps,
uint256 rebateInBps,
uint256 epoch,
uint256 expiryTimestamp
);
function handleWithdrawal(address staker, uint256 penaltyAmount) external;
function vote(uint256 campaignID, uint256 option) external;
function getLatestNetworkFeeData()
external
view
returns (uint256 feeInBps, uint256 expiryTimestamp);
function shouldBurnRewardForEpoch(uint256 epoch) external view returns (bool);
/**
* @dev return staker's reward percentage in precision for a past epoch only
* fee handler should call this function when a staker wants to claim reward
* return 0 if staker has no votes or stakes
*/
function getPastEpochRewardPercentageInPrecision(address staker, uint256 epoch)
external
view
returns (uint256);
/**
* @dev return staker's reward percentage in precision for the current epoch
* reward percentage is not finalized until the current epoch is ended
*/
function getCurrentEpochRewardPercentageInPrecision(address staker)
external
view
returns (uint256);
}
// File: contracts/sol6/IKyberFeeHandler.sol
pragma solidity 0.6.6;
interface IKyberFeeHandler {
event RewardPaid(address indexed staker, uint256 indexed epoch, IERC20 indexed token, uint256 amount);
event RebatePaid(address indexed rebateWallet, IERC20 indexed token, uint256 amount);
event PlatformFeePaid(address indexed platformWallet, IERC20 indexed token, uint256 amount);
event KncBurned(uint256 kncTWei, IERC20 indexed token, uint256 amount);
function handleFees(
IERC20 token,
address[] calldata eligibleWallets,
uint256[] calldata rebatePercentages,
address platformWallet,
uint256 platformFee,
uint256 networkFee
) external payable;
function claimReserveRebate(address rebateWallet) external returns (uint256);
function claimPlatformFee(address platformWallet) external returns (uint256);
function claimStakerReward(
address staker,
uint256 epoch
) external returns(uint amount);
}
// File: contracts/sol6/IKyberNetworkProxy.sol
pragma solidity 0.6.6;
interface IKyberNetworkProxy {
event ExecuteTrade(
address indexed trader,
IERC20 src,
IERC20 dest,
address destAddress,
uint256 actualSrcAmount,
uint256 actualDestAmount,
address platformWallet,
uint256 platformFeeBps
);
/// @notice backward compatible
function tradeWithHint(
ERC20 src,
uint256 srcAmount,
ERC20 dest,
address payable destAddress,
uint256 maxDestAmount,
uint256 minConversionRate,
address payable walletId,
bytes calldata hint
) external payable returns (uint256);
function tradeWithHintAndFee(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
address payable destAddress,
uint256 maxDestAmount,
uint256 minConversionRate,
address payable platformWallet,
uint256 platformFeeBps,
bytes calldata hint
) external payable returns (uint256 destAmount);
function trade(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
address payable destAddress,
uint256 maxDestAmount,
uint256 minConversionRate,
address payable platformWallet
) external payable returns (uint256);
/// @notice backward compatible
/// @notice Rate units (10 ** 18) => destQty (twei) / srcQty (twei) * 10 ** 18
function getExpectedRate(
ERC20 src,
ERC20 dest,
uint256 srcQty
) external view returns (uint256 expectedRate, uint256 worstRate);
function getExpectedRateAfterFee(
IERC20 src,
IERC20 dest,
uint256 srcQty,
uint256 platformFeeBps,
bytes calldata hint
) external view returns (uint256 expectedRate);
}
// File: contracts/sol6/ISimpleKyberProxy.sol
pragma solidity 0.6.6;
/*
* @title simple Kyber Network proxy interface
* add convenient functions to help with kyber proxy API
*/
interface ISimpleKyberProxy {
function swapTokenToEther(
IERC20 token,
uint256 srcAmount,
uint256 minConversionRate
) external returns (uint256 destAmount);
function swapEtherToToken(IERC20 token, uint256 minConversionRate)
external
payable
returns (uint256 destAmount);
function swapTokenToToken(
IERC20 src,
uint256 srcAmount,
IERC20 dest,
uint256 minConversionRate
) external returns (uint256 destAmount);
}
// File: contracts/sol6/IBurnableToken.sol
pragma solidity 0.6.6;
interface IBurnableToken {
function burn(uint256 _value) external returns (bool);
}
// File: contracts/sol6/Dao/ISanityRate.sol
pragma solidity 0.6.6;
/// @title Sanity Rate check to prevent burning knc with too expensive or cheap price
/// @dev Using ChainLink as the provider for current knc/eth price
interface ISanityRate {
// return latest rate of knc/eth
function latestAnswer() external view returns (uint256);
}
// File: contracts/sol6/utils/zeppelin/SafeMath.sol
pragma solidity 0.6.6;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
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;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}
// File: contracts/sol6/Dao/DaoOperator.sol
pragma solidity 0.6.6;
contract DaoOperator {
address public daoOperator;
constructor(address _daoOperator) public {
require(_daoOperator != address(0), "daoOperator is 0");
daoOperator = _daoOperator;
}
modifier onlyDaoOperator() {
require(msg.sender == daoOperator, "only daoOperator");
_;
}
}
// File: contracts/sol6/Dao/KyberFeeHandler.sol
pragma solidity 0.6.6;
/**
* @title IKyberProxy
* This interface combines two interfaces.
* It is needed since we use one function from each of the interfaces.
*
*/
interface IKyberProxy is IKyberNetworkProxy, ISimpleKyberProxy {
// empty block
}
/**
* @title kyberFeeHandler
*
* @dev kyberFeeHandler works tightly with contracts kyberNetwork and kyberDao.
* Some events are moved to interface, for easier usage
* @dev Terminology:
* Epoch - Voting campaign time frame in kyberDao.
* kyberDao voting campaigns are in the scope of epochs.
* BRR - Burn / Reward / Rebate. kyberNetwork fee is used for 3 purposes:
* Burning KNC
* Reward an address that staked knc in kyberStaking contract. AKA - stakers
* Rebate reserves for supporting trades.
* @dev Code flow:
* 1. Accumulating && claiming Fees. Per trade on kyberNetwork, it calls handleFees() function which
* internally accounts for network & platform fees from the trade. Fee distribution:
* rewards: accumulated per epoch. can be claimed by the kyberDao after epoch is concluded.
* rebates: accumulated per rebate wallet, can be claimed any time.
* Burn: accumulated in the contract. Burned value and interval limited with safe check using
sanity rate.
* Platfrom fee: accumulated per platform wallet, can be claimed any time.
* 2. Network Fee distribution: Per epoch kyberFeeHandler contract reads BRR distribution percentage
* from kyberDao. When the data expires, kyberFeeHandler reads updated values.
*/
contract KyberFeeHandler is IKyberFeeHandler, Utils5, DaoOperator, ReentrancyGuard {
using SafeMath for uint256;
uint256 internal constant DEFAULT_REWARD_BPS = 3000;
uint256 internal constant DEFAULT_REBATE_BPS = 3000;
uint256 internal constant SANITY_RATE_DIFF_BPS = 1000; // 10%
struct BRRData {
uint64 expiryTimestamp;
uint32 epoch;
uint16 rewardBps;
uint16 rebateBps;
}
struct BRRWei {
uint256 rewardWei;
uint256 fullRebateWei;
uint256 paidRebateWei;
uint256 burnWei;
}
IKyberDao public kyberDao;
IKyberProxy public kyberProxy;
address public kyberNetwork;
IERC20 public immutable knc;
uint256 public immutable burnBlockInterval;
uint256 public lastBurnBlock;
BRRData public brrAndEpochData;
address public daoSetter;
/// @dev amount of eth to burn for each burn knc call
uint256 public weiToBurn = 2 ether;
mapping(address => uint256) public feePerPlatformWallet;
mapping(address => uint256) public rebatePerWallet;
mapping(uint256 => uint256) public rewardsPerEpoch;
mapping(uint256 => uint256) public rewardsPaidPerEpoch;
// hasClaimedReward[staker][epoch]: true/false if the staker has/hasn't claimed the reward for an epoch
mapping(address => mapping (uint256 => bool)) public hasClaimedReward;
uint256 public totalPayoutBalance; // total balance in the contract that is for rebate, reward, platform fee
/// @dev use to get rate of KNC/ETH to check if rate to burn knc is normal
/// @dev index 0 is currently used contract address, indexes > 0 are older versions
ISanityRate[] internal sanityRateContract;
event FeeDistributed(
IERC20 indexed token,
address indexed platformWallet,
uint256 platformFeeWei,
uint256 rewardWei,
uint256 rebateWei,
address[] rebateWallets,
uint256[] rebatePercentBpsPerWallet,
uint256 burnAmtWei
);
event BRRUpdated(
uint256 rewardBps,
uint256 rebateBps,
uint256 burnBps,
uint256 expiryTimestamp,
uint256 indexed epoch
);
event EthReceived(uint256 amount);
event KyberDaoAddressSet(IKyberDao kyberDao);
event BurnConfigSet(ISanityRate sanityRate, uint256 weiToBurn);
event RewardsRemovedToBurn(uint256 indexed epoch, uint256 rewardsWei);
event KyberNetworkUpdated(address kyberNetwork);
event KyberProxyUpdated(IKyberProxy kyberProxy);
constructor(
address _daoSetter,
IKyberProxy _kyberProxy,
address _kyberNetwork,
IERC20 _knc,
uint256 _burnBlockInterval,
address _daoOperator
) public DaoOperator(_daoOperator) {
require(_daoSetter != address(0), "daoSetter 0");
require(_kyberProxy != IKyberProxy(0), "kyberNetworkProxy 0");
require(_kyberNetwork != address(0), "kyberNetwork 0");
require(_knc != IERC20(0), "knc 0");
require(_burnBlockInterval != 0, "_burnBlockInterval 0");
daoSetter = _daoSetter;
kyberProxy = _kyberProxy;
kyberNetwork = _kyberNetwork;
knc = _knc;
burnBlockInterval = _burnBlockInterval;
//start with epoch 0
updateBRRData(DEFAULT_REWARD_BPS, DEFAULT_REBATE_BPS, now, 0);
}
modifier onlyKyberDao {
require(msg.sender == address(kyberDao), "only kyberDao");
_;
}
modifier onlyKyberNetwork {
require(msg.sender == address(kyberNetwork), "only kyberNetwork");
_;
}
modifier onlyNonContract {
require(tx.origin == msg.sender, "only non-contract");
_;
}
receive() external payable {
emit EthReceived(msg.value);
}
/// @dev handleFees function is called per trade on kyberNetwork. unless the trade is not involving any fees.
/// @param token Token currency of fees
/// @param rebateWallets a list of rebate wallets that will get rebate for this trade.
/// @param rebateBpsPerWallet percentage of rebate for each wallet, out of total rebate.
/// @param platformWallet Wallet address that will receive the platfrom fee.
/// @param platformFee Fee amount (in wei) the platfrom wallet is entitled to.
/// @param networkFee Fee amount (in wei) to be allocated for BRR
function handleFees(
IERC20 token,
address[] calldata rebateWallets,
uint256[] calldata rebateBpsPerWallet,
address platformWallet,
uint256 platformFee,
uint256 networkFee
) external payable override onlyKyberNetwork nonReentrant {
require(token == ETH_TOKEN_ADDRESS, "token not eth");
require(msg.value == platformFee.add(networkFee), "msg.value not equal to total fees");
// handle platform fee
feePerPlatformWallet[platformWallet] = feePerPlatformWallet[platformWallet].add(
platformFee
);
if (networkFee == 0) {
// only platform fee paid
totalPayoutBalance = totalPayoutBalance.add(platformFee);
emit FeeDistributed(
ETH_TOKEN_ADDRESS,
platformWallet,
platformFee,
0,
0,
rebateWallets,
rebateBpsPerWallet,
0
);
return;
}
BRRWei memory brrAmounts;
uint256 epoch;
// Decoding BRR data
(brrAmounts.rewardWei, brrAmounts.fullRebateWei, epoch) = getRRWeiValues(networkFee);
brrAmounts.paidRebateWei = updateRebateValues(
brrAmounts.fullRebateWei, rebateWallets, rebateBpsPerWallet
);
brrAmounts.rewardWei = brrAmounts.rewardWei.add(
brrAmounts.fullRebateWei.sub(brrAmounts.paidRebateWei)
);
rewardsPerEpoch[epoch] = rewardsPerEpoch[epoch].add(brrAmounts.rewardWei);
// update total balance of rewards, rebates, fee
totalPayoutBalance = totalPayoutBalance.add(
platformFee).add(brrAmounts.rewardWei).add(brrAmounts.paidRebateWei
);
brrAmounts.burnWei = networkFee.sub(brrAmounts.rewardWei).sub(brrAmounts.paidRebateWei);
emit FeeDistributed(
ETH_TOKEN_ADDRESS,
platformWallet,
platformFee,
brrAmounts.rewardWei,
brrAmounts.paidRebateWei,
rebateWallets,
rebateBpsPerWallet,
brrAmounts.burnWei
);
}
/// @notice WARNING When staker address is a contract,
/// it should be able to receive claimed reward in ETH whenever anyone calls this function.
/// @dev not revert if already claimed or reward percentage is 0
/// allow writing a wrapper to claim for multiple epochs
/// @param staker address.
/// @param epoch for which epoch the staker is claiming the reward
function claimStakerReward(
address staker,
uint256 epoch
) external override nonReentrant returns(uint256 amountWei) {
if (hasClaimedReward[staker][epoch]) {
// staker has already claimed reward for the epoch
return 0;
}
// the relative part of the reward the staker is entitled to for the epoch.
// units Precision: 10 ** 18 = 100%
// if the epoch is current or in the future, kyberDao will return 0 as result
uint256 percentageInPrecision = kyberDao.getPastEpochRewardPercentageInPrecision(staker, epoch);
if (percentageInPrecision == 0) {
return 0; // not revert, in case a wrapper wants to claim reward for multiple epochs
}
require(percentageInPrecision <= PRECISION, "percentage too high");
// Amount of reward to be sent to staker
amountWei = rewardsPerEpoch[epoch].mul(percentageInPrecision).div(PRECISION);
// redundant check, can't happen
assert(totalPayoutBalance >= amountWei);
assert(rewardsPaidPerEpoch[epoch].add(amountWei) <= rewardsPerEpoch[epoch]);
rewardsPaidPerEpoch[epoch] = rewardsPaidPerEpoch[epoch].add(amountWei);
totalPayoutBalance = totalPayoutBalance.sub(amountWei);
hasClaimedReward[staker][epoch] = true;
// send reward to staker
(bool success, ) = staker.call{value: amountWei}("");
require(success, "staker rewards transfer failed");
emit RewardPaid(staker, epoch, ETH_TOKEN_ADDRESS, amountWei);
}
/// @dev claim rebate per reserve wallet. called by any address
/// @param rebateWallet the wallet to claim rebates for. Total accumulated rebate sent to this wallet.
/// @return amountWei amount of rebate claimed
function claimReserveRebate(address rebateWallet)
external
override
nonReentrant
returns (uint256 amountWei)
{
require(rebatePerWallet[rebateWallet] > 1, "no rebate to claim");
// Get total amount of rebate accumulated
amountWei = rebatePerWallet[rebateWallet].sub(1);
// redundant check, can't happen
assert(totalPayoutBalance >= amountWei);
totalPayoutBalance = totalPayoutBalance.sub(amountWei);
rebatePerWallet[rebateWallet] = 1; // avoid zero to non zero storage cost
// send rebate to rebate wallet
(bool success, ) = rebateWallet.call{value: amountWei}("");
require(success, "rebate transfer failed");
emit RebatePaid(rebateWallet, ETH_TOKEN_ADDRESS, amountWei);
return amountWei;
}
/// @dev claim accumulated fee per platform wallet. Called by any address
/// @param platformWallet the wallet to claim fee for. Total accumulated fee sent to this wallet.
/// @return amountWei amount of fee claimed
function claimPlatformFee(address platformWallet)
external
override
nonReentrant
returns (uint256 amountWei)
{
require(feePerPlatformWallet[platformWallet] > 1, "no fee to claim");
// Get total amount of fees accumulated
amountWei = feePerPlatformWallet[platformWallet].sub(1);
// redundant check, can't happen
assert(totalPayoutBalance >= amountWei);
totalPayoutBalance = totalPayoutBalance.sub(amountWei);
feePerPlatformWallet[platformWallet] = 1; // avoid zero to non zero storage cost
(bool success, ) = platformWallet.call{value: amountWei}("");
require(success, "platform fee transfer failed");
emit PlatformFeePaid(platformWallet, ETH_TOKEN_ADDRESS, amountWei);
return amountWei;
}
/// @dev set kyberDao contract address once and set setter address to zero.
/// @param _kyberDao kyberDao address.
function setDaoContract(IKyberDao _kyberDao) external {
require(msg.sender == daoSetter, "only daoSetter");
require(_kyberDao != IKyberDao(0));
kyberDao = _kyberDao;
emit KyberDaoAddressSet(kyberDao);
daoSetter = address(0);
}
/// @dev set new kyberNetwork address by daoOperator
/// @param _kyberNetwork new kyberNetwork contract
function setNetworkContract(address _kyberNetwork) external onlyDaoOperator {
require(_kyberNetwork != address(0), "kyberNetwork 0");
if (_kyberNetwork != kyberNetwork) {
kyberNetwork = _kyberNetwork;
emit KyberNetworkUpdated(kyberNetwork);
}
}
/// @dev Allow to set kyberNetworkProxy address by daoOperator
/// @param _newProxy new kyberNetworkProxy contract
function setKyberProxy(IKyberProxy _newProxy) external onlyDaoOperator {
require(_newProxy != IKyberProxy(0), "kyberNetworkProxy 0");
if (_newProxy != kyberProxy) {
kyberProxy = _newProxy;
emit KyberProxyUpdated(_newProxy);
}
}
/// @dev set knc sanity rate contract and amount wei to burn
/// @param _sanityRate new sanity rate contract
/// @param _weiToBurn new amount of wei to burn
function setBurnConfigParams(ISanityRate _sanityRate, uint256 _weiToBurn)
external
onlyDaoOperator
{
require(_weiToBurn > 0, "_weiToBurn is 0");
if (sanityRateContract.length == 0 || (_sanityRate != sanityRateContract[0])) {
// it is a new sanity rate contract
if (sanityRateContract.length == 0) {
sanityRateContract.push(_sanityRate);
} else {
sanityRateContract.push(sanityRateContract[0]);
sanityRateContract[0] = _sanityRate;
}
}
weiToBurn = _weiToBurn;
emit BurnConfigSet(_sanityRate, _weiToBurn);
}
/// @dev Burn knc. The burn amount is limited. Forces block delay between burn calls.
/// @dev only none ontract can call this function
/// @return kncBurnAmount amount of knc burned
function burnKnc() external onlyNonContract returns (uint256 kncBurnAmount) {
// check if current block > last burn block number + num block interval
require(block.number > lastBurnBlock + burnBlockInterval, "wait more blocks to burn");
// update last burn block number
lastBurnBlock = block.number;
// Get amount to burn, if greater than weiToBurn, burn only weiToBurn per function call.
uint256 balance = address(this).balance;
// redundant check, can't happen
assert(balance >= totalPayoutBalance);
uint256 srcAmount = balance.sub(totalPayoutBalance);
srcAmount = srcAmount > weiToBurn ? weiToBurn : srcAmount;
// Get rate
uint256 kyberEthKncRate = kyberProxy.getExpectedRateAfterFee(
ETH_TOKEN_ADDRESS,
knc,
srcAmount,
0,
""
);
validateEthToKncRateToBurn(kyberEthKncRate);
// Buy some knc and burn
kncBurnAmount = kyberProxy.swapEtherToToken{value: srcAmount}(
knc,
kyberEthKncRate
);
require(IBurnableToken(address(knc)).burn(kncBurnAmount), "knc burn failed");
emit KncBurned(kncBurnAmount, ETH_TOKEN_ADDRESS, srcAmount);
return kncBurnAmount;
}
/// @dev if no one voted for an epoch (like epoch 0), no one gets rewards - should burn it.
/// Will move the epoch reward amount to burn amount. So can later be burned.
/// calls kyberDao contract to check if there were any votes for this epoch.
/// @param epoch epoch number to check.
function makeEpochRewardBurnable(uint256 epoch) external {
require(kyberDao != IKyberDao(0), "kyberDao not set");
require(kyberDao.shouldBurnRewardForEpoch(epoch), "should not burn reward");
uint256 rewardAmount = rewardsPerEpoch[epoch];
require(rewardAmount > 0, "reward is 0");
// redundant check, can't happen
require(totalPayoutBalance >= rewardAmount, "total reward less than epoch reward");
totalPayoutBalance = totalPayoutBalance.sub(rewardAmount);
rewardsPerEpoch[epoch] = 0;
emit RewardsRemovedToBurn(epoch, rewardAmount);
}
/// @notice should be called off chain
/// @dev returns list of sanity rate contracts
/// @dev index 0 is currently used contract address, indexes > 0 are older versions
function getSanityRateContracts() external view returns (ISanityRate[] memory sanityRates) {
sanityRates = sanityRateContract;
}
/// @dev return latest knc/eth rate from sanity rate contract
function getLatestSanityRate() external view returns (uint256 kncToEthSanityRate) {
if (sanityRateContract.length > 0 && sanityRateContract[0] != ISanityRate(0)) {
kncToEthSanityRate = sanityRateContract[0].latestAnswer();
} else {
kncToEthSanityRate = 0;
}
}
function getBRR()
public
returns (
uint256 rewardBps,
uint256 rebateBps,
uint256 epoch
)
{
uint256 expiryTimestamp;
(rewardBps, rebateBps, expiryTimestamp, epoch) = readBRRData();
// Check current timestamp
if (now > expiryTimestamp && kyberDao != IKyberDao(0)) {
uint256 burnBps;
(burnBps, rewardBps, rebateBps, epoch, expiryTimestamp) = kyberDao
.getLatestBRRDataWithCache();
require(burnBps.add(rewardBps).add(rebateBps) == BPS, "Bad BRR values");
emit BRRUpdated(rewardBps, rebateBps, burnBps, expiryTimestamp, epoch);
// Update brrAndEpochData
updateBRRData(rewardBps, rebateBps, expiryTimestamp, epoch);
}
}
function readBRRData()
public
view
returns (
uint256 rewardBps,
uint256 rebateBps,
uint256 expiryTimestamp,
uint256 epoch
)
{
rewardBps = uint256(brrAndEpochData.rewardBps);
rebateBps = uint256(brrAndEpochData.rebateBps);
epoch = uint256(brrAndEpochData.epoch);
expiryTimestamp = uint256(brrAndEpochData.expiryTimestamp);
}
function updateBRRData(
uint256 reward,
uint256 rebate,
uint256 expiryTimestamp,
uint256 epoch
) internal {
// reward and rebate combined values <= BPS. Tested in getBRR.
require(expiryTimestamp < 2**64, "expiry timestamp overflow");
require(epoch < 2**32, "epoch overflow");
brrAndEpochData.rewardBps = uint16(reward);
brrAndEpochData.rebateBps = uint16(rebate);
brrAndEpochData.expiryTimestamp = uint64(expiryTimestamp);
brrAndEpochData.epoch = uint32(epoch);
}
function getRRWeiValues(uint256 RRAmountWei)
internal
returns (
uint256 rewardWei,
uint256 rebateWei,
uint256 epoch
)
{
// Decoding BRR data
uint256 rewardInBps;
uint256 rebateInBps;
(rewardInBps, rebateInBps, epoch) = getBRR();
rebateWei = RRAmountWei.mul(rebateInBps).div(BPS);
rewardWei = RRAmountWei.mul(rewardInBps).div(BPS);
}
function updateRebateValues(
uint256 rebateWei,
address[] memory rebateWallets,
uint256[] memory rebateBpsPerWallet
) internal returns (uint256 totalRebatePaidWei) {
uint256 totalRebateBps;
uint256 walletRebateWei;
for (uint256 i = 0; i < rebateWallets.length; i++) {
require(rebateWallets[i] != address(0), "rebate wallet address 0");
walletRebateWei = rebateWei.mul(rebateBpsPerWallet[i]).div(BPS);
rebatePerWallet[rebateWallets[i]] = rebatePerWallet[rebateWallets[i]].add(
walletRebateWei
);
// a few wei could be left out due to rounding down. so count only paid wei
totalRebatePaidWei = totalRebatePaidWei.add(walletRebateWei);
totalRebateBps = totalRebateBps.add(rebateBpsPerWallet[i]);
}
require(totalRebateBps <= BPS, "rebates more then 100%");
}
function validateEthToKncRateToBurn(uint256 rateEthToKnc) internal view {
require(rateEthToKnc <= MAX_RATE, "ethToKnc rate out of bounds");
require(rateEthToKnc > 0, "ethToKnc rate is 0");
require(sanityRateContract.length > 0, "no sanity rate contract");
require(sanityRateContract[0] != ISanityRate(0), "sanity rate is 0x0, burning is blocked");
// get latest knc/eth rate from sanity contract
uint256 kncToEthRate = sanityRateContract[0].latestAnswer();
require(kncToEthRate > 0, "sanity rate is 0");
require(kncToEthRate <= MAX_RATE, "sanity rate out of bounds");
uint256 sanityEthToKncRate = PRECISION.mul(PRECISION).div(kncToEthRate);
// rate shouldn't be SANITY_RATE_DIFF_BPS lower than sanity rate
require(
rateEthToKnc.mul(BPS) >= sanityEthToKncRate.mul(BPS.sub(SANITY_RATE_DIFF_BPS)),
"kyberNetwork eth to knc rate too low"
);
}
}
{
"compilationTarget": {
"KyberFeeHandler.sol": "KyberFeeHandler"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 430
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_daoSetter","type":"address"},{"internalType":"contract IKyberProxy","name":"_kyberProxy","type":"address"},{"internalType":"address","name":"_kyberNetwork","type":"address"},{"internalType":"contract IERC20","name":"_knc","type":"address"},{"internalType":"uint256","name":"_burnBlockInterval","type":"uint256"},{"internalType":"address","name":"_daoOperator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rewardBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rebateBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"burnBps","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"expiryTimestamp","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"BRRUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract ISanityRate","name":"sanityRate","type":"address"},{"indexed":false,"internalType":"uint256","name":"weiToBurn","type":"uint256"}],"name":"BurnConfigSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EthReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"address","name":"platformWallet","type":"address"},{"indexed":false,"internalType":"uint256","name":"platformFeeWei","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardWei","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rebateWei","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"rebateWallets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"rebatePercentBpsPerWallet","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"burnAmtWei","type":"uint256"}],"name":"FeeDistributed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"kncTWei","type":"uint256"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"KncBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IKyberDao","name":"kyberDao","type":"address"}],"name":"KyberDaoAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"kyberNetwork","type":"address"}],"name":"KyberNetworkUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IKyberProxy","name":"kyberProxy","type":"address"}],"name":"KyberProxyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"platformWallet","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"PlatformFeePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rebateWallet","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RebatePaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"staker","type":"address"},{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RewardPaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardsWei","type":"uint256"}],"name":"RewardsRemovedToBurn","type":"event"},{"inputs":[],"name":"brrAndEpochData","outputs":[{"internalType":"uint64","name":"expiryTimestamp","type":"uint64"},{"internalType":"uint32","name":"epoch","type":"uint32"},{"internalType":"uint16","name":"rewardBps","type":"uint16"},{"internalType":"uint16","name":"rebateBps","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnBlockInterval","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"burnKnc","outputs":[{"internalType":"uint256","name":"kncBurnAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"platformWallet","type":"address"}],"name":"claimPlatformFee","outputs":[{"internalType":"uint256","name":"amountWei","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebateWallet","type":"address"}],"name":"claimReserveRebate","outputs":[{"internalType":"uint256","name":"amountWei","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"staker","type":"address"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"claimStakerReward","outputs":[{"internalType":"uint256","name":"amountWei","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"daoOperator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"daoSetter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feePerPlatformWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBRR","outputs":[{"internalType":"uint256","name":"rewardBps","type":"uint256"},{"internalType":"uint256","name":"rebateBps","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLatestSanityRate","outputs":[{"internalType":"uint256","name":"kncToEthSanityRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSanityRateContracts","outputs":[{"internalType":"contract ISanityRate[]","name":"sanityRates","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address[]","name":"rebateWallets","type":"address[]"},{"internalType":"uint256[]","name":"rebateBpsPerWallet","type":"uint256[]"},{"internalType":"address","name":"platformWallet","type":"address"},{"internalType":"uint256","name":"platformFee","type":"uint256"},{"internalType":"uint256","name":"networkFee","type":"uint256"}],"name":"handleFees","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"hasClaimedReward","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"knc","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kyberDao","outputs":[{"internalType":"contract IKyberDao","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kyberNetwork","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kyberProxy","outputs":[{"internalType":"contract IKyberProxy","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastBurnBlock","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"}],"name":"makeEpochRewardBurnable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"readBRRData","outputs":[{"internalType":"uint256","name":"rewardBps","type":"uint256"},{"internalType":"uint256","name":"rebateBps","type":"uint256"},{"internalType":"uint256","name":"expiryTimestamp","type":"uint256"},{"internalType":"uint256","name":"epoch","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rebatePerWallet","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardsPaidPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardsPerEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract ISanityRate","name":"_sanityRate","type":"address"},{"internalType":"uint256","name":"_weiToBurn","type":"uint256"}],"name":"setBurnConfigParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKyberDao","name":"_kyberDao","type":"address"}],"name":"setDaoContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IKyberProxy","name":"_newProxy","type":"address"}],"name":"setKyberProxy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_kyberNetwork","type":"address"}],"name":"setNetworkContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalPayoutBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"weiToBurn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"stateMutability":"payable","type":"receive"}]