文件 1 的 1:VaultRebalancer.sol
pragma solidity 0.8.6;
interface IERC20 {
function totalSupply() external view returns (uint);
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint);
function symbol() external view returns (string memory);
function decimals() external view returns (uint);
function approve(address spender, uint amount) external returns (bool);
function mint(address account, uint amount) external;
function burn(address account, uint amount) external;
function transferFrom(address sender, address recipient, uint amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint value);
event Approval(address indexed owner, address indexed spender, uint value);
}
interface IVault is IERC20 {
function deposit(address _account, uint _amount) external;
function depositETH(address _account) external payable;
function withdraw(uint _amount) external;
function withdrawETH(uint _amount) external;
function withdrawFrom(address _source, uint _amount) external;
function withdrawFromETH(address _source, uint _amount) external;
function withdrawAll() external;
function withdrawAllETH() external;
function pushToken(address _token, uint _amount) external;
function setDepositsEnabled(bool _value) external;
function addIncome(uint _addAmount) external;
function rewardRate() external view returns(uint);
function underlying() external view returns(address);
function pendingAccountReward(address _account) external view returns(uint);
function claim(address _account) external;
}
interface IVaultController {
function depositsEnabled() external view returns(bool);
function setDepositLimit(address _vault, uint _amount) external;
function depositLimit(address _vault) external view returns(uint);
function setRebalancer(address _rebalancer) external;
function rebalancer() external view returns(address);
}
interface IVaultFactory {
function isVault(address _vault) external view returns(bool);
}
interface IOwnable {
function transferOwnership(address _newOwner) external;
function acceptOwnership() external;
}
interface IRewardDistribution {
function distributeReward(address _account, address _token) external;
function setTotalRewardPerBlock(uint _value) external;
function migrateRewards(address _recipient, uint _amount) external;
function accrueAllPools() external;
function pendingAccountReward(address _account, address _pair) external view returns(uint);
function addPool(
address _pair,
address _token,
bool _isSupply,
uint _points
) external;
function setReward(
address _pair,
address _token,
bool _isSupply,
uint _points
) external;
}
interface ILendingController {
function interestRateModel() external view returns(address);
function rewardDistribution() external view returns(IRewardDistribution);
function feeRecipient() external view returns(address);
function LIQ_MIN_HEALTH() external view returns(uint);
function minBorrowUSD() external view returns(uint);
function liqFeeSystem(address _token) external view returns(uint);
function liqFeeCaller(address _token) external view returns(uint);
function liqFeesTotal(address _token) external view returns(uint);
function colFactor(address _token) external view returns(uint);
function depositLimit(address _lendingPair, address _token) external view returns(uint);
function borrowLimit(address _lendingPair, address _token) external view returns(uint);
function originFee(address _token) external view returns(uint);
function depositsEnabled() external view returns(bool);
function borrowingEnabled() external view returns(bool);
function setFeeRecipient(address _feeRecipient) external;
function setColFactor(address _token, uint _value) external;
function tokenPrice(address _token) external view returns(uint);
function tokenSupported(address _token) external view returns(bool);
function setRewardDistribution(address _value) external;
function setInterestRateModel(address _value) external;
function targetLiqHealth() external view returns(uint);
function setDepositLimit(address _pair, address _token, uint _value) external;
}
interface IWETH {
function deposit() external payable;
function withdraw(uint wad) external;
function balanceOf(address account) external view returns (uint);
function transfer(address recipient, uint amount) external returns (bool);
function approve(address spender, uint amount) external returns (bool);
}
interface ILendingPair {
function checkAccountHealth(address _account) external view;
function accrueAccount(address _account) external;
function accrue() external;
function accountHealth(address _account) external view returns(uint);
function totalDebt(address _token) external view returns(uint);
function tokenA() external view returns(address);
function tokenB() external view returns(address);
function lpToken(address _token) external view returns(IERC20);
function debtOf(address _account, address _token) external view returns(uint);
function pendingDebtTotal(address _token) external view returns(uint);
function pendingSupplyTotal(address _token) external view returns(uint);
function lastBlockAccrued() external view returns(uint);
function deposit(address _account, address _token, uint _amount) external;
function pendingBorrowInterest(address _token, address _account) external view returns(uint);
function pendingSupplyInterest(address _token, address _account) external view returns(uint);
function supplyRatePerBlock(address _token) external view returns(uint);
function withdraw(address _token, uint _amount) external;
function borrow(address _token, uint _amount) external;
function repay(address _token, uint _amount) external;
function withdrawAll(address _token) external;
function depositRepay(address _account, address _token, uint _amount) external;
function withdrawBorrow(address _token, uint _amount) external;
function lendingController() external view returns(ILendingController);
function borrowBalance(
address _account,
address _borrowedToken,
address _returnToken
) external view returns(uint);
function supplyBalance(
address _account,
address _suppliedToken,
address _returnToken
) external view returns(uint);
function convertTokenValues(
address _fromToken,
address _toToken,
uint _inputAmount
) external view returns(uint);
}
interface IPairFactory {
function pairByTokens(address _tokenA, address _tokenB) external view returns(address);
function createPair(
address _tokenA,
address _tokenB
) external returns(address);
}
interface IVaultRebalancer {
function unload(address _vault, address _pair, uint _amount) external;
function distributeIncome(address _vault) external;
function pairDeposits(address _pair, address _underlying) external view returns(uint);
}
library Math {
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a / 2) + (b / 2) + (((a % 2) + (b % 2)) / 2);
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b + (a % b == 0 ? 0 : 1);
}
}
library SafeMath {
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b > a) return (false, 0);
return (true, a - b);
}
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a / b);
}
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
if (b == 0) return (false, 0);
return (true, a % b);
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a, "SafeMath: subtraction overflow");
return a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
if (a == 0) return 0;
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
function div(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: division by zero");
return a / b;
}
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
require(b > 0, "SafeMath: modulo by zero");
return a % b;
}
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a / b;
}
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
return a % b;
}
}
contract Ownable {
address public owner;
address public pendingOwner;
event OwnershipTransferInitiated(address indexed previousOwner, address indexed newOwner);
event OwnershipTransferConfirmed(address indexed previousOwner, address indexed newOwner);
constructor() {
owner = msg.sender;
emit OwnershipTransferConfirmed(address(0), owner);
}
modifier onlyOwner() {
require(isOwner(), "Ownable: caller is not the owner");
_;
}
function isOwner() public view returns (bool) {
return msg.sender == owner;
}
function transferOwnership(address _newOwner) external onlyOwner {
require(_newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferInitiated(owner, _newOwner);
pendingOwner = _newOwner;
}
function acceptOwnership() external {
require(msg.sender == pendingOwner, "Ownable: caller is not pending owner");
emit OwnershipTransferConfirmed(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
function safeApprove(IERC20 token, address spender, uint256 value) internal {
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
library Address {
function isContract(address account) internal view returns (bool) {
uint256 size;
assembly { size := extcodesize(account) }
return size > 0;
}
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) {
if (success) {
return returndata;
} else {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
_;
_status = _NOT_ENTERED;
}
}
contract Constants {
IWETH internal constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
}
library TransferHelper {
using SafeERC20 for IERC20;
IWETH internal constant WETH = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
function safeTransferFrom(address _token, address _sender, uint _amount) public {
require(_amount > 0, "TransferHelper: amount must be > 0");
IERC20(_token).safeTransferFrom(_sender, address(this), _amount);
}
function safeTransfer(address _token, address _recipient, uint _amount) public {
require(_amount > 0, "TransferHelper: amount must be > 0");
IERC20(_token).safeTransfer(_recipient, _amount);
}
function wethWithdrawTo(address _to, uint _amount) public {
require(_amount > 0, "TransferHelper: amount must be > 0");
require(_to != address(0), "TransferHelper: invalid recipient");
WETH.withdraw(_amount);
(bool success, ) = _to.call { value: _amount }(new bytes(0));
require(success, 'TransferHelper: ETH transfer failed');
}
function depositWeth() internal {
require(msg.value > 0, "TransferHelper: amount must be > 0");
WETH.deposit { value: msg.value }();
}
}
contract VaultRebalancer is IVaultRebalancer, Ownable, ReentrancyGuard, Constants {
using Address for address;
uint private constant DISTRIBUTION_PERIOD = 45_800;
uint private constant MAX_INCENTIVE = 1e17;
uint public callIncentive;
address public immutable vaultController;
address public immutable vaultFactory;
address public immutable pairFactory;
mapping (address => mapping (address => uint)) public override pairDeposits;
event InitiateRebalancerMigration(address indexed newOwner);
event Rebalance(address indexed fromPair, address indexed toPair, uint amount);
event FeeDistribution(uint amount);
event NewCallIncentive(uint value);
modifier onlyVault() {
require(IVaultFactory(vaultFactory).isVault(msg.sender), "VaultRebalancer: caller is not the vault");
_;
}
modifier vaultOrOwner() {
require(
IVaultFactory(vaultFactory).isVault(msg.sender) ||
msg.sender == owner,
"VaultRebalancer: unauthorized");
_;
}
receive() external payable {}
constructor(
address _vaultController,
address _vaultFactory,
address _pairFactory,
uint _callIncentive
) {
_requireContract(_vaultController);
_requireContract(_vaultFactory);
_requireContract(_pairFactory);
vaultController = _vaultController;
vaultFactory = _vaultFactory;
pairFactory = _pairFactory;
callIncentive = _callIncentive;
}
function rebalance(
address _vault,
address _fromPair,
address _toPair,
uint _withdrawAmount
) external onlyOwner nonReentrant {
_validatePair(_vault, _fromPair);
_validatePair(_vault, _toPair);
uint income = _pairWithdrawWithIncome(_vault, _fromPair, _withdrawAmount);
address underlying = _vaultUnderlying(_vault);
_pairDeposit(_vault, underlying, _toPair, _withdrawAmount);
_sendIncentiveWithLimit(underlying, income);
emit Rebalance(address(_fromPair), address(_toPair), _withdrawAmount);
}
function enterPair(
address _vault,
address _toPair,
uint _depositAmount
) external onlyOwner nonReentrant {
_validatePair(_vault, _toPair);
address underlying = address(_vaultUnderlying(_vault));
IVault(_vault).pushToken(underlying, _depositAmount);
uint callerIncentive = _sendIncentiveWithLimit(underlying, _depositAmount);
_pairDeposit(_vault, underlying, _toPair, _depositAmount - callerIncentive);
emit Rebalance(address(0), address(_toPair), _depositAmount);
}
function collectIncome(address _vault, address[] memory _pairs) external nonReentrant {
uint income;
for (uint i = 0; i < _pairs.length; i++) {
_validatePair(_vault, _pairs[i]);
address underlying = _vaultUnderlying(_vault);
uint balance = ILendingPair(_pairs[i]).supplyBalance(_vault, underlying, underlying);
income += (balance - pairDeposits[_pairs[i]][underlying]);
pairDeposits[_pairs[i]][underlying] = balance;
}
IVault(_vault).pushToken(_vaultUnderlying(_vault), income);
_sendIncentiveWithLimit(_vaultUnderlying(_vault), income);
}
function unload(
address _vault,
address _pair,
uint _amount
) external override vaultOrOwner {
_validatePair(_vault, _pair);
_pairWithdrawWithIncome(_vault, _pair, _amount);
TransferHelper.safeTransfer(_vaultUnderlying(_vault), _vault, _amount);
distributeIncome(_vault);
}
function distributeIncome(address _vault) public override {
IERC20 underlying = IERC20(_vaultUnderlying(_vault));
uint income = underlying.balanceOf(address(this));
if (income > 0) {
underlying.approve(_vault, income);
IVault(_vault).addIncome(income);
emit FeeDistribution(income);
}
}
function setCallIncentive(uint _value) external onlyOwner {
callIncentive = _value;
emit NewCallIncentive(_value);
}
function rescueToken(address _token, uint _amount) external onlyOwner {
TransferHelper.safeTransfer(_token, msg.sender, _amount);
}
function initPairDeposits(address _underlying, address[] memory _pairs, uint[] memory _amounts) external onlyOwner {
for (uint i = 0; i < _pairs.length; i++) {
require(pairDeposits[_pairs[i]][_underlying] == 0, "VaultRebalancer: already initialized");
pairDeposits[_pairs[i]][_underlying] = _amounts[i];
}
}
function _pairWithdrawWithIncome(
address _vault,
address _pair,
uint _amount
) internal returns(uint) {
address underlying = _vaultUnderlying(_vault);
ILendingPair pair = ILendingPair(_pair);
_ensureDepositRecord(_vault, underlying, _pair);
uint income = _balanceWithPendingInterest(_vault, underlying, _pair) - pairDeposits[_pair][underlying];
uint transferAmount = _amount + income;
if (transferAmount > 0) {
pair.accrueAccount(_vault);
IVault(_vault).pushToken(address(pair.lpToken(underlying)), transferAmount);
pair.withdraw(underlying, transferAmount);
pairDeposits[_pair][underlying] = _balanceWithPendingInterest(_vault, underlying, _pair);
}
return income;
}
function _ensureDepositRecord(
address _vault,
address _underlying,
address _pair
) internal {
if (pairDeposits[_pair][_underlying] == 0) {
pairDeposits[_pair][_underlying] = _balanceWithPendingInterest(_vault, _underlying, _pair);
}
}
function _sendIncentiveWithLimit(address _underlying, uint _fromAmount) internal returns(uint) {
uint callerIncentive = Math.min(_fromAmount * callIncentive / 100e18, MAX_INCENTIVE);
IERC20(_underlying).transfer(msg.sender, callerIncentive);
return callerIncentive;
}
function _pairDeposit(
address _vault,
address _underlying,
address _pair,
uint _amount
) internal {
IERC20(_underlying).approve(_pair, _amount);
ILendingPair(_pair).deposit(_vault, _underlying, _amount);
pairDeposits[_pair][_underlying] = _balanceWithPendingInterest(_vault, _underlying, _pair);
}
function _balanceWithPendingInterest(
address _vault,
address _underlying,
address _pair
) internal view returns(uint) {
ILendingPair pair = ILendingPair(_pair);
uint balance = pair.supplyBalance(_vault, _underlying, _underlying);
uint pending = pair.pendingSupplyInterest(_underlying, _vault);
return balance + pending;
}
function _lpBalance(
address _pair,
address _underlying
) internal view returns(uint) {
return IERC20(ILendingPair(_pair).lpToken(_underlying)).balanceOf(address(this));
}
function _validatePair(address _vault, address _pair) internal view {
ILendingPair pair = ILendingPair(_pair);
address underlying = _vaultUnderlying(_vault);
require(
pair.tokenA() == underlying || pair.tokenB() == underlying,
"VaultRebalancer: invalid underlying"
);
require(
_pair == IPairFactory(pairFactory).pairByTokens(pair.tokenA(), pair.tokenB()),
"VaultRebalancer: invalid lending pair"
);
}
function _requireContract(address _value) internal view {
require(_value.isContract(), "VaultRebalancer: must be a contract");
}
function _vaultUnderlying(address _vault) internal view returns(address) {
return IVault(_vault).underlying();
}
}