// SPDX-License-Identifier: MITpragmasolidity >=0.6.2 <0.8.0;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on 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-assemblyassembly { size :=extcodesize(account) }
return size >0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable 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");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data, stringmemory errorMessage) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value, stringmemory errorMessage) internalreturns (bytesmemory) {
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, bytesmemory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data, stringmemory errorMessage) internalviewreturns (bytesmemory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data, stringmemory errorMessage) internalreturns (bytesmemory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function_verifyCallResult(bool success, bytesmemory returndata, stringmemory errorMessage) privatepurereturns(bytesmemory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly// solhint-disable-next-line no-inline-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Contract Source Code
File 2 of 15: Controllable.sol
//SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;import"./Governable.sol";
contractControllableisGovernable{
constructor(address _storage) publicGovernable(_storage) {
}
modifieronlyController() {
require(store.isController(msg.sender), "Not a controller");
_;
}
modifieronlyControllerOrGovernance(){
require((store.isController(msg.sender) || store.isGovernance(msg.sender)),
"The caller must be controller or governance");
_;
}
functioncontroller() publicviewreturns (address) {
return store.controller();
}
}
Contract Source Code
File 3 of 15: Controller.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;import"@openzeppelin/contracts/utils/Address.sol";
import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"./inheritance/Governable.sol";
import"./interface/IController.sol";
import"./interface/IStrategy.sol";
import"./interface/IVault.sol";
import"./RewardForwarder.sol";
contractControllerisGovernable{
usingSafeERC20forIERC20;
usingAddressforaddress;
usingSafeMathforuint256;
// ========================= Fields =========================// external partiesaddresspublic targetToken;
addresspublic protocolFeeReceiver;
addresspublic profitSharingReceiver;
addresspublic rewardForwarder;
addresspublic universalLiquidator;
addresspublic dolomiteYieldFarmingRouter;
uint256public nextImplementationDelay;
/// 15% of fees captured go to iFARM stakersuint256public profitSharingNumerator =700;
uint256public nextProfitSharingNumerator =0;
uint256public nextProfitSharingNumeratorTimestamp =0;
/// 5% of fees captured go to strategistsuint256public strategistFeeNumerator =0;
uint256public nextStrategistFeeNumerator =0;
uint256public nextStrategistFeeNumeratorTimestamp =0;
/// 5% of fees captured go to the devs of the platformuint256public platformFeeNumerator =300;
uint256public nextPlatformFeeNumerator =0;
uint256public nextPlatformFeeNumeratorTimestamp =0;
/// used for queuing a new delayuint256public tempNextImplementationDelay =0;
uint256public tempNextImplementationDelayTimestamp =0;
uint256publicconstant MAX_TOTAL_FEE =3000;
uint256publicconstant FEE_DENOMINATOR =10000;
/// @notice This mapping allows certain contracts to stake on a user's behalfmapping (address=>bool) public addressWhitelist;
mapping (bytes32=>bool) public codeWhitelist;
// All eligible hardWorkers that we havemapping (address=>bool) public hardWorkers;
// ========================= Events =========================eventQueueProfitSharingChange(uint profitSharingNumerator, uint validAtTimestamp);
eventConfirmProfitSharingChange(uint profitSharingNumerator);
eventQueueStrategistFeeChange(uint strategistFeeNumerator, uint validAtTimestamp);
eventConfirmStrategistFeeChange(uint strategistFeeNumerator);
eventQueuePlatformFeeChange(uint platformFeeNumerator, uint validAtTimestamp);
eventConfirmPlatformFeeChange(uint platformFeeNumerator);
eventQueueNextImplementationDelay(uint implementationDelay, uint validAtTimestamp);
eventConfirmNextImplementationDelay(uint implementationDelay);
eventAddedAddressToWhitelist(addressindexed _address);
eventRemovedAddressFromWhitelist(addressindexed _address);
eventAddedCodeToWhitelist(addressindexed _address);
eventRemovedCodeFromWhitelist(addressindexed _address);
eventSharePriceChangeLog(addressindexed vault,
addressindexed strategy,
uint256 oldSharePrice,
uint256 newSharePrice,
uint256 timestamp
);
// ========================= Modifiers =========================modifieronlyHardWorkerOrGovernance() {
require(hardWorkers[msg.sender] || (msg.sender== governance()),
"only hard worker can call this");
_;
}
constructor(address _storage,
address _targetToken,
address _protocolFeeReceiver,
address _profitSharingReceiver,
address _rewardForwarder,
address _universalLiquidator,
uint _nextImplementationDelay
)
Governable(_storage)
public{
require(_targetToken !=address(0), "_targetToken should not be empty");
require(_protocolFeeReceiver !=address(0), "_protocolFeeReceiver should not be empty");
require(_profitSharingReceiver !=address(0), "_profitSharingReceiver should not be empty");
require(_rewardForwarder !=address(0), "_rewardForwarder should not be empty");
require(_nextImplementationDelay >0, "_nextImplementationDelay should be gt 0");
targetToken = _targetToken;
protocolFeeReceiver = _protocolFeeReceiver;
profitSharingReceiver = _profitSharingReceiver;
rewardForwarder = _rewardForwarder;
universalLiquidator = _universalLiquidator;
nextImplementationDelay = _nextImplementationDelay;
}
// [Grey list]// An EOA can safely interact with the system no matter what.// If you're using Metamask, you're using an EOA.// Only smart contracts may be affected by this grey list.//// This contract will not be able to ban any EOA from the system// even if an EOA is being added to the greyList, he/she will still be able// to interact with the whole system as if nothing happened.// Only smart contracts will be affected by being added to the greyList.functiongreyList(address _addr) publicviewreturns (bool) {
return!addressWhitelist[_addr] &&!codeWhitelist[getContractHash(_addr)];
}
// Only smart contracts will be affected by the whitelist.functionaddToWhitelist(address _target) publiconlyGovernance{
addressWhitelist[_target] =true;
emit AddedAddressToWhitelist(_target);
}
functionaddMultipleToWhitelist(address[] memory _targets) publiconlyGovernance{
for (uint256 i =0; i < _targets.length; i++) {
addressWhitelist[_targets[i]] =true;
}
}
functionremoveFromWhitelist(address _target) publiconlyGovernance{
addressWhitelist[_target] =false;
emit RemovedAddressFromWhitelist(_target);
}
functionremoveMultipleFromWhitelist(address[] memory _targets) publiconlyGovernance{
for (uint256 i =0; i < _targets.length; i++) {
addressWhitelist[_targets[i]] =false;
}
}
functiongetContractHash(address a) publicviewreturns (bytes32 hash) {
assembly {
hash :=extcodehash(a)
}
}
functionaddCodeToWhitelist(address _target) publiconlyGovernance{
codeWhitelist[getContractHash(_target)] =true;
emit AddedCodeToWhitelist(_target);
}
functionremoveCodeFromWhitelist(address _target) publiconlyGovernance{
codeWhitelist[getContractHash(_target)] =false;
emit RemovedCodeFromWhitelist(_target);
}
functionsetRewardForwarder(address _rewardForwarder) publiconlyGovernance{
require(_rewardForwarder !=address(0), "new reward forwarder should not be empty");
rewardForwarder = _rewardForwarder;
}
functionsetTargetToken(address _targetToken) publiconlyGovernance{
require(_targetToken !=address(0), "new target token should not be empty");
targetToken = _targetToken;
}
functionsetProfitSharingReceiver(address _profitSharingReceiver) publiconlyGovernance{
require(_profitSharingReceiver !=address(0), "new profit sharing receiver should not be empty");
profitSharingReceiver = _profitSharingReceiver;
}
functionsetProtocolFeeReceiver(address _protocolFeeReceiver) publiconlyGovernance{
require(_protocolFeeReceiver !=address(0), "new protocol fee receiver should not be empty");
protocolFeeReceiver = _protocolFeeReceiver;
}
functionsetUniversalLiquidator(address _universalLiquidator) publiconlyGovernance{
require(_universalLiquidator !=address(0), "new universal liquidator should not be empty");
universalLiquidator = _universalLiquidator;
}
functionsetDolomiteYieldFarmingRouter(address _dolomiteYieldFarmingRouter) publiconlyGovernance{
require(_dolomiteYieldFarmingRouter !=address(0), "new reward forwarder should not be empty");
dolomiteYieldFarmingRouter = _dolomiteYieldFarmingRouter;
}
functiongetPricePerFullShare(address _vault) publicviewreturns (uint256) {
return IVault(_vault).getPricePerFullShare();
}
functiondoHardWork(address _vault) externalonlyHardWorkerOrGovernance{
uint256 oldSharePrice = IVault(_vault).getPricePerFullShare();
IVault(_vault).doHardWork();
emit SharePriceChangeLog(
_vault,
IVault(_vault).strategy(),
oldSharePrice,
IVault(_vault).getPricePerFullShare(),
block.timestamp
);
}
functionaddHardWorker(address _worker) publiconlyGovernance{
require(_worker !=address(0), "_worker must be defined");
hardWorkers[_worker] =true;
}
functionremoveHardWorker(address _worker) publiconlyGovernance{
require(_worker !=address(0), "_worker must be defined");
hardWorkers[_worker] =false;
}
// transfers token in the controller contract to the governancefunctionsalvage(address _token, uint256 _amount) externalonlyGovernance{
IERC20(_token).safeTransfer(governance(), _amount);
}
functionsalvageStrategy(address _strategy, address _token, uint256 _amount) externalonlyGovernance{
// the strategy is responsible for maintaining the list of// salvageable tokens, to make sure that governance cannot come// in and take away the coins
IStrategy(_strategy).salvageToken(governance(), _token, _amount);
}
functionfeeDenominator() publicpurereturns (uint) {
// keep the interface for this function as a `view` for now, in case it changes in the futurereturn FEE_DENOMINATOR;
}
functionsetProfitSharingNumerator(uint _profitSharingNumerator) publiconlyGovernance{
require(
_profitSharingNumerator + strategistFeeNumerator + platformFeeNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
nextProfitSharingNumerator = _profitSharingNumerator;
nextProfitSharingNumeratorTimestamp =block.timestamp+ nextImplementationDelay;
emit QueueProfitSharingChange(nextProfitSharingNumerator, nextProfitSharingNumeratorTimestamp);
}
functionconfirmSetProfitSharingNumerator() publiconlyGovernance{
require(
nextProfitSharingNumerator !=0&& nextProfitSharingNumeratorTimestamp !=0&&block.timestamp>= nextProfitSharingNumeratorTimestamp,
"invalid timestamp or no new profit sharing numerator confirmed"
);
require(
nextProfitSharingNumerator + strategistFeeNumerator + platformFeeNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
profitSharingNumerator = nextProfitSharingNumerator;
nextProfitSharingNumerator =0;
nextProfitSharingNumeratorTimestamp =0;
emit ConfirmProfitSharingChange(profitSharingNumerator);
}
functionsetStrategistFeeNumerator(uint _strategistFeeNumerator) publiconlyGovernance{
require(
_strategistFeeNumerator + platformFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
nextStrategistFeeNumerator = _strategistFeeNumerator;
nextStrategistFeeNumeratorTimestamp =block.timestamp+ nextImplementationDelay;
emit QueueStrategistFeeChange(nextStrategistFeeNumerator, nextStrategistFeeNumeratorTimestamp);
}
functionconfirmSetStrategistFeeNumerator() publiconlyGovernance{
require(
nextStrategistFeeNumerator !=0&& nextStrategistFeeNumeratorTimestamp !=0&&block.timestamp>= nextStrategistFeeNumeratorTimestamp,
"invalid timestamp or no new strategist fee numerator confirmed"
);
require(
nextStrategistFeeNumerator + platformFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
strategistFeeNumerator = nextStrategistFeeNumerator;
nextStrategistFeeNumerator =0;
nextStrategistFeeNumeratorTimestamp =0;
emit ConfirmStrategistFeeChange(strategistFeeNumerator);
}
functionsetPlatformFeeNumerator(uint _platformFeeNumerator) publiconlyGovernance{
require(
_platformFeeNumerator + strategistFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
nextPlatformFeeNumerator = _platformFeeNumerator;
nextPlatformFeeNumeratorTimestamp =block.timestamp+ nextImplementationDelay;
emit QueuePlatformFeeChange(nextPlatformFeeNumerator, nextPlatformFeeNumeratorTimestamp);
}
functionconfirmSetPlatformFeeNumerator() publiconlyGovernance{
require(
nextPlatformFeeNumerator !=0&& nextPlatformFeeNumeratorTimestamp !=0&&block.timestamp>= nextPlatformFeeNumeratorTimestamp,
"invalid timestamp or no new platform fee numerator confirmed"
);
require(
nextPlatformFeeNumerator + strategistFeeNumerator + profitSharingNumerator <= MAX_TOTAL_FEE,
"total fee too high"
);
platformFeeNumerator = nextPlatformFeeNumerator;
nextPlatformFeeNumerator =0;
nextPlatformFeeNumeratorTimestamp =0;
emit ConfirmPlatformFeeChange(platformFeeNumerator);
}
functionsetNextImplementationDelay(uint256 _nextImplementationDelay) publiconlyGovernance{
require(
_nextImplementationDelay >0,
"invalid _nextImplementationDelay"
);
tempNextImplementationDelay = _nextImplementationDelay;
tempNextImplementationDelayTimestamp =block.timestamp+ nextImplementationDelay;
emit QueueNextImplementationDelay(tempNextImplementationDelay, tempNextImplementationDelayTimestamp);
}
functionconfirmNextImplementationDelay() publiconlyGovernance{
require(
tempNextImplementationDelayTimestamp !=0&&block.timestamp>= tempNextImplementationDelayTimestamp,
"invalid timestamp or no new implementation delay confirmed"
);
nextImplementationDelay = tempNextImplementationDelay;
tempNextImplementationDelay =0;
tempNextImplementationDelayTimestamp =0;
emit ConfirmNextImplementationDelay(nextImplementationDelay);
}
}
Contract Source Code
File 4 of 15: Governable.sol
//SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;import"./Storage.sol";
contractGovernable{
Storage public store;
constructor(address _store) public{
require(_store !=address(0), "new storage shouldn't be empty");
store = Storage(_store);
}
modifieronlyGovernance() {
require(store.isGovernance(msg.sender), "Not governance");
_;
}
functionsetStorage(address _store) publiconlyGovernance{
require(_store !=address(0), "new storage shouldn't be empty");
store = Storage(_store);
}
functiongovernance() publicviewreturns (address) {
return store.governance();
}
}
Contract Source Code
File 5 of 15: IController.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;interfaceIController{
// ========================= Events =========================eventQueueProfitSharingChange(uint profitSharingNumerator, uint validAtTimestamp);
eventConfirmProfitSharingChange(uint profitSharingNumerator);
eventQueueStrategistFeeChange(uint strategistFeeNumerator, uint validAtTimestamp);
eventConfirmStrategistFeeChange(uint strategistFeeNumerator);
eventQueuePlatformFeeChange(uint platformFeeNumerator, uint validAtTimestamp);
eventConfirmPlatformFeeChange(uint platformFeeNumerator);
eventQueueNextImplementationDelay(uint implementationDelay, uint validAtTimestamp);
eventConfirmNextImplementationDelay(uint implementationDelay);
eventAddedStakingContract(addressindexed stakingContract);
eventRemovedStakingContract(addressindexed stakingContract);
eventSharePriceChangeLog(addressindexed vault,
addressindexed strategy,
uint256 oldSharePrice,
uint256 newSharePrice,
uint256 timestamp
);
// ==================== Functions ====================/**
* An EOA can safely interact with the system no matter what. If you're using Metamask, you're using an EOA. Only
* smart contracts may be affected by this grey list. This contract will not be able to ban any EOA from the system
* even if an EOA is being added to the greyList, he/she will still be able to interact with the whole system as if
* nothing happened. Only smart contracts will be affected by being added to the greyList. This grey list is only
* used in VaultV3.sol, see the code there for reference
*/functiongreyList(address _target) externalviewreturns (bool);
functionaddressWhiteList(address _target) externalviewreturns (bool);
functioncodeWhiteList(address _target) externalviewreturns (bool);
functionaddToWhitelist(address _target) external;
functionaddCodeToWhitelist(address _target) external;
functionstore() externalviewreturns (address);
functiongovernance() externalviewreturns (address);
functiondoHardWork(address _vault) external;
functionaddHardWorker(address _worker) external;
functionremoveHardWorker(address _worker) external;
functionsalvage(address _token, uint256 amount) external;
functionsalvageStrategy(address _strategy, address _token, uint256 amount) external;
/**
* @return The targeted profit token to convert all-non-compounding rewards to. Defaults to WETH.
*/functiontargetToken() externalviewreturns (address);
functionsetTargetToken(address _targetToken) external;
functionprofitSharingReceiver() externalviewreturns (address);
functionsetProfitSharingReceiver(address _profitSharingReceiver) external;
functionprotocolFeeReceiver() externalviewreturns (address);
functionsetProtocolFeeReceiver(address _protocolFeeReceiver) external;
functionrewardForwarder() externalviewreturns (address);
functionsetRewardForwarder(address _rewardForwarder) external;
functionuniversalLiquidator() externalviewreturns (address);
functionsetUniversalLiquidator(address _universalLiquidator) external;
functiondolomiteYieldFarmingRouter() externalviewreturns (address);
functionsetDolomiteYieldFarmingRouter(address _value) external;
functionnextImplementationDelay() externalviewreturns (uint256);
functionprofitSharingNumerator() externalviewreturns (uint256);
functionstrategistFeeNumerator() externalviewreturns (uint256);
functionplatformFeeNumerator() externalviewreturns (uint256);
functionfeeDenominator() externalviewreturns (uint256);
functionsetProfitSharingNumerator(uint _profitSharingNumerator) external;
functionconfirmSetProfitSharingNumerator() external;
functionsetStrategistFeeNumerator(uint _strategistFeeNumerator) external;
functionconfirmSetStrategistFeeNumerator() external;
functionsetPlatformFeeNumerator(uint _platformFeeNumerator) external;
functionconfirmSetPlatformFeeNumerator() external;
functionsetNextImplementationDelay(uint256 _nextImplementationDelay) external;
functionconfirmNextImplementationDelay() external;
functionnextProfitSharingNumerator() externalviewreturns (uint256);
functionnextProfitSharingNumeratorTimestamp() externalviewreturns (uint256);
functionnextStrategistFeeNumerator() externalviewreturns (uint256);
functionnextStrategistFeeNumeratorTimestamp() externalviewreturns (uint256);
functionnextPlatformFeeNumerator() externalviewreturns (uint256);
functionnextPlatformFeeNumeratorTimestamp() externalviewreturns (uint256);
functiontempNextImplementationDelay() externalviewreturns (uint256);
functiontempNextImplementationDelayTimestamp() externalviewreturns (uint256);
}
Contract Source Code
File 6 of 15: IERC20.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;/**
* @dev A routing contract that is responsible for taking the harvested gains and routing them into FARM and additional
* buyback tokens for the corresponding strategy
*/interfaceIRewardForwarder{
functionstore() externalviewreturns (address);
functiongovernance() externalviewreturns (address);
/**
* @dev This function sends converted `_buybackTokens` to `msg.sender`. The returned amounts will match the
* `amounts` return value. The fee amounts are converted to the profit sharing token and sent to the proper
* addresses (profit sharing, strategist, and governance (platform)).
*
* @param _token the token that will be compounded or sold into the profit sharing token for the Harvest
* collective (users that stake iFARM)
* @param _profitSharingFee the amount of `_token` that will be sold into the profit sharing token
* @param _strategistFee the amount of `_token` that will be sold into the profit sharing token for the
* strategist
* @param _platformFee the amount of `_token` that will be sold into the profit sharing token for the Harvest
* treasury
* @param _buybackTokens the output tokens that `_buyBackAmounts` should be swapped to (outputToken)
* @param _buybackAmounts the amounts of `_token` that will be bought into more `_buybackTokens` token
* @return amounts The amounts that were purchased of _buybackTokens
*/functionnotifyFeeAndBuybackAmounts(address _token,
uint256 _profitSharingFee,
uint256 _strategistFee,
uint256 _platformFee,
address[] calldata _buybackTokens,
uint256[] calldata _buybackAmounts
) externalreturns (uint[] memory amounts);
/**
* @dev This function converts the fee amounts to the profit sharing token and sends them to the proper addresses
* (profit sharing, strategist, and governance (platform)).
*
* @param _token the token that will be compounded or sold into the profit sharing token for the Harvest
* collective (users that stake iFARM)
* @param _profitSharingFee the amount of `_token` that will be sold into the profit sharing token
* @param _strategistFee the amount of `_token` that will be sold into the profit sharing token for the
* strategist
* @param _platformFee the amount of `_token` that will be sold into the profit sharing token for the Harvest
* treasury
*/functionnotifyFee(address _token,
uint256 _profitSharingFee,
uint256 _strategistFee,
uint256 _platformFee
) external;
}
Contract Source Code
File 9 of 15: IStrategy.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;interfaceIStrategy{
/// @notice declared as public so child contract can call itfunctionisUnsalvageableToken(address token) externalviewreturns (bool);
functionsalvageToken(address recipient, address token, uint amount) external;
functiongovernance() externalviewreturns (address);
functioncontroller() externalviewreturns (address);
functionunderlying() externalviewreturns (address);
functionvault() externalviewreturns (address);
functionwithdrawAllToVault() external;
functionwithdrawToVault(uint256 _amount) external;
functioninvestedUnderlyingBalance() externalviewreturns (uint256);
functiondoHardWork() external;
functiondepositArbCheck() externalviewreturns (bool);
functionstrategist() externalviewreturns (address);
/**
* @return The value of any accumulated rewards that are under control by the strategy. Each index corresponds with
* the tokens in `rewardTokens`. This function is not a `view`, because some protocols, like Curve, need
* writeable functions to get the # of claimable reward tokens
*/functiongetRewardPoolValues() externalreturns (uint256[] memory);
}
Contract Source Code
File 10 of 15: IUniversalLiquidatorV1.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;pragmaexperimentalABIEncoderV2;/**
* @dev A contract that handles all liquidations from an `inputToken` to an `outputToken`. This contract simplifies
* all swap logic so strategies can be focused on management of funds and forwarding gains to this contract
* for the most efficient liquidation. If the liquidation path of an asset changes, governance needs only to
* create a new instance of this contract or modify the liquidation path via `configureSwap`, and all callers of
* the contract benefit from the change and uniformity.
*/interfaceIUniversalLiquidatorV1{
// ==================== Events ====================eventSwap(addressindexed buyToken,
addressindexed sellToken,
addressindexed recipient,
address initiator,
uint256 amountIn,
uint256 slippage,
uint256 total
);
// ==================== Functions ====================functiongovernance() externalviewreturns (address);
functioncontroller() externalviewreturns (address);
functionnextImplementation() externalviewreturns (address);
functionscheduleUpgrade(address _nextImplementation) external;
/**
* Constructor replacement because this contract is meant to be upgradable
*/functioninitializeUniversalLiquidator(address _storage
) external;
/**
* @param _path The path that is used for selling token at path[0] into path[path.length - 1].
* @param _router The router to use for this path.
*/functionconfigureSwap(address[] calldata _path,
address _router
) external;
/**
* @param _paths The paths that are used for selling token at path[i][0] into path[i][path[i].length - 1].
* @param _routers The routers to use for each index, `i`.
*/functionconfigureSwaps(address[][] calldata _paths,
address[] calldata _routers
) external;
/**
* @return The router used to execute the swap from `_inputToken` to `_outputToken`
*/functiongetSwapRouter(address _inputToken,
address _outputToken
) externalviewreturns (address);
functionswapTokens(address _tokenIn,
address _tokenOut,
uint256 _amountIn,
uint256 _amountOutMin,
address _recipient
) externalreturns (uint _amountOut);
}
Contract Source Code
File 11 of 15: IVault.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;interfaceIVault{
functioninitializeVault(address _storage,
address _underlying,
uint256 _toInvestNumerator,
uint256 _toInvestDenominator
) external;
functionbalanceOf(address _holder) externalviewreturns (uint256);
functionunderlyingBalanceInVault() externalviewreturns (uint256);
functionunderlyingBalanceWithInvestment() externalviewreturns (uint256);
functiongovernance() externalviewreturns (address);
functioncontroller() externalviewreturns (address);
functionunderlying() externalviewreturns (address);
functionunderlyingUnit() externalviewreturns (uint);
functionstrategy() externalviewreturns (address);
functionsetStrategy(address _strategy) external;
functionannounceStrategyUpdate(address _strategy) external;
functionsetVaultFractionToInvest(uint256 _numerator, uint256 _denominator) external;
functiondeposit(uint256 _amount) external;
functiondeposit(uint256 _amount, address _receiver) external;
functiondepositFor(uint256 _amount, address _holder) external;
functionwithdrawAll() external;
functionwithdraw(uint256 _numberOfShares) external;
functiongetPricePerFullShare() externalviewreturns (uint256);
functionunderlyingBalanceWithInvestmentForHolder(address _holder) viewexternalreturns (uint256);
/**
* The total amount available to be deposited from this vault into the strategy, while adhering to the
* `vaultFractionToInvestNumerator` and `vaultFractionToInvestDenominator` rules
*/functionavailableToInvestOut() externalviewreturns (uint256);
/**
* This should be callable only by the controller (by the hard worker) or by governance
*/functiondoHardWork() external;
}
Contract Source Code
File 12 of 15: RewardForwarder.sol
// SPDX-License-Identifier: Unlicensepragmasolidity 0.6.12;import"@openzeppelin/contracts/math/SafeMath.sol";
import"@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"./inheritance/Governable.sol";
import"./interface/IController.sol";
import"./interface/IRewardForwarder.sol";
import"./interface/IProfitSharingReceiver.sol";
import"./interface/IStrategy.sol";
import"./interface/IUniversalLiquidatorV1.sol";
import"./inheritance/Controllable.sol";
/**
* @dev This contract receives rewards from strategies and is responsible for routing the reward's liquidation into
* specific buyback tokens and profit tokens for the DAO.
*/contractRewardForwarderisControllable{
usingSafeERC20forIERC20;
usingSafeMathforuint256;
constructor(address _storage
) publicControllable(_storage) {}
functionnotifyFee(address _token,
uint256 _profitSharingFee,
uint256 _strategistFee,
uint256 _platformFee
) external{
_notifyFee(
_token,
_profitSharingFee,
_strategistFee,
_platformFee
);
}
function_notifyFee(address _token,
uint256 _profitSharingFee,
uint256 _strategistFee,
uint256 _platformFee
) internal{
address _controller = controller();
address liquidator = IController(_controller).universalLiquidator();
uint totalTransferAmount = _profitSharingFee.add(_strategistFee).add(_platformFee);
require(totalTransferAmount >0, "totalTransferAmount should not be 0");
IERC20(_token).safeTransferFrom(msg.sender, address(this), totalTransferAmount);
address _targetToken = IController(_controller).targetToken();
if (_targetToken != _token) {
IERC20(_token).safeApprove(liquidator, 0);
IERC20(_token).safeApprove(liquidator, totalTransferAmount);
uint amountOutMin =1;
if (_strategistFee >0) {
IUniversalLiquidatorV1(liquidator).swapTokens(
_token,
_targetToken,
_strategistFee,
amountOutMin,
IStrategy(msg.sender).strategist()
);
}
if (_platformFee >0) {
IUniversalLiquidatorV1(liquidator).swapTokens(
_token,
_targetToken,
_platformFee,
amountOutMin,
IController(_controller).protocolFeeReceiver()
);
}
if (_profitSharingFee >0) {
IUniversalLiquidatorV1(liquidator).swapTokens(
_token,
_targetToken,
_profitSharingFee,
amountOutMin,
IController(_controller).profitSharingReceiver()
);
}
} else {
IERC20(_targetToken).safeTransfer(IStrategy(msg.sender).strategist(), _strategistFee);
IERC20(_targetToken).safeTransfer(IController(_controller).protocolFeeReceiver(), _platformFee);
IERC20(_targetToken).safeTransfer(IController(_controller).profitSharingReceiver(), _profitSharingFee);
}
}
}
Contract Source Code
File 13 of 15: SafeERC20.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.8.0;import"./IERC20.sol";
import"../../math/SafeMath.sol";
import"../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingSafeMathforuint256;
usingAddressforaddress;
functionsafeTransfer(IERC20 token, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/functionsafeApprove(IERC20 token, address spender, uint256 value) internal{
// safeApprove should only be called when setting an initial allowance,// or when resetting it to zero. To increase and decrease it, use// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'// solhint-disable-next-line max-line-lengthrequire((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));
}
functionsafeIncreaseAllowance(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));
}
functionsafeDecreaseAllowance(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));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) { // Return data is optional// solhint-disable-next-line max-line-lengthrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
Contract Source Code
File 14 of 15: SafeMath.sol
// SPDX-License-Identifier: MITpragmasolidity >=0.6.0 <0.8.0;/**
* @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.
*/librarySafeMath{
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryAdd(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
/**
* @dev Returns the substraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontrySub(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/functiontryMul(uint256 a, uint256 b) internalpurereturns (bool, 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/522if (a ==0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryDiv(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b ==0) return (false, 0);
return (true, a / b);
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/functiontryMod(uint256 a, uint256 b) internalpurereturns (bool, uint256) {
if (b ==0) return (false, 0);
return (true, a % b);
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/functionadd(uint256 a, uint256 b) internalpurereturns (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.
*/functionsub(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/functionmul(uint256 a, uint256 b) internalpurereturns (uint256) {
if (a ==0) return0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting 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.
*/functiondiv(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b >0, "SafeMath: division by zero");
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting 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.
*/functionmod(uint256 a, uint256 b) internalpurereturns (uint256) {
require(b >0, "SafeMath: modulo by zero");
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/functionsub(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryDiv}.
*
* 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.
*/functiondiv(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b >0, errorMessage);
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* 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.
*/functionmod(uint256 a, uint256 b, stringmemory errorMessage) internalpurereturns (uint256) {
require(b >0, errorMessage);
return a % b;
}
}