// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
/**
* @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) {
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 Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (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.
*/
function transfer(address recipient, uint256 amount) external returns (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.
*/
function allowance(address owner, address spender) external view returns (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.
*/
function approve(address spender, uint256 amount) external returns (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.
*/
function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed 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.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
}
/*
* Here we have a list of constants. In order to get access to an address
* managed by ValueVaultMaster, the calling contract should copy and define
* some of these constants and use them as keys.
* Keys themselves are immutable. Addresses can be immutable or mutable.
*
* Vault addresses are immutable once set, and the list may grow:
* K_VAULT_WETH = 0;
* K_VAULT_ETH_USDC_UNI_V2_LP = 1;
* K_VAULT_ETH_WBTC_UNI_V2_LP = 2;
*
* Strategy addresses are mutable:
* K_STRATEGY_WETH_SODA_POOL = 0;
* K_STRATEGY_WETH_MULTI_POOL = 1;
* K_STRATEGY_ETHUSDC_MULTIPOOL = 100;
* K_STRATEGY_ETHWBTC_MULTIPOOL = 200;
*/
/*
* ValueVaultMaster manages all the vaults and strategies of our Value Vaults system.
*/
contract ValueVaultMaster {
address public governance;
address public bank;
address public minorPool;
address public profitSharer;
address public govToken; // VALUE
address public yfv; // When harvesting, convert some parts to YFV for govVault
address public usdc; // we only used USDC to estimate APY
address public govVault; // YFV -> VALUE, vUSD, vETH and 6.7% profit from Value Vaults
address public insuranceFund = 0xb7b2Ea8A1198368f950834875047aA7294A2bDAa; // set to Governance Multisig at start
address public performanceReward = 0x7Be4D5A99c903C437EC77A20CB6d0688cBB73c7f; // set to deploy wallet at start
uint256 public constant FEE_DENOMINATOR = 10000;
uint256 public govVaultProfitShareFee = 670; // 6.7% | VIP-1 (https://yfv.finance/vip-vote/vip_1)
uint256 public gasFee = 50; // 0.5% at start and can be set by governance decision
uint256 public minStakeTimeToClaimVaultReward = 24 hours;
mapping(address => bool) public isVault;
mapping(uint256 => address) public vaultByKey;
mapping(address => bool) public isStrategy;
mapping(uint256 => address) public strategyByKey;
mapping(address => uint256) public strategyQuota;
constructor(address _govToken, address _yfv, address _usdc) public {
govToken = _govToken;
yfv = _yfv;
usdc = _usdc;
governance = tx.origin;
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
// Immutable once set.
function setBank(address _bank) external {
require(msg.sender == governance, "!governance");
require(bank == address(0));
bank = _bank;
}
// Mutable in case we want to upgrade the pool.
function setMinorPool(address _minorPool) external {
require(msg.sender == governance, "!governance");
minorPool = _minorPool;
}
// Mutable in case we want to upgrade this module.
function setProfitSharer(address _profitSharer) external {
require(msg.sender == governance, "!governance");
profitSharer = _profitSharer;
}
// Mutable, in case governance want to upgrade VALUE to new version
function setGovToken(address _govToken) external {
require(msg.sender == governance, "!governance");
govToken = _govToken;
}
// Immutable once added, and you can always add more.
function addVault(uint256 _key, address _vault) external {
require(msg.sender == governance, "!governance");
require(vaultByKey[_key] == address(0), "vault: key is taken");
isVault[_vault] = true;
vaultByKey[_key] = _vault;
}
// Mutable and removable.
function addStrategy(uint256 _key, address _strategy) external {
require(msg.sender == governance, "!governance");
isStrategy[_strategy] = true;
strategyByKey[_key] = _strategy;
}
// Set 0 to disable quota (no limit)
function setStrategyQuota(address _strategy, uint256 _quota) external {
require(msg.sender == governance, "!governance");
strategyQuota[_strategy] = _quota;
}
function removeStrategy(uint256 _key) external {
require(msg.sender == governance, "!governance");
isStrategy[strategyByKey[_key]] = false;
delete strategyByKey[_key];
}
function setGovVault(address _govVault) public {
require(msg.sender == governance, "!governance");
govVault = _govVault;
}
function setInsuranceFund(address _insuranceFund) public {
require(msg.sender == governance, "!governance");
insuranceFund = _insuranceFund;
}
function setPerformanceReward(address _performanceReward) public{
require(msg.sender == governance, "!governance");
performanceReward = _performanceReward;
}
function setGovVaultProfitShareFee(uint256 _govVaultProfitShareFee) public {
require(msg.sender == governance, "!governance");
govVaultProfitShareFee = _govVaultProfitShareFee;
}
function setGasFee(uint256 _gasFee) public {
require(msg.sender == governance, "!governance");
gasFee = _gasFee;
}
function setMinStakeTimeToClaimVaultReward(uint256 _minStakeTimeToClaimVaultReward) public {
require(msg.sender == governance, "!governance");
minStakeTimeToClaimVaultReward = _minStakeTimeToClaimVaultReward;
}
/**
* This function allows governance to take unsupported tokens out of the contract.
* This is in an effort to make someone whole, should they seriously mess up.
* There is no guarantee governance will vote to return these.
* It also allows for removal of airdropped tokens.
*/
function governanceRecoverUnsupported(IERC20x _token, uint256 amount, address to) external {
require(msg.sender == governance, "!governance");
_token.transfer(to, amount);
}
}
interface IERC20x {
function transfer(address recipient, uint256 amount) external returns (bool);
}
interface IStrategyV2p1 {
function approve(IERC20 _token) external;
function approveForSpender(IERC20 _token, address spender) external;
// Deposit tokens to a farm to yield more tokens.
function deposit(address _vault, uint256 _amount) external; // IStrategy
function deposit(uint256 _poolId, uint256 _amount) external; // IStrategyV2
// Claim farming tokens
function claim(address _vault) external; // IStrategy
function claim(uint256 _poolId) external; // IStrategyV2
// The vault request to harvest the profit
function harvest(uint256 _bankPoolId) external; // IStrategy
function harvest(uint256 _bankPoolId, uint256 _poolId) external; // IStrategyV2
// Withdraw the principal from a farm.
function withdraw(address _vault, uint256 _amount) external; // IStrategy
function withdraw(uint256 _poolId, uint256 _amount) external; // IStrategyV2
// Set 0 to disable quota (no limit)
function poolQuota(uint256 _poolId) external view returns (uint256);
// Use when we want to switch between strategies
function forwardToAnotherStrategy(address _dest, uint256 _amount) external returns (uint256);
function switchBetweenPoolsByGov(uint256 _sourcePoolId, uint256 _destPoolId, uint256 _amount) external; // IStrategyV2p1
// Source LP token of this strategy
function getLpToken() external view returns(address);
// Target farming token of this strategy.
function getTargetToken(address _vault) external view returns(address); // IStrategy
function getTargetToken(uint256 _poolId) external view returns(address); // IStrategyV2
function balanceOf(address _vault) external view returns (uint256); // IStrategy
function balanceOf(uint256 _poolId) external view returns (uint256); // IStrategyV2
function pendingReward(address _vault) external view returns (uint256); // IStrategy
function pendingReward(uint256 _poolId) external view returns (uint256); // IStrategyV2
function expectedAPY(address _vault) external view returns (uint256); // IStrategy
function expectedAPY(uint256 _poolId, uint256 _lpTokenUsdcPrice) external view returns (uint256); // IStrategyV2
function governanceRescueToken(IERC20 _token) external returns (uint256);
}
interface IOneSplit {
function getExpectedReturn(
IERC20 fromToken,
IERC20 destToken,
uint256 amount,
uint256 parts,
uint256 flags // See constants in IOneSplit.sol
) external view returns(
uint256 returnAmount,
uint256[] memory distribution
);
}
interface IUniswapRouter {
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
}
interface IValueLiquidPool {
function swapExactAmountIn(address, uint, address, uint, uint) external returns (uint, uint);
function swapExactAmountOut(address, uint, address, uint, uint) external returns (uint, uint);
function calcInGivenOut(uint, uint, uint, uint, uint, uint) external pure returns (uint);
function calcOutGivenIn(uint, uint, uint, uint, uint, uint) external pure returns (uint);
function getDenormalizedWeight(address) external view returns (uint);
function getBalance(address) external view returns (uint);
function swapFee() external view returns (uint);
}
interface IStakingRewards {
// Views
function lastTimeRewardApplicable() external view returns (uint256);
function rewardPerToken() external view returns (uint256);
function rewardRate() external view returns (uint256);
function earned(address account) external view returns (uint256);
function getRewardForDuration() external view returns (uint256);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
// Mutative
function stake(uint256 amount) external; // Goff, DokiDoki
function stake(uint256 amount, string memory affCode) external; // dego.finance
function withdraw(uint256 amount) external;
function getReward() external;
function exit() external;
// DokiDoki
function getRewardsAmount(address account) external view returns(uint256);
function claim(uint amount) external;
}
interface ISushiPool {
function deposit(uint256 _poolId, uint256 _amount) external;
function claim(uint256 _poolId) external;
function withdraw(uint256 _poolId, uint256 _amount) external;
function emergencyWithdraw(uint256 _poolId) external;
}
interface IProfitSharer {
function shareProfit() external returns (uint256);
}
interface IValueVaultBank {
function make_profit(uint256 _poolId, uint256 _amount) external;
}
// This contract is owned by Timelock.
// What it does is simple: deposit WETH to pools, and wait for ValueVaultBank's command.
// Atm we support 3 pool types: IStakingRewards (golff.finance), ISushiPool (chickenswap.org), ISodaPool (soda.finance)
contract WETHMultiPoolStrategy is IStrategyV2p1 {
using SafeMath for uint256;
address public strategist;
address public governance;
uint256 public constant FEE_DENOMINATOR = 10000;
IOneSplit public onesplit = IOneSplit(0x50FDA034C0Ce7a8f7EFDAebDA7Aa7cA21CC1267e);
IUniswapRouter public unirouter = IUniswapRouter(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
ValueVaultMaster public valueVaultMaster;
IERC20 public lpToken; // WETH
mapping(address => mapping(address => address[])) public uniswapPaths; // [input -> output] => uniswap_path
mapping(address => mapping(address => address)) public liquidPools; // [input -> output] => value_liquid_pool (valueliquid.io)
struct PoolInfo {
address vault;
IERC20 targetToken;
address targetPool;
uint256 targetPoolId; // poolId in soda/chicken pool (no use for IStakingRewards pool eg. golff.finance)
uint256 minHarvestForTakeProfit;
uint8 poolType; // 0: IStakingRewards, 1: ISushiPool, 2: ISodaPool, 3: Dego.Finance, 4: DokiDoki.Finance
uint256 poolQuota; // set 0 to disable quota (no limit)
uint256 balance;
}
mapping(uint256 => PoolInfo) public poolMap; // poolIndex -> poolInfo
uint256 public totalBalance;
bool public aggressiveMode; // will try to stake all lpTokens available (be forwarded from bank or from another strategies)
uint8[] public poolPreferredIds; // sorted by preference
// weth = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
constructor(ValueVaultMaster _valueVaultMaster,
IERC20 _lpToken,
bool _aggressiveMode) public {
valueVaultMaster = _valueVaultMaster;
lpToken = _lpToken;
aggressiveMode = _aggressiveMode;
governance = tx.origin;
strategist = tx.origin;
// Approve all
lpToken.approve(valueVaultMaster.bank(), type(uint256).max);
lpToken.approve(address(unirouter), type(uint256).max);
}
// [0] targetToken: kfcToken = 0xE63684BcF2987892CEfB4caA79BD21b34e98A291
// targetPool: kfcPool = 0x87AE4928f6582376a0489E9f70750334BBC2eb35
// [1] targetToken: degoToken = 0x88EF27e69108B2633F8E1C184CC37940A075cC02
// targetPool: degoPool = 0x28681d373aF03A0Eb00ACE262c5dad9A0C65F276
function setPoolInfo(uint256 _poolId, address _vault, IERC20 _targetToken, address _targetPool, uint256 _targetPoolId, uint256 _minHarvestForTakeProfit, uint8 _poolType, uint256 _poolQuota) external {
require(msg.sender == governance, "!governance");
poolMap[_poolId].vault = _vault;
poolMap[_poolId].targetToken = _targetToken;
poolMap[_poolId].targetPool = _targetPool;
poolMap[_poolId].targetPoolId = _targetPoolId;
poolMap[_poolId].minHarvestForTakeProfit = _minHarvestForTakeProfit;
poolMap[_poolId].poolType = _poolType;
poolMap[_poolId].poolQuota = _poolQuota;
_targetToken.approve(address(unirouter), type(uint256).max);
lpToken.approve(_vault, type(uint256).max);
lpToken.approve(address(_targetPool), type(uint256).max);
}
function approve(IERC20 _token) external override {
require(msg.sender == governance, "!governance");
_token.approve(valueVaultMaster.bank(), type(uint256).max);
_token.approve(address(unirouter), type(uint256).max);
}
function approveForSpender(IERC20 _token, address spender) external override {
require(msg.sender == governance, "!governance");
_token.approve(spender, type(uint256).max);
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setStrategist(address _strategist) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
strategist = _strategist;
}
function setPoolPreferredIds(uint8[] memory _poolPreferredIds) public {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
delete poolPreferredIds;
for (uint8 i = 0; i < _poolPreferredIds.length; ++i) {
poolPreferredIds.push(_poolPreferredIds[i]);
}
}
function setMinHarvestForTakeProfit(uint256 _poolId, uint256 _minHarvestForTakeProfit) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
poolMap[_poolId].minHarvestForTakeProfit = _minHarvestForTakeProfit;
}
function setPoolQuota(uint256 _poolId, uint256 _poolQuota) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
poolMap[_poolId].poolQuota = _poolQuota;
}
// Sometime the balance could be slightly changed (due to the pool, or because we call xxxByGov methods)
function setPoolBalance(uint256 _poolId, uint256 _balance) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
poolMap[_poolId].balance = _balance;
}
function setTotalBalance(uint256 _totalBalance) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
totalBalance = _totalBalance;
}
function setAggressiveMode(bool _aggressiveMode) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
aggressiveMode = _aggressiveMode;
}
function setOnesplit(IOneSplit _onesplit) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
onesplit = _onesplit;
}
function setUnirouter(IUniswapRouter _unirouter) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
unirouter = _unirouter;
}
/**
* @dev See {IStrategy-deposit}.
*/
function deposit(address _vault, uint256 _amount) public override {
require(valueVaultMaster.isVault(msg.sender), "sender not vault");
require(poolPreferredIds.length > 0, "no pool");
for (uint256 i = 0; i < poolPreferredIds.length; ++i) {
uint256 _pid = poolPreferredIds[i];
if (poolMap[_pid].vault == _vault) {
uint256 _quota = poolMap[_pid].poolQuota;
if (_quota == 0 || balanceOf(_pid) < _quota) {
_deposit(_pid, _amount);
return;
}
}
}
revert("Exceeded pool quota");
}
/**
* @dev See {IStrategyV2-deposit}.
*/
function deposit(uint256 _poolId, uint256 _amount) public override {
require(poolMap[_poolId].vault == msg.sender, "sender not vault");
_deposit(_poolId, _amount);
}
function _deposit(uint256 _poolId, uint256 _amount) internal {
PoolInfo storage pool = poolMap[_poolId];
if (aggressiveMode) {
_amount = lpToken.balanceOf(address(this));
}
if (pool.poolType == 0 || pool.poolType == 4) {
IStakingRewards(pool.targetPool).stake(_amount);
} else if (pool.poolType == 3) {
IStakingRewards(pool.targetPool).stake(_amount, string("valuedefidev02"));
} else {
ISushiPool(pool.targetPool).deposit(pool.targetPoolId, _amount);
}
pool.balance = pool.balance.add(_amount);
totalBalance = totalBalance.add(_amount);
}
/**
* @dev See {IStrategy-claim}.
*/
function claim(address _vault) external override {
require(valueVaultMaster.isVault(_vault), "not vault");
require(poolPreferredIds.length > 0, "no pool");
for (uint256 i = 0; i < poolPreferredIds.length; ++i) {
uint256 _pid = poolPreferredIds[i];
if (poolMap[_pid].vault == _vault) {
_claim(_pid);
}
}
}
/**
* @dev See {IStrategyV2-claim}.
*/
function claim(uint256 _poolId) external override {
require(poolMap[_poolId].vault == msg.sender, "sender not vault");
_claim(_poolId);
}
function _claim(uint256 _poolId) internal {
PoolInfo storage pool = poolMap[_poolId];
if (pool.poolType == 0 || pool.poolType == 3) {
IStakingRewards(pool.targetPool).getReward();
} else if (pool.poolType == 4) {
IStakingRewards(pool.targetPool).claim(IStakingRewards(pool.targetPool).getRewardsAmount(address(this)));
} else if (pool.poolType == 1) {
ISushiPool(pool.targetPool).deposit(pool.targetPoolId, 0);
} else {
ISushiPool(pool.targetPool).claim(pool.targetPoolId);
}
}
/**
* @dev See {IStrategy-withdraw}.
*/
function withdraw(address _vault, uint256 _amount) external override {
require(valueVaultMaster.isVault(msg.sender), "sender not vault");
require(poolPreferredIds.length > 0, "no pool");
for (uint256 i = poolPreferredIds.length; i >= 1; --i) {
uint256 _pid = poolPreferredIds[i - 1];
if (poolMap[_pid].vault == _vault) {
uint256 _bal = poolMap[_pid].balance;
if (_bal > 0) {
_withdraw(_pid, (_bal > _amount) ? _amount : _bal);
uint256 strategyBal = lpToken.balanceOf(address(this));
lpToken.transfer(valueVaultMaster.bank(), strategyBal);
if (strategyBal >= _amount) break;
if (strategyBal > 0) _amount = _amount - strategyBal;
}
}
}
}
/**
* @dev See {IStrategyV2-withdraw}.
*/
function withdraw(uint256 _poolId, uint256 _amount) external override {
require(poolMap[_poolId].vault == msg.sender, "sender not vault");
if (lpToken.balanceOf(address(this)) >= _amount) return; // has enough balance, no need to withdraw from pool
_withdraw(_poolId, _amount);
}
function _withdraw(uint256 _poolId, uint256 _amount) internal {
PoolInfo storage pool = poolMap[_poolId];
if (pool.poolType == 0 || pool.poolType == 3 || pool.poolType == 4) {
IStakingRewards(pool.targetPool).withdraw(_amount);
} else {
ISushiPool(pool.targetPool).withdraw(pool.targetPoolId, _amount);
}
if (pool.balance < _amount) {
_amount = pool.balance;
}
pool.balance = pool.balance - _amount;
if (totalBalance >= _amount) totalBalance = totalBalance - _amount;
}
function depositByGov(address pool, uint8 _poolType, uint256 _targetPoolId, uint256 _amount) external {
require(msg.sender == governance, "!governance");
if (_poolType == 0 || _poolType == 4) {
IStakingRewards(pool).stake(_amount);
} else if (_poolType == 3) {
IStakingRewards(pool).stake(_amount, string("valuedefidev02"));
} else {
ISushiPool(pool).deposit(_targetPoolId, _amount);
}
}
function claimByGov(address pool, uint8 _poolType, uint256 _targetPoolId) external {
require(msg.sender == governance, "!governance");
if (_poolType == 0 || _poolType == 3) {
IStakingRewards(pool).getReward();
} else if (_poolType == 4) {
IStakingRewards(pool).claim(IStakingRewards(pool).getRewardsAmount(address(this)));
} else if (_poolType == 1) {
ISushiPool(pool).deposit(_targetPoolId, 0);
} else {
ISushiPool(pool).claim(_targetPoolId);
}
}
function withdrawByGov(address pool, uint8 _poolType, uint256 _targetPoolId, uint256 _amount) external {
require(msg.sender == governance, "!governance");
if (_poolType == 0 || _poolType == 3 || _poolType == 4) {
IStakingRewards(pool).withdraw(_amount);
} else {
ISushiPool(pool).withdraw(_targetPoolId, _amount);
}
}
function emergencyWithdrawByGov(address pool, uint256 _targetPoolId) external {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
ISushiPool(pool).emergencyWithdraw(_targetPoolId);
}
function switchBetweenPoolsByGov(uint256 _sourcePoolId, uint256 _destPoolId, uint256 _amount) external override {
require(msg.sender == governance, "!governance");
_withdraw(_sourcePoolId, _amount);
_deposit(_destPoolId, _amount);
}
/**
* @dev See {IStrategyV2-poolQuota}
*/
function poolQuota(uint256 _poolId) external override view returns (uint256) {
return poolMap[_poolId].poolQuota;
}
/**
* @dev See {IStrategyV2-forwardToAnotherStrategy}
*/
function forwardToAnotherStrategy(address _dest, uint256 _amount) external override returns (uint256 sent) {
require(valueVaultMaster.isVault(msg.sender), "not vault");
require(valueVaultMaster.isStrategy(_dest), "not strategy");
require(IStrategyV2p1(_dest).getLpToken() == address(lpToken), "!lpToken");
uint256 lpTokenBal = lpToken.balanceOf(address(this));
sent = (_amount < lpTokenBal) ? _amount : lpTokenBal;
lpToken.transfer(_dest, sent);
}
function setUnirouterPath(address _input, address _output, address [] memory _path) public {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
uniswapPaths[_input][_output] = _path;
}
function setLiquidPool(address _input, address _output, address _pool) public {
require(msg.sender == governance || msg.sender == strategist, "!governance && !strategist");
liquidPools[_input][_output] = _pool;
IERC20(_input).approve(_pool, type(uint256).max);
}
function _swapTokens(address _input, address _output, uint256 _amount) internal {
address _pool = liquidPools[_input][_output];
if (_pool != address(0)) { // use ValueLiquid
// swapExactAmountIn(tokenIn, tokenAmountIn, tokenOut, minAmountOut, maxPrice)
IValueLiquidPool(_pool).swapExactAmountIn(_input, _amount, _output, 1, type(uint256).max);
} else { // use Uniswap
address[] memory path = uniswapPaths[_input][_output];
if (path.length == 0) {
// path: _input -> _output
path = new address[](2);
path[0] = _input;
path[1] = _output;
}
// swapExactTokensForTokens(amountIn, amountOutMin, path, to, deadline)
unirouter.swapExactTokensForTokens(_amount, 1, path, address(this), now.add(1800));
}
}
/**
* @dev See {IStrategy-harvest}.
*/
function harvest(uint256 _bankPoolId) external override {
address _vault = msg.sender;
require(valueVaultMaster.isVault(_vault), "!vault"); // additional protection so we don't burn the funds
require(poolPreferredIds.length > 0, "no pool");
for (uint256 i = 0; i < poolPreferredIds.length; ++i) {
uint256 _pid = poolPreferredIds[i];
if (poolMap[_pid].vault == _vault) {
_harvest(_bankPoolId, _pid);
}
}
}
/**
* @dev See {IStrategy-harvest}.
*/
function harvest(uint256 _bankPoolId, uint256 _poolId) external override {
require(valueVaultMaster.isVault(msg.sender), "!vault"); // additional protection so we don't burn the funds
_harvest(_bankPoolId, _poolId);
}
function _harvest(uint256 _bankPoolId, uint256 _poolId) internal {
PoolInfo storage pool = poolMap[_poolId];
_claim(_poolId);
IERC20 targetToken = pool.targetToken;
uint256 targetTokenBal = targetToken.balanceOf(address(this));
if (targetTokenBal < pool.minHarvestForTakeProfit) return;
_swapTokens(address(targetToken), address(lpToken), targetTokenBal);
uint256 wethBal = lpToken.balanceOf(address(this));
if (wethBal > 0) {
address profitSharer = valueVaultMaster.profitSharer();
address performanceReward = valueVaultMaster.performanceReward();
address bank = valueVaultMaster.bank();
if (valueVaultMaster.govVaultProfitShareFee() > 0 && profitSharer != address(0)) {
address _govToken = valueVaultMaster.yfv();
uint256 _govVaultProfitShareFee = wethBal.mul(valueVaultMaster.govVaultProfitShareFee()).div(FEE_DENOMINATOR);
_swapTokens(address(lpToken), _govToken, _govVaultProfitShareFee);
IERC20(_govToken).transfer(profitSharer, IERC20(_govToken).balanceOf(address(this)));
IProfitSharer(profitSharer).shareProfit();
}
if (valueVaultMaster.gasFee() > 0 && performanceReward != address(0)) {
uint256 _gasFee = wethBal.mul(valueVaultMaster.gasFee()).div(FEE_DENOMINATOR);
lpToken.transfer(performanceReward, _gasFee);
}
uint256 balanceLeft = lpToken.balanceOf(address(this));
if (lpToken.allowance(address(this), bank) < balanceLeft) {
lpToken.approve(bank, 0);
lpToken.approve(bank, balanceLeft);
}
IValueVaultBank(bank).make_profit(_bankPoolId, balanceLeft);
}
}
/**
* @dev See {IStrategyV2-getLpToken}.
*/
function getLpToken() external view override returns(address) {
return address(lpToken);
}
/**
* @dev See {IStrategy-getTargetToken}.
* Always use pool 0 (default).
*/
function getTargetToken(address) external override view returns(address) {
return address(poolMap[0].targetToken);
}
/**
* @dev See {IStrategyV2-getTargetToken}.
*/
function getTargetToken(uint256 _poolId) external override view returns(address) {
return address(poolMap[_poolId].targetToken);
}
function balanceOf(address _vault) public override view returns (uint256 _balanceOfVault) {
_balanceOfVault = 0;
for (uint256 i = 0; i < poolPreferredIds.length; ++i) {
uint256 _pid = poolPreferredIds[i];
if (poolMap[_pid].vault == _vault) {
_balanceOfVault = _balanceOfVault.add(poolMap[_pid].balance);
}
}
}
function balanceOf(uint256 _poolId) public override view returns (uint256) {
return poolMap[_poolId].balance;
}
function pendingReward(address) public override view returns (uint256) {
return pendingReward(0);
}
// Only support IStakingRewards pool
function pendingReward(uint256 _poolId) public override view returns (uint256) {
if (poolMap[_poolId].poolType != 0) return 0; // do not support other pool types
return IStakingRewards(poolMap[_poolId].targetPool).earned(address(this));
}
// always use pool 0 (default)
function expectedAPY(address) public override view returns (uint256) {
return expectedAPY(0, 0);
}
// Helper function - should never use it on-chain.
// Only support IStakingRewards pool.
// Return 10000x of APY. _lpTokenUsdcPrice is not used.
function expectedAPY(uint256 _poolId, uint256) public override view returns (uint256) {
if (poolMap[_poolId].poolType != 0) return 0; // do not support other pool types
IStakingRewards targetPool = IStakingRewards(poolMap[_poolId].targetPool);
uint256 totalSupply = targetPool.totalSupply();
if (totalSupply == 0) return 0;
uint256 investAmt = poolMap[_poolId].balance;
uint256 oneHourReward = targetPool.rewardRate().mul(3600);
uint256 returnAmt = oneHourReward.mul(investAmt).div(totalSupply);
IERC20 usdc = IERC20(valueVaultMaster.usdc());
(uint256 investInUSDC, ) = onesplit.getExpectedReturn(lpToken, usdc, investAmt, 10, 0);
(uint256 returnInUSDC, ) = onesplit.getExpectedReturn(poolMap[_poolId].targetToken, usdc, returnAmt, 10, 0);
return returnInUSDC.mul(8760).mul(FEE_DENOMINATOR).div(investInUSDC); // 100 -> 1%
}
event ExecuteTransaction(address indexed target, uint value, string signature, bytes data);
/**
* @dev This is from Timelock contract, the governance should be a Timelock contract before calling this
*/
function executeTransaction(address target, uint value, string memory signature, bytes memory data) public returns (bytes memory) {
require(msg.sender == governance, "!governance");
bytes memory callData;
if (bytes(signature).length == 0) {
callData = data;
} else {
callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data);
}
// solium-disable-next-line security/no-call-value
(bool success, bytes memory returnData) = target.call{value: value}(callData);
require(success, "WETHMultiPoolStrategy::executeTransaction: Transaction execution reverted.");
emit ExecuteTransaction(target, value, signature, data);
return returnData;
}
/**
* @dev if there is any token stuck we will need governance support to rescue the fund
*/
function governanceRescueToken(IERC20 _token) external override returns (uint256 balance) {
address bank = valueVaultMaster.bank();
require(bank == msg.sender, "sender not bank");
balance = _token.balanceOf(address(this));
_token.transfer(bank, balance);
}
}
{
"compilationTarget": {
"WETHMultiPoolStrategy.sol": "WETHMultiPoolStrategy"
},
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": []
}
[{"inputs":[{"internalType":"contract ValueVaultMaster","name":"_valueVaultMaster","type":"address"},{"internalType":"contract IERC20","name":"_lpToken","type":"address"},{"internalType":"bool","name":"_aggressiveMode","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"string","name":"signature","type":"string"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"ExecuteTransaction","type":"event"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aggressiveMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"approveForSpender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"_balanceOfVault","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"claim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint8","name":"_poolType","type":"uint8"},{"internalType":"uint256","name":"_targetPoolId","type":"uint256"}],"name":"claimByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint8","name":"_poolType","type":"uint8"},{"internalType":"uint256","name":"_targetPoolId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"depositByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint256","name":"_targetPoolId","type":"uint256"}],"name":"emergencyWithdrawByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"string","name":"signature","type":"string"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"executeTransaction","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"expectedAPY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"expectedAPY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_dest","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"forwardToAnotherStrategy","outputs":[{"internalType":"uint256","name":"sent","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getLpToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"getTargetToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"getTargetToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governance","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"_token","type":"address"}],"name":"governanceRescueToken","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bankPoolId","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bankPoolId","type":"uint256"},{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"harvest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"liquidPools","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lpToken","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"onesplit","outputs":[{"internalType":"contract IOneSplit","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"pendingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"pendingReward","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolMap","outputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"contract IERC20","name":"targetToken","type":"address"},{"internalType":"address","name":"targetPool","type":"address"},{"internalType":"uint256","name":"targetPoolId","type":"uint256"},{"internalType":"uint256","name":"minHarvestForTakeProfit","type":"uint256"},{"internalType":"uint8","name":"poolType","type":"uint8"},{"internalType":"uint256","name":"poolQuota","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"poolPreferredIds","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"}],"name":"poolQuota","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_aggressiveMode","type":"bool"}],"name":"setAggressiveMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_governance","type":"address"}],"name":"setGovernance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"address","name":"_pool","type":"address"}],"name":"setLiquidPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"_minHarvestForTakeProfit","type":"uint256"}],"name":"setMinHarvestForTakeProfit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IOneSplit","name":"_onesplit","type":"address"}],"name":"setOnesplit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"_balance","type":"uint256"}],"name":"setPoolBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"address","name":"_vault","type":"address"},{"internalType":"contract IERC20","name":"_targetToken","type":"address"},{"internalType":"address","name":"_targetPool","type":"address"},{"internalType":"uint256","name":"_targetPoolId","type":"uint256"},{"internalType":"uint256","name":"_minHarvestForTakeProfit","type":"uint256"},{"internalType":"uint8","name":"_poolType","type":"uint8"},{"internalType":"uint256","name":"_poolQuota","type":"uint256"}],"name":"setPoolInfo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"_poolPreferredIds","type":"uint8[]"}],"name":"setPoolPreferredIds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"_poolQuota","type":"uint256"}],"name":"setPoolQuota","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_strategist","type":"address"}],"name":"setStrategist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_totalBalance","type":"uint256"}],"name":"setTotalBalance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IUniswapRouter","name":"_unirouter","type":"address"}],"name":"setUnirouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_input","type":"address"},{"internalType":"address","name":"_output","type":"address"},{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"setUnirouterPath","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"strategist","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_sourcePoolId","type":"uint256"},{"internalType":"uint256","name":"_destPoolId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"switchBetweenPoolsByGov","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unirouter","outputs":[{"internalType":"contract IUniswapRouter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"uniswapPaths","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"valueVaultMaster","outputs":[{"internalType":"contract ValueVaultMaster","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_vault","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint8","name":"_poolType","type":"uint8"},{"internalType":"uint256","name":"_targetPoolId","type":"uint256"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawByGov","outputs":[],"stateMutability":"nonpayable","type":"function"}]