From cc93e66134f210ca34c30b380f63b204f38db377 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 11:58:50 +0100 Subject: [PATCH 01/13] genericproxy --- contracts/GenericVaultProxy.sol | 108 ++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 contracts/GenericVaultProxy.sol diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol new file mode 100644 index 0000000..635bce7 --- /dev/null +++ b/contracts/GenericVaultProxy.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import { + SafeERC20, + IERC20, + Address +} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +interface IVault { + function deposit() external; + + function withdraw() external; + function withdraw(uint256 amount) external; +} + +interface IAny { + function Swapout(uint256 amount, address bindaddr) external; +} + +contract GenericVaultProxy { + using SafeERC20 for IERC20; + using Address for address; + + IVault public vault; + address public want; + + address public strategist; + address public keeper; + address public governance; + address public pendingGovernance; + + constructor(address _keeper, address _gov, address _vault) public { + vault = IVault(_vault); + want = vault.token(); + keeper = _keeper; + governance = _gov; + strategist = msg.sender; + IERC20(want).safeApprove(address(vault), type(uint256).max); + } + + modifier onlyGov { + require(msg.sender == governance); + _; + } + + modifier onlyGuardians { + require( + msg.sender == strategist || + msg.sender == keeper || + msg.sender == governance + ); + _; + } + + function name() external view returns (string memory) { + return "BridgeVaultProxy"; + } + + function _wantBalance() internal view returns (uint256){ + return IERC20(want).balanceOf(address(this)); + } + function _vaultBalance() internal view returns (uint256){ + return vault.balanceOf(address(this)).mul(vault.pricePerShare()).div(10 ** vault.decimals()); + } + + function acceptGovernor() external { + require(msg.sender == pendingGovernance); + governance = pendingGovernance; + pendingGovernance = address(0); + } + + function setPendingGovernance(address _pendingGovernance) external onlyGov { + pendingGovernance = _pendingGovernance; + } + + function totalAssets() public view returns (uint256) { + return _vaultBalance().add(_wantBalance()); + } + + function deposit() external onlyGuardians { + if (_wantBalance() > 0) { + vault.deposit(); + } + } + + function sendAllBack() external onlyGuardians { + vault.withdraw(); + IAny(want).Swapout(_wantBalance(), address(this)); + } + + function sendWantBack(uint256 amount) external onlyGuardians { + uint256 wantBal = _wantBalance(); + if(wantBal < amount){ + uint256 toWithdraw = amount.sub(wantBal); + vault.withdraw(toWithdraw.mul(10 ** vault.decimals()).div(vault.pricePerShare())); + } + + IAny(want).Swapout(Math.min(_wantBalance(), amount), address(this)); + } + + //sweep function in case bridge breaks and we are trapped + function sweep(address token, uint256 amount) external onlyGov { + IERC20(token).safeTransfer(governance, amount); + } + +} \ No newline at end of file From 7e120215b5b8493281e1973949871bab71c421c0 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 12:24:34 +0100 Subject: [PATCH 02/13] code finished --- .gitignore | 2 + brownie-config.yml | 4 +- contracts/EthStrategy.sol | 107 ------------------------ contracts/GenericEthStrategy.sol | 139 +++++++++++++++++++++++++++++++ contracts/GenericVaultProxy.sol | 7 ++ contracts/WethToBscStrategy.sol | 123 --------------------------- 6 files changed, 150 insertions(+), 232 deletions(-) delete mode 100644 contracts/EthStrategy.sol create mode 100644 contracts/GenericEthStrategy.sol delete mode 100644 contracts/WethToBscStrategy.sol diff --git a/.gitignore b/.gitignore index 0ff2835..2c71213 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ reports/ # Node/npm node_modules/ +@openzeppelin +@yearnvaults \ No newline at end of file diff --git a/brownie-config.yml b/brownie-config.yml index 906db86..03b4488 100644 --- a/brownie-config.yml +++ b/brownie-config.yml @@ -5,14 +5,14 @@ networks: autofetch_sources: True dependencies: - - iearn-finance/yearn-vaults@0.3.5 + - iearn-finance/yearn-vaults@0.4.3 - OpenZeppelin/openzeppelin-contracts@3.1.0 compiler: solc: version: 0.6.12 remappings: - - "@yearnvaults=iearn-finance/yearn-vaults@0.3.5" + - "@yearnvaults=iearn-finance/yearn-vaults@0.4.3" - "@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.1.0" reports: diff --git a/contracts/EthStrategy.sol b/contracts/EthStrategy.sol deleted file mode 100644 index 45df655..0000000 --- a/contracts/EthStrategy.sol +++ /dev/null @@ -1,107 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity 0.6.12; -pragma experimental ABIEncoderV2; - -import {BaseStrategy} from "@yearnvaults/contracts/BaseStrategy.sol"; -import { - SafeERC20, - SafeMath, - IERC20, - Address -} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "@openzeppelin/contracts/math/Math.sol"; - -interface IFusdt { - function Swapout(uint256 amount, address bindaddr) external; -} - -contract Strategy is BaseStrategy { - using SafeERC20 for IERC20; - using Address for address; - using SafeMath for uint256; - - constructor(address _vault) public BaseStrategy(_vault) {} - - function name() external view override returns (string memory) { - return "StrategyEthUsdt"; - } - - function estimatedTotalAssets() public view override returns (uint256) { - return want.balanceOf(address(this)); - } - - function prepareReturn(uint256 _debtOutstanding) - internal - override - returns ( - uint256 _profit, - uint256 _loss, - uint256 _debtPayment - ) - { - uint256 debt = vault.strategies(address(this)).totalDebt; - uint256 wantBalance = balanceOfWant(); - - // Set profit or loss based on the initial debt - if (debt <= wantBalance) { - _profit = wantBalance - debt; - } else { - _loss = debt - wantBalance; - } - - // Repay debt. Amount will depend if we had profit or loss - if (_debtOutstanding > 0) { - if (_profit >= 0) { - _debtPayment = Math.min( - _debtOutstanding, - wantBalance.sub(_profit) - ); - } else { - _debtPayment = Math.min( - _debtOutstanding, - wantBalance.sub(_loss) - ); - } - } - } - - function adjustPosition(uint256 _debtOutstanding) internal override { - if (emergencyExit) { - return; - } - - uint256 balance = balanceOfWant(); - if (_debtOutstanding >= balance) { - return; - } - - IFusdt(address(want)).Swapout(balance, address(this)); - } - - function liquidatePosition(uint256 _amountNeeded) - internal - override - returns (uint256 _liquidatedAmount, uint256 _loss) - { - uint256 totalAssets = want.balanceOf(address(this)); - if (_amountNeeded > totalAssets) { - _liquidatedAmount = totalAssets; - _loss = _amountNeeded.sub(totalAssets); - } else { - _liquidatedAmount = _amountNeeded; - } - } - - function balanceOfWant() public view returns (uint256) { - return IERC20(want).balanceOf(address(this)); - } - - function prepareMigration(address _newStrategy) internal override {} - - function protectedTokens() - internal - view - override - returns (address[] memory) - {} -} diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol new file mode 100644 index 0000000..d7e66be --- /dev/null +++ b/contracts/GenericEthStrategy.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import {BaseStrategy} from "@yearnvaults/contracts/BaseStrategy.sol"; +import { + SafeERC20, + SafeMath, + IERC20, + Address +} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/math/Math.sol"; + + +contract Strategy is BaseStrategy { + using SafeERC20 for IERC20; + using Address for address; + using SafeMath for uint256; + + address public bridge; + uint256 public pendingProfit; + uint256 public minSend; + uint256 public maxSend; + + bool public fourthreeprotection; + + constructor(address _vault, address _bridge, uint256 _minSend, uint256 _maxSend) public BaseStrategy(_vault) { + + bridge = _bridge; + minSend = _minSend; + maxSend = _maxSend; + } + + function name() external view override returns (string memory) { + return "BridgeStrategy"; + } + + function estimatedTotalAssets() public view override returns (uint256) { + return totalDebt().add(pendingProfit); + } + + function totalDebt() public view returns (uint256) { + return vault.strategies(address(this)).totalDebt; + } + + function _wantBalance() internal view returns (uint256){ + return IERC20(want).balanceOf(address(this)); + } + + function prepareReturn(uint256 _debtOutstanding) + internal + override + returns ( + uint256 _profit, + uint256 _loss, + uint256 _debtPayment + ) + { + uint256 wantBal = _wantBalance(); + + if(wantBal == 0){ + return (0,0,0); + } + + _debtPayment = Math.min(wantBal, _debtOutstanding); + + wantBal = wantBal.sub(_debtPayment); + + if(pendingProfit > 0 && wantBal > 0){ + _profit = Math.min(pendingProfit, wantBal); + pendingProfit = pendingProfit.sub(_profit); + } + + } + + function adjustPosition(uint256 _debtOutstanding) internal override { + if (emergencyExit) { + return; + } + + uint256 balance = _wantBalance(); + if (_debtOutstanding >= balance) { + return; + } + balance = balance.sub(_debtOutstanding); + + if(balance > minSend){ + want.safeTransfer(bridge, Math.min(balance, maxSend)); + } + + + } + + function liquidateAllPositions() + internal + override + returns (uint256 _amountFreed) + { + + liquidatePosition(type(uint256).max); + _amountFreed = _wantBalance(); + require(_amountFreed >= totalDebt(), "Money in bridge"); + } + + //we dont use this as harvest trigger is overriden + function ethToWant(uint256 _amtInWei) + public + view + override + returns (uint256) + { + return(_amtInWei); + } + + //should never really be called as we keep late in queue + function liquidatePosition(uint256 _amountNeeded) + internal + override + returns (uint256 _liquidatedAmount, uint256 _loss) + { + uint256 totalAssets = _wantBalance(); + _liquidatedAmount = Math.min(totalAssets, _amountNeeded); + + //sub 43 protection + if(fourthreeprotection){ + require(_amountNeeded == _liquidatedAmount, "fourthreeprotection"); + } + } + + + function prepareMigration(address _newStrategy) internal override {} + + function protectedTokens() + internal + view + override + returns (address[] memory) + {} +} diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 635bce7..8e888a0 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -7,11 +7,17 @@ import { IERC20, Address } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import "@openzeppelin/contracts/math/Math.sol"; +import "@openzeppelin/contracts/math/SafeMath.sol"; interface IVault { function deposit() external; function withdraw() external; + function token() external view returns(address); + function decimals() external view returns(uint256); + function balanceOf(address) external view returns(uint256); + function pricePerShare() external view returns(uint256); function withdraw(uint256 amount) external; } @@ -22,6 +28,7 @@ interface IAny { contract GenericVaultProxy { using SafeERC20 for IERC20; using Address for address; + using SafeMath for uint256; IVault public vault; address public want; diff --git a/contracts/WethToBscStrategy.sol b/contracts/WethToBscStrategy.sol deleted file mode 100644 index 273a1a9..0000000 --- a/contracts/WethToBscStrategy.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity 0.6.12; -pragma experimental ABIEncoderV2; - -import {BaseStrategy} from "@yearnvaults/contracts/BaseStrategy.sol"; -import { - SafeERC20, - SafeMath, - IERC20, - Address -} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import "@openzeppelin/contracts/math/Math.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -interface IWETH is IERC20 { - function deposit() external payable; - - function withdraw(uint256) external; -} - -contract WethToBscStrategy is BaseStrategy { - using SafeERC20 for IERC20; - using Address for address; - using SafeMath for uint256; - - address public ethDepositToBsc = - address(0x13B432914A996b0A48695dF9B2d701edA45FF264); - - event Transfer(address indexed from, address indexed to, uint256 value); - - constructor(address _vault) public BaseStrategy(_vault) {} - - function setEthDepositAddress(address _ethDepositToBsc) - external - onlyGovernance - { - ethDepositToBsc = _ethDepositToBsc; - } - - function name() external view override returns (string memory) { - return "WethToBscStrategy"; - } - - function estimatedTotalAssets() public view override returns (uint256) { - return balanceOfWant(); - } - - function prepareReturn(uint256 _debtOutstanding) - internal - override - returns ( - uint256 _profit, - uint256 _loss, - uint256 _debtPayment - ) - { - // If we got eth back from the proxy, let's convert to weth - uint256 balanceReturnedFromBSC = address(this).balance; - if (balanceReturnedFromBSC > 0) { - IWETH(address(want)).deposit{value: address(this).balance}(); - } - - uint256 debt = vault.strategies(address(this)).totalDebt; - if (debt < balanceOfWant()) { - _profit = balanceOfWant().sub(debt); - } - - if (_debtOutstanding > 0) { - _debtPayment = Math.min(_debtOutstanding, balanceOfWant()); - } - } - - function adjustPosition(uint256 _debtOutstanding) internal override { - if (emergencyExit) { - return; - } - - uint256 balance = balanceOfWant(); - if (_debtOutstanding >= balance) { - return; - } - - IWETH(address(want)).withdraw(balanceOfWant()); - - uint256 balanceToTransfer = address(this).balance; - payable(ethDepositToBsc).transfer(balanceToTransfer); - emit Transfer(address(this), ethDepositToBsc, balanceToTransfer); - } - - function liquidatePosition(uint256 _amountNeeded) - internal - override - returns (uint256 _liquidatedAmount, uint256 _loss) - { - uint256 totalAssets = want.balanceOf(address(this)); - if (_amountNeeded > totalAssets) { - _liquidatedAmount = totalAssets; - _loss = _amountNeeded.sub(totalAssets); - } else { - _liquidatedAmount = _amountNeeded; - } - } - - function balanceOfWant() public view returns (uint256) { - return IERC20(want).balanceOf(address(this)); - } - - function prepareMigration(address _newStrategy) internal override { - uint256 balanceReturnedFromBSC = address(this).balance; - if (balanceReturnedFromBSC > 0) { - IWETH(address(want)).deposit{value: address(this).balance}(); - } - } - - function protectedTokens() - internal - view - override - returns (address[] memory) - {} - - receive() external payable {} -} From 6a26c0adf572741f87bf2da5ad98d39699f88415 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 12:30:32 +0100 Subject: [PATCH 03/13] added setters --- contracts/GenericEthStrategy.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index d7e66be..bcbdcc5 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -47,6 +47,18 @@ contract Strategy is BaseStrategy { return IERC20(want).balanceOf(address(this)); } + function setBridge(address _bridge) external onlyGovernance { + bridge = _bridge; + } + + function setMinSend(uint256 _minSend) external onlyEmergencyAuthorized { + minSend = _minSend; + } + + function setMaxSend(uint256 _maxSend) external onlyEmergencyAuthorized { + maxSend = _maxSend; + } + function prepareReturn(uint256 _debtOutstanding) internal override From cccb0fed93881757f7a64594c23fdb4f47282a9f Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 12:37:05 +0100 Subject: [PATCH 04/13] setters and harvest trigger --- contracts/GenericEthStrategy.sol | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index bcbdcc5..3c23762 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -59,6 +59,15 @@ contract Strategy is BaseStrategy { maxSend = _maxSend; } + function incrementPendingProfit(uint256 _newProfit) external onlyEmergencyAuthorized { + pendingProfit = pendingProfit.add(_newProfit); + } + + //for use if a mistake is made + function clearPendingProfit() external onlyEmergencyAuthorized { + pendingProfit = 0; + } + function prepareReturn(uint256 _debtOutstanding) internal override @@ -139,6 +148,25 @@ contract Strategy is BaseStrategy { } } + //simplified harvest function + function harvestTrigger(uint256 gasCost) public override view returns (bool) { + + StrategyParams memory params = vault.strategies(address(this)); + + // Should not trigger if strategy is not activated + if (params.activation == 0) return false; + + // Check for profits and losses + uint256 wantBal = _wantBalance(); + bool harvest; + if(wantBal > debtThreshold){ + harvest = vault.debtOutstanding() >= debtThreshold; + harvest = pendingProfit >= debtThreshold; + } + + return harvest; + } + function prepareMigration(address _newStrategy) internal override {} From ca14abb4958c3700b09bdc020a036f834cc26d37 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 12:38:25 +0100 Subject: [PATCH 05/13] fixed error --- contracts/GenericEthStrategy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index 3c23762..075e736 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -2,7 +2,7 @@ pragma solidity 0.6.12; pragma experimental ABIEncoderV2; -import {BaseStrategy} from "@yearnvaults/contracts/BaseStrategy.sol"; +import {BaseStrategy, StrategyParams} from "@yearnvaults/contracts/BaseStrategy.sol"; import { SafeERC20, SafeMath, From 3201346a4fe01461638dd6a0677c2eba4a068646 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 12:40:12 +0100 Subject: [PATCH 06/13] fourthree protection --- contracts/GenericEthStrategy.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index 075e736..67668a6 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -59,6 +59,10 @@ contract Strategy is BaseStrategy { maxSend = _maxSend; } + function setFourThreeProtection(bool _protection) external onlyEmergencyAuthorized { + fourthreeprotection = _protection; + } + function incrementPendingProfit(uint256 _newProfit) external onlyEmergencyAuthorized { pendingProfit = pendingProfit.add(_newProfit); } From 6ee39c69a7d825bd914644e20adf9012a7b683ca Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 13:11:55 +0100 Subject: [PATCH 07/13] poolpis freedom --- contracts/GenericEthStrategy.sol | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index 67668a6..a79aff6 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -63,13 +63,8 @@ contract Strategy is BaseStrategy { fourthreeprotection = _protection; } - function incrementPendingProfit(uint256 _newProfit) external onlyEmergencyAuthorized { - pendingProfit = pendingProfit.add(_newProfit); - } - - //for use if a mistake is made - function clearPendingProfit() external onlyEmergencyAuthorized { - pendingProfit = 0; + function setPendingProfit(uint256 _pendingProfit) external onlyEmergencyAuthorized { + pendingProfit = _pendingProfit; } function prepareReturn(uint256 _debtOutstanding) From 55e30f42da8240b974f807234127d36889688e18 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 13:18:04 +0100 Subject: [PATCH 08/13] proxy changes to use max loss and migrate vault --- contracts/GenericVaultProxy.sol | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 8e888a0..1afc9ca 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -37,6 +37,7 @@ contract GenericVaultProxy { address public keeper; address public governance; address public pendingGovernance; + uint256 public maxLoss; constructor(address _keeper, address _gov, address _vault) public { vault = IVault(_vault); @@ -44,6 +45,7 @@ contract GenericVaultProxy { keeper = _keeper; governance = _gov; strategist = msg.sender; + maxLoss = 1; IERC20(want).safeApprove(address(vault), type(uint256).max); } @@ -61,6 +63,18 @@ contract GenericVaultProxy { _; } + // Move yvDAI funds to a new yVault + function migrateToNewDaiYVault(IVault newYVault) external onlyGov { + uint256 balanceOfYVault = yVault.balanceOf(address(this)); + if (balanceOfYVault > 0) { + yVault.withdraw(balanceOfYVault, address(this), maxLoss); + } + investmentToken.safeApprove(address(yVault), 0); + + yVault = newYVault; + _depositInvestmentTokenInYVault(); + } + function name() external view returns (string memory) { return "BridgeVaultProxy"; } @@ -81,6 +95,9 @@ contract GenericVaultProxy { function setPendingGovernance(address _pendingGovernance) external onlyGov { pendingGovernance = _pendingGovernance; } + function setMaxLoss(address _maxLoss) external onlyGuardians { + maxLoss = _maxLoss; + } function totalAssets() public view returns (uint256) { return _vaultBalance().add(_wantBalance()); @@ -93,7 +110,10 @@ contract GenericVaultProxy { } function sendAllBack() external onlyGuardians { - vault.withdraw(); + uint256 balanceOfYVault = yVault.balanceOf(address(this)); + if (balanceOfYVault > 0) { + yVault.withdraw(balanceOfYVault, address(this), maxLoss); + } IAny(want).Swapout(_wantBalance(), address(this)); } @@ -101,7 +121,7 @@ contract GenericVaultProxy { uint256 wantBal = _wantBalance(); if(wantBal < amount){ uint256 toWithdraw = amount.sub(wantBal); - vault.withdraw(toWithdraw.mul(10 ** vault.decimals()).div(vault.pricePerShare())); + vault.withdraw(toWithdraw.mul(10 ** vault.decimals()).div(vault.pricePerShare()), address(this), maxLoss); } IAny(want).Swapout(Math.min(_wantBalance(), amount), address(this)); From ca67f972247ed4d607e592e7906a39ce7f015d3f Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 13:20:49 +0100 Subject: [PATCH 09/13] compile errors --- contracts/GenericVaultProxy.sol | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 1afc9ca..70cf72b 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -19,6 +19,11 @@ interface IVault { function balanceOf(address) external view returns(uint256); function pricePerShare() external view returns(uint256); function withdraw(uint256 amount) external; + function withdraw( + uint256 amount, + address account, + uint256 maxLoss + ) external returns (uint256); } interface IAny { @@ -65,14 +70,14 @@ contract GenericVaultProxy { // Move yvDAI funds to a new yVault function migrateToNewDaiYVault(IVault newYVault) external onlyGov { - uint256 balanceOfYVault = yVault.balanceOf(address(this)); + uint256 balanceOfYVault = vault.balanceOf(address(this)); if (balanceOfYVault > 0) { - yVault.withdraw(balanceOfYVault, address(this), maxLoss); + vault.withdraw(balanceOfYVault, address(this), maxLoss); } - investmentToken.safeApprove(address(yVault), 0); + IERC20(want).safeApprove(address(vault), 0); - yVault = newYVault; - _depositInvestmentTokenInYVault(); + vault = newYVault; + vault.deposit(); } function name() external view returns (string memory) { @@ -95,7 +100,7 @@ contract GenericVaultProxy { function setPendingGovernance(address _pendingGovernance) external onlyGov { pendingGovernance = _pendingGovernance; } - function setMaxLoss(address _maxLoss) external onlyGuardians { + function setMaxLoss(uint256 _maxLoss) external onlyGuardians { maxLoss = _maxLoss; } @@ -110,9 +115,9 @@ contract GenericVaultProxy { } function sendAllBack() external onlyGuardians { - uint256 balanceOfYVault = yVault.balanceOf(address(this)); + uint256 balanceOfYVault = vault.balanceOf(address(this)); if (balanceOfYVault > 0) { - yVault.withdraw(balanceOfYVault, address(this), maxLoss); + vault.withdraw(balanceOfYVault, address(this), maxLoss); } IAny(want).Swapout(_wantBalance(), address(this)); } From 4d765e0f41a9b7ac3b51e7a219f26a2ccfffcb23 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 3 Sep 2021 13:24:31 +0100 Subject: [PATCH 10/13] migrate fix --- contracts/GenericVaultProxy.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 70cf72b..9034a36 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -77,6 +77,7 @@ contract GenericVaultProxy { IERC20(want).safeApprove(address(vault), 0); vault = newYVault; + IERC20(want).safeApprove(address(vault), type(uint256).max); vault.deposit(); } From a4416a55595e1f25fe82301c9b92043c1f0c2f96 Mon Sep 17 00:00:00 2001 From: flashfish Date: Mon, 6 Sep 2021 00:35:34 +0100 Subject: [PATCH 11/13] added delegated assets --- contracts/GenericEthStrategy.sol | 4 +++ contracts/GenericVaultProxy.sol | 2 +- tests/test_eth_strat.py | 59 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 tests/test_eth_strat.py diff --git a/contracts/GenericEthStrategy.sol b/contracts/GenericEthStrategy.sol index a79aff6..910ecec 100644 --- a/contracts/GenericEthStrategy.sol +++ b/contracts/GenericEthStrategy.sol @@ -35,6 +35,10 @@ contract Strategy is BaseStrategy { return "BridgeStrategy"; } + function delegatedAssets() external view override returns (uint256) { + return totalDebt(); + } + function estimatedTotalAssets() public view override returns (uint256) { return totalDebt().add(pendingProfit); } diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 9034a36..74e5d81 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -81,7 +81,7 @@ contract GenericVaultProxy { vault.deposit(); } - function name() external view returns (string memory) { + function name() external pure returns (string memory) { return "BridgeVaultProxy"; } diff --git a/tests/test_eth_strat.py b/tests/test_eth_strat.py new file mode 100644 index 0000000..2402d84 --- /dev/null +++ b/tests/test_eth_strat.py @@ -0,0 +1,59 @@ +import pytest +import brownie +from brownie import Contract, Wei, accounts, chain + +def test_deploy( + chain, + whale, + interface, + Strategy, + Contract, + accounts, +): + deployer = accounts.at('0xBb4eDcFeC106B378e4b4ec478a985017Bd423523', force=True) + strategist = accounts.at('0xFeC07aca9d4311FE6F114Dbd25BBb8E6f8894AEA', force=True) + + registry = Contract('0x50c1a2eA0a861A967D9d0FFE2AE4012c2E053804') + dai = interface.ERC20('0x6B175474E89094C44Da98b954EedeAC495271d0F') + + expected_address = '0xAd02E4C635DA744CC1754d14170dC157df6232aF' + strat_ms = accounts.at('0x16388463d60FFE0661Cf7F1f31a7D658aC790ff7', force=True) + yearn_dev_ms = '0x846e211e8ba920B353FB717631C015cf04061Cc9' + + tx = registry.newExperimentalVault(dai, strat_ms, yearn_dev_ms, strat_ms, "", "", {'from': strategist}) + vault = Contract(tx.return_value) + + bridge = '0xC564EE9f21Ed8A2d8E7e76c085740d5e4c5FaFbE' + min_send = 1_000 * 1e18 + max_send = 950_000 * 1e18 + + strategy = Strategy.deploy(vault, bridge, min_send, max_send, {'from': deployer}) + + assert strategy == expected_address + print(vault.apiVersion()) + + print(strategy.estimatedTotalAssets()/1e18) + + vault.addStrategy(strategy, 10000, 0, 2**256-1, 1000, {'from': strat_ms}) + vault.setDepositLimit(100_000_000 *1e18, {'from': strat_ms}) + + dai.approve(vault, 2 ** 256 - 1, {"from": whale}) + before_balance = dai.balanceOf(bridge) + + deposit = 10_000 *1e18 + + vault.deposit(deposit, {"from": whale}) + + strategy.harvest({'from': deployer}) + + print((dai.balanceOf(bridge) - before_balance)/1e18 ) + + + dai.transfer(strategy, 1000 *1e18, {"from": whale}) + strategy.setPendingProfit(100 *1e18, {'from': strat_ms}) + strategy.harvest({'from': deployer}) + assert dai.balanceOf(vault) == 100 *1e18 + assert dai.balanceOf(strategy) == 900 *1e18 + + + From fbcf7d61fe44d44dc603693e662fe8ca8b63cd21 Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 10 Sep 2021 13:58:34 +0100 Subject: [PATCH 12/13] improvements --- contracts/GenericVaultProxy.sol | 45 ++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index 74e5d81..a1972bb 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -43,13 +43,15 @@ contract GenericVaultProxy { address public governance; address public pendingGovernance; uint256 public maxLoss; + uint256 public dustThreshold; - constructor(address _keeper, address _gov, address _vault) public { + constructor(address _keeper, address _gov, address _vault, uint256 _dustThreshold) public { vault = IVault(_vault); want = vault.token(); keeper = _keeper; governance = _gov; strategist = msg.sender; + dustThreshold = _dustThreshold; maxLoss = 1; IERC20(want).safeApprove(address(vault), type(uint256).max); } @@ -59,6 +61,12 @@ contract GenericVaultProxy { _; } + modifier onlyManagers { + require(msg.sender == strategist || + msg.sender == governance); + _; + } + modifier onlyGuardians { require( msg.sender == strategist || @@ -82,7 +90,7 @@ contract GenericVaultProxy { } function name() external pure returns (string memory) { - return "BridgeVaultProxy"; + return "BridgeVaultProxyV2"; } function _wantBalance() internal view returns (uint256){ @@ -101,7 +109,13 @@ contract GenericVaultProxy { function setPendingGovernance(address _pendingGovernance) external onlyGov { pendingGovernance = _pendingGovernance; } - function setMaxLoss(uint256 _maxLoss) external onlyGuardians { + function setKeeper(address _keeper) external onlyManagers { + keeper = _keeper; + } + function setStrategist(address _strategist) external onlyManagers { + strategist = _strategist; + } + function setMaxLoss(uint256 _maxLoss) external onlyManagers { maxLoss = _maxLoss; } @@ -109,13 +123,18 @@ contract GenericVaultProxy { return _vaultBalance().add(_wantBalance()); } - function deposit() external onlyGuardians { + //to mimic harvest on normal strats + function harvest() external onlyGuardians { if (_wantBalance() > 0) { vault.deposit(); } } - function sendAllBack() external onlyGuardians { + function harvestTrigger(uint256 callCost) public view returns (bool) { + return _wantBalance() > dustThreshold; + } + + function sendAllBack() external onlyManagers { uint256 balanceOfYVault = vault.balanceOf(address(this)); if (balanceOfYVault > 0) { vault.withdraw(balanceOfYVault, address(this), maxLoss); @@ -123,7 +142,7 @@ contract GenericVaultProxy { IAny(want).Swapout(_wantBalance(), address(this)); } - function sendWantBack(uint256 amount) external onlyGuardians { + function sendWantBack(uint256 amount) external onlyManagers { uint256 wantBal = _wantBalance(); if(wantBal < amount){ uint256 toWithdraw = amount.sub(wantBal); @@ -135,7 +154,21 @@ contract GenericVaultProxy { //sweep function in case bridge breaks and we are trapped function sweep(address token, uint256 amount) external onlyGov { + assert(token != want && token != address(vault)); IERC20(token).safeTransfer(governance, amount); } + function migrate(address newStrategy) external onlyGov { + uint256 balanceWant = _wantBalance(); + + if(balanceWant > 0){ + IERC20(want).safeTransfer(newStrategy, balanceWant); + } + uint256 ytokens = vault.balanceOf(address(this)); + if(ytokens >0){ + IERC20(address(vault)).safeTransfer(newStrategy, ytokens); + } + + } + } \ No newline at end of file From 3085851383dd64af71ef9188afe7bcb94c71260e Mon Sep 17 00:00:00 2001 From: flashfish Date: Fri, 10 Sep 2021 13:59:01 +0100 Subject: [PATCH 13/13] comment --- contracts/GenericVaultProxy.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/GenericVaultProxy.sol b/contracts/GenericVaultProxy.sol index a1972bb..6e54f1f 100644 --- a/contracts/GenericVaultProxy.sol +++ b/contracts/GenericVaultProxy.sol @@ -152,7 +152,7 @@ contract GenericVaultProxy { IAny(want).Swapout(Math.min(_wantBalance(), amount), address(this)); } - //sweep function in case bridge breaks and we are trapped + //sweep function in case we get extra tokens sent function sweep(address token, uint256 amount) external onlyGov { assert(token != want && token != address(vault)); IERC20(token).safeTransfer(governance, amount);