From e8a57bdd021af6fded046b2d748952fd71ed7b35 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Thu, 22 Sep 2022 15:37:49 +0300 Subject: [PATCH 01/44] feat(vault): remove rebalance and _setCacheNextBestStrategy --- contracts/protocol/tokenization/Vault.sol | 38 ++--------------------- 1 file changed, 3 insertions(+), 35 deletions(-) diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index eca9ec21..0e2cf7e7 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -29,6 +29,9 @@ import { IERC20PermitLegacy } from "../../interfaces/opty/IERC20PermitLegacy.sol import { IVault } from "../../interfaces/opty/IVault.sol"; import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; import { IRiskManager } from "../earn-protocol-configuration/contracts/interfaces/opty/IRiskManager.sol"; +import { + IInvestStrategyRegistry +} from "../earn-protocol-configuration/contracts/interfaces/opty/IInvestStrategyRegistry.sol"; /** * @title Vault contract inspired by AAVE V2's AToken.sol @@ -229,30 +232,6 @@ contract Vault is emit LogUnpause((vaultConfiguration & (1 << 249)) != 0, msg.sender); } - /** - * @inheritdoc IVault - */ - function rebalance() external override { - _checkVaultDeposit(); - _setCacheNextInvestStrategySteps(getNextBestInvestStrategy()); - bytes32 _nextBestInvestStrategyHash = computeInvestStrategyHash(_cacheNextInvestStrategySteps); - if (_nextBestInvestStrategyHash != investStrategyHash) { - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAllFromStrategy(investStrategySteps); - } - // _setInvestStrategySteps - delete investStrategySteps; - for (uint256 _i; _i < _cacheNextInvestStrategySteps.length; _i++) { - investStrategySteps.push(_cacheNextInvestStrategySteps[_i]); - } - investStrategyHash = _nextBestInvestStrategyHash; - } - uint256 _balanceUT = balanceUT(); - if (investStrategyHash != Constants.ZERO_BYTES32 && _balanceUT > 0) { - _vaultDepositToStrategy(investStrategySteps, _balanceUT); - } - } - /** * @inheritdoc IVault */ @@ -806,17 +785,6 @@ contract Vault is emit LogTotalValueLockedLimitUT(totalValueLockedLimitUT, msg.sender); } - /** - * @dev Internal function for caching the next invest strategy metadata - * @param _investStrategySteps list strategy steps - */ - function _setCacheNextInvestStrategySteps(DataTypes.StrategyStep[] memory _investStrategySteps) internal { - delete _cacheNextInvestStrategySteps; - for (uint256 _i; _i < _investStrategySteps.length; _i++) { - _cacheNextInvestStrategySteps.push(_investStrategySteps[_i]); - } - } - /** * @dev Internal function to save risk profile code * @param _riskProfileCode risk profile code From 4190952883df21c9cbf7947e56983c246e8b0f03 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 27 Sep 2022 21:26:54 +0300 Subject: [PATCH 02/44] feat(refactor): move strategies to vault storage, store strategy metadata in investregistry --- contracts/interfaces/opty/IVault.sol | 29 ++-- .../mocks/contracts/TestEmergencyBrake.sol | 11 +- contracts/mocks/contracts/TestVault.sol | 3 +- .../protocol/earn-protocol-configuration | 2 +- contracts/protocol/tokenization/Vault.sol | 133 +++++++++++++++--- .../protocol/tokenization/VaultStorage.sol | 4 + contracts/utils/Errors.sol | 3 + 7 files changed, 144 insertions(+), 41 deletions(-) diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index 92a43dc3..ecdc4ff8 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -126,15 +126,6 @@ interface IVault { */ function setUnpaused(bool _unpaused) external; - /** - * @notice Withdraw the underlying asset of vault from previous strategy if any, - * claims and swaps the reward tokens for the underlying token - * performs batch minting of shares for users deposited previously without rebalance, - * deposits the assets into the new strategy if any or holds the same in the vault - * @dev the vault will be charged to compensate gas fees if operator calls this function - */ - function rebalance() external; - /** * @notice Deposit underlying tokens to the vault * @dev Mint the shares right away as per oracle based price per full share value @@ -157,12 +148,14 @@ interface IVault { * @dev Burn the shares right away as per oracle based price per full share value * @param _receiver the address which will receive the underlying tokens * @param _userWithdrawVT amount in vault token + * @param _withdrawStrategies array of strategy hashes to identify which strategies to withdraw from * @param _accountsProof merkle proof for caller * @param _codesProof merkle proof for code hash if caller is smart contract */ function userWithdrawVault( address _receiver, uint256 _userWithdrawVT, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof, bytes32[] calldata _codesProof ) external returns (uint256); @@ -334,12 +327,6 @@ interface IVault { view returns (uint256); - /** - * @notice retireves current strategy metadata - * @return array of strategy steps - */ - function getInvestStrategySteps() external view returns (DataTypes.StrategyStep[] memory); - /** * @dev function to compute the keccak256 hash of the strategy steps * @param _investStrategySteps metadata for invest strategy @@ -399,4 +386,16 @@ interface IVault { * @param rewardTokenAmount Amount of reward token claimed */ event RewardTokenClaimed(address liquidityPool, uint256 rewardTokenAmount); + + /** + * @notice Emitted when a strategy is added to the vault + * @param _strategyHash the hash of the strategy added + */ + event AddStrategy(bytes32 _strategyHash); + + /** + * @notice Emitted when a strategy is removed from the vault + * @param _strategyHash the hash of the strategy removed + */ + event RemoveStrategy(bytes32 _strategyHash); } diff --git a/contracts/mocks/contracts/TestEmergencyBrake.sol b/contracts/mocks/contracts/TestEmergencyBrake.sol index b6f427d8..7cd3bd51 100644 --- a/contracts/mocks/contracts/TestEmergencyBrake.sol +++ b/contracts/mocks/contracts/TestEmergencyBrake.sol @@ -28,9 +28,10 @@ contract TestEmergencyBrake { function runUserWithdrawVault( uint256 _userWithdrawVT, bytes32[] calldata _accountProofs, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _codeProofs ) external { - vaultAddr.userWithdrawVault(address(this), _userWithdrawVT, _accountProofs, _codeProofs); + vaultAddr.userWithdrawVault(address(this), _userWithdrawVT, _withdrawStrategies, _accountProofs, _codeProofs); } function runTwoTxnUserDepositVault( @@ -46,24 +47,26 @@ contract TestEmergencyBrake { function runTwoTxnUserWithdrawVault( uint256 _userDepositUT, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountProofs, bytes32[] calldata _codeProofs ) external { tokenAddr.approve(address(vaultAddr), (_userDepositUT + _userDepositUT)); tokenAddr.transfer(address(vaultAddr), (_userDepositUT + _userDepositUT)); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs, _codeProofs); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs, _codeProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _withdrawStrategies, _accountProofs, _codeProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _withdrawStrategies, _accountProofs, _codeProofs); } function runTwoTxnDepositAndWithdraw( uint256 _userDepositUT, bytes calldata _permit, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountProofs, bytes32[] calldata _codeProofs ) external { tokenAddr.approve(address(vaultAddr), _userDepositUT); vaultAddr.userDepositVault(address(this), _userDepositUT, _permit, _accountProofs, _codeProofs); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs, _codeProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _withdrawStrategies, _accountProofs, _codeProofs); } function runTwoTxnDepositAndTransfer( diff --git a/contracts/mocks/contracts/TestVault.sol b/contracts/mocks/contracts/TestVault.sol index b122ffb4..71dc64d1 100644 --- a/contracts/mocks/contracts/TestVault.sol +++ b/contracts/mocks/contracts/TestVault.sol @@ -29,10 +29,11 @@ contract TestVault { function withdraw( IVault _vault, uint256 _amountVT, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof, bytes32[] calldata _codesProof ) external { - _vault.userWithdrawVault(msg.sender, _amountVT, _accountsProof, _codesProof); + _vault.userWithdrawVault(msg.sender, _amountVT, _withdrawStrategies, _accountsProof, _codesProof); } function withdrawERC20(ERC20 _token, address _recipient) external { diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index b1abdebb..085d13d8 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit b1abdebb37ac0eddd11d49f0188b6ee5668adcbc +Subproject commit 085d13d87be71070c678d3170f0dd7a2b7ab677f diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 0e2cf7e7..0101da46 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -3,6 +3,8 @@ pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; +//NOTE: Variable Shadowing + // helper contracts import { MultiCall } from "../../utils/MultiCall.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; @@ -21,6 +23,7 @@ import { StrategyManager } from "../lib/StrategyManager.sol"; import { ClaimAndHarvest } from "../lib/ClaimAndHarvest.sol"; import { MerkleProof } from "@openzeppelin/contracts/cryptography/MerkleProof.sol"; import { Counters } from "@openzeppelin/contracts/utils/Counters.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; // interfaces import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -53,6 +56,7 @@ contract Vault is using ClaimAndHarvest for address; using StrategyManager for DataTypes.StrategyStep[]; using Counters for Counters.Counter; + using EnumerableSet for EnumerableSet.Bytes32Set; /** * @dev The version of the Vault business logic @@ -254,11 +258,12 @@ contract Vault is function userWithdrawVault( address _receiver, uint256 _userWithdrawVT, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof, bytes32[] calldata _codesProof ) external override nonReentrant returns (uint256) { _emergencyBrake(false); - return _withdrawVaultFor(_receiver, _userWithdrawVT, _accountsProof, _codesProof); + return _withdrawVaultFor(_receiver, _userWithdrawVT, _withdrawStrategies, _accountsProof, _codesProof); } /** @@ -271,6 +276,32 @@ contract Vault is } } + function vaultDepositToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external onlyStrategyOperator { + _checkVaultDeposit(); + + IInvestStrategyRegistry investStrategyRegistry = + IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + + require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); + + //add check for minimum LP returned + //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) + + _vaultDepositToStrategy(investStrategyRegistry.getStrategySteps(_strategyHash), _depositValueUT); + } + + function vaultWithdrawFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external onlyStrategyOperator { + IInvestStrategyRegistry investStrategyRegistry = + IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + + require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); + + //add check for minimum USDC returned + //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) + + _vaultWithdrawSomeFromStrategy(investStrategyRegistry.getStrategySteps(_strategyHash), _withdrawAmountLP); + } + /** * @inheritdoc IVault */ @@ -507,13 +538,6 @@ contract Vault is ); } - /** - * @inheritdoc IVault - */ - function getInvestStrategySteps() public view override returns (DataTypes.StrategyStep[] memory) { - return investStrategySteps; - } - /** * @inheritdoc IVault */ @@ -625,12 +649,14 @@ contract Vault is * @dev internal function withdraw for an user * @param _receiver address of the receiver of the underlying token * @param _userWithdrawVT amount in vault token + * @param _withdrawStrategies strategies to withdraw from * @param _accountsProof merkle proof for caller * @param _codesProof merkle proof for code hash if caller is smart contract */ function _withdrawVaultFor( address _receiver, uint256 _userWithdrawVT, + bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof, bytes32[] calldata _codesProof ) internal returns (uint256) { @@ -647,15 +673,37 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - - uint256 _oraAmountLP = - investStrategySteps.getOraSomeValueLP( - address(registryContract), - underlyingToken, - _expectedStratWithdrawUT - ); - - _vaultWithdrawSomeFromStrategy(investStrategySteps, _oraAmountLP); + IInvestStrategyRegistry investStrategyRegistry = + IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + + for (uint256 i; i < _withdrawStrategies.length; i++) { + uint256 _oraAmountLP; + uint256 buffer = + investStrategyRegistry.getStrategyWithdrawalBuffer(address(this), _withdrawStrategies[i]); + bytes32 withdrawStrategyHash = _withdrawStrategies[i]; + DataTypes.StrategyStep[] memory _steps = investStrategyRegistry.getStrategySteps(withdrawStrategyHash); + + if (buffer >= _expectedStratWithdrawUT) { + _oraAmountLP = _steps.getOraSomeValueLP( + address(registryContract), + underlyingToken, + _expectedStratWithdrawUT + ); + + _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); + + break; + } else { + _expectedStratWithdrawUT -= buffer; + _oraAmountLP = _steps.getOraSomeValueLP(address(registryContract), underlyingToken, buffer); + + _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); + + if (i == _withdrawStrategies.length - 1 && _expectedStratWithdrawUT != 0) { + revert(Errors.WITHDRAWAL_TOO_LARGE); + } + } + } // Identify Slippage // UT requested from strategy withdraw = _expectedStratWithdrawUT @@ -859,18 +907,27 @@ contract Vault is * @return amount in underlying token */ function _oraVaultAndStratValueUT() internal view returns (uint256) { - return _oraStratValueUT().add(balanceUT()); + uint256 _totalValue = balanceUT(); + + DataTypes.StrategyStep[][] memory _allSteps = _getAllStrategySteps(); + + for (uint256 i; i < _allSteps.length; i++) { + _totalValue += _oraStratValueUT(_allSteps[i]); //use SafeMath + } + + return _totalValue; } /** * @dev Computes the amount in underlying token for the investment made in strategy + * @param _steps the strategy steps for the strategy to check * @return amount in underlying token */ - function _oraStratValueUT() internal view returns (uint256) { + function _oraStratValueUT(DataTypes.StrategyStep[] memory _steps) internal view returns (uint256) { // totaldebt return investStrategyHash != Constants.ZERO_BYTES32 - ? investStrategySteps.getOraValueUT(address(registryContract), payable(address(this)), underlyingToken) + ? _steps.getOraValueUT(address(registryContract), payable(address(this)), underlyingToken) : 0; } @@ -990,6 +1047,42 @@ contract Vault is nonce.increment(); } + function _getAllStrategySteps() internal view returns (DataTypes.StrategyStep[][] memory) { + //perhaps add a check for inactivity of strategy + DataTypes.StrategyStep[][] memory allSteps; + uint256 numStrategies = strategies.length(); + + for (uint256 i; i < numStrategies; i++) { + allSteps[i] = IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()) + .getStrategySteps(strategies.at(i)); + } + + return allSteps; + } + + /** + * @notice adds a strategy hash to vault strategies + * @param _strategyHash the hash of the strategy + */ + function _addStrategy(bytes32 _strategyHash) internal { + require(!strategies.contains(_strategyHash), "InvestStrategyRegistry: strategy already set"); + + strategies.add(_strategyHash); + + emit AddStrategy(_strategyHash); + } + + /** + * @notice removes a strategy hash to vault strategies + * @param _strategyHash the hash of the strategy + */ + function _removeStrategy(bytes32 _strategyHash) internal { + require(strategies.contains(_strategyHash), "InvestStrategyRegistry: strategy does not exist"); + strategies.remove(_strategyHash); + + emit RemoveStrategy(_strategyHash); + } + //===Internal pure functions===// /** diff --git a/contracts/protocol/tokenization/VaultStorage.sol b/contracts/protocol/tokenization/VaultStorage.sol index a770cf75..a7c80fcb 100644 --- a/contracts/protocol/tokenization/VaultStorage.sol +++ b/contracts/protocol/tokenization/VaultStorage.sol @@ -6,6 +6,7 @@ pragma solidity ^0.6.12; // library import { DataTypes } from "../../protocol/earn-protocol-configuration/contracts/libraries/types/DataTypes.sol"; import { Counters } from "@openzeppelin/contracts/utils/Counters.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; /** * @title Vault state that can change @@ -107,4 +108,7 @@ contract VaultStorageV3 is VaultStorageV2 { /**@dev deposit and withdraw flag*/ mapping(uint256 => bool) internal _blockTransaction; + + /**@dev set of strategy hashes which the vault may operate on */ + EnumerableSet.Bytes32Set internal strategies; } diff --git a/contracts/utils/Errors.sol b/contracts/utils/Errors.sol index d81ea9d2..75dbc257 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -29,4 +29,7 @@ library Errors { string public constant PERMIT_FAILED = "24"; string public constant PERMIT_LEGACY_FAILED = "25"; string public constant AMOUNT_EXCEEDS_ALLOWANCE = "26"; + string public constant BUFFER_EXCEEDED = "27"; + string public constant INVALID_STRATEGY = "28"; + string public constant WITHDRAWAL_TOO_LARGE = "29"; } From 23546397ac686370c68dfab08f6e05a1ee314ba7 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 27 Sep 2022 21:31:41 +0300 Subject: [PATCH 03/44] feat(config): update earn protocol configuration --- contracts/protocol/earn-protocol-configuration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 085d13d8..53bd088f 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 085d13d87be71070c678d3170f0dd7a2b7ab677f +Subproject commit 53bd088fced9226eee448548be051407ab01442c From 33af74e6dfdc7a493f803445d065be90ea157d4d Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Thu, 29 Sep 2022 18:38:32 +0300 Subject: [PATCH 04/44] feat(rename): rename InvestStrategyRegitsry => StrategyRegistry --- .../protocol/earn-protocol-configuration | 2 +- contracts/protocol/tokenization/Vault.sol | 19 ++++++++----------- 2 files changed, 9 insertions(+), 12 deletions(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 53bd088f..7693a692 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 53bd088fced9226eee448548be051407ab01442c +Subproject commit 7693a6926791d01318e02baf2cbd62c558eaef71 diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 0101da46..8f3a4e26 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -32,9 +32,7 @@ import { IERC20PermitLegacy } from "../../interfaces/opty/IERC20PermitLegacy.sol import { IVault } from "../../interfaces/opty/IVault.sol"; import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; import { IRiskManager } from "../earn-protocol-configuration/contracts/interfaces/opty/IRiskManager.sol"; -import { - IInvestStrategyRegistry -} from "../earn-protocol-configuration/contracts/interfaces/opty/IInvestStrategyRegistry.sol"; +import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; /** * @title Vault contract inspired by AAVE V2's AToken.sol @@ -279,8 +277,7 @@ contract Vault is function vaultDepositToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external onlyStrategyOperator { _checkVaultDeposit(); - IInvestStrategyRegistry investStrategyRegistry = - IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + IStrategyRegistry investStrategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); @@ -291,8 +288,7 @@ contract Vault is } function vaultWithdrawFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external onlyStrategyOperator { - IInvestStrategyRegistry investStrategyRegistry = - IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + IStrategyRegistry investStrategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); @@ -673,8 +669,8 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - IInvestStrategyRegistry investStrategyRegistry = - IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()); + IStrategyRegistry investStrategyRegistry = + IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); for (uint256 i; i < _withdrawStrategies.length; i++) { uint256 _oraAmountLP; @@ -1053,8 +1049,9 @@ contract Vault is uint256 numStrategies = strategies.length(); for (uint256 i; i < numStrategies; i++) { - allSteps[i] = IInvestStrategyRegistry(IRegistry(registryContract).getInvestStrategyRegistry()) - .getStrategySteps(strategies.at(i)); + allSteps[i] = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()).getStrategySteps( + strategies.at(i) + ); } return allSteps; From 7726921eaefe8ae7fc4ef52953a5fb93c412deb9 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Thu, 29 Sep 2022 18:57:06 +0300 Subject: [PATCH 05/44] feat(strategyregistry): move withdrawal buffers to vault storage --- contracts/protocol/earn-protocol-configuration | 2 +- contracts/protocol/tokenization/Vault.sol | 4 ++-- contracts/protocol/tokenization/VaultStorage.sol | 3 +++ 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 7693a692..3ef68db2 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 7693a6926791d01318e02baf2cbd62c558eaef71 +Subproject commit 3ef68db29aba313fb942fb1d38b03472c684efce diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 8f3a4e26..34e26cb2 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -674,9 +674,9 @@ contract Vault is for (uint256 i; i < _withdrawStrategies.length; i++) { uint256 _oraAmountLP; - uint256 buffer = - investStrategyRegistry.getStrategyWithdrawalBuffer(address(this), _withdrawStrategies[i]); + bytes32 withdrawStrategyHash = _withdrawStrategies[i]; + uint256 buffer = withdrawalBuffers[withdrawStrategyHash]; DataTypes.StrategyStep[] memory _steps = investStrategyRegistry.getStrategySteps(withdrawStrategyHash); if (buffer >= _expectedStratWithdrawUT) { diff --git a/contracts/protocol/tokenization/VaultStorage.sol b/contracts/protocol/tokenization/VaultStorage.sol index a7c80fcb..5a549487 100644 --- a/contracts/protocol/tokenization/VaultStorage.sol +++ b/contracts/protocol/tokenization/VaultStorage.sol @@ -111,4 +111,7 @@ contract VaultStorageV3 is VaultStorageV2 { /**@dev set of strategy hashes which the vault may operate on */ EnumerableSet.Bytes32Set internal strategies; + + /**@dev mapping of strategy to withdrawal buffer */ + mapping(bytes32 => uint256) internal withdrawalBuffers; } From 998faebc3a24d6cb627d67ee7227d1050a567193 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Fri, 30 Sep 2022 16:14:23 +0300 Subject: [PATCH 06/44] feat(strategies): expose add/remove strategy and remove unrequired function --- contracts/interfaces/opty/IVault.sol | 18 ++++++++++------ contracts/protocol/tokenization/Vault.sol | 25 +++++++++++++---------- 2 files changed, 26 insertions(+), 17 deletions(-) diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index ecdc4ff8..78802a14 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -191,6 +191,18 @@ interface IVault { */ function harvestAll(address _liquidityPool) external; + /** + * @notice adds a strategy to the vault + * @param _strategyHash hash of strategy + */ + function addStrategy(bytes32 _strategyHash) external; + + /** + * @notice removes a strategy from the vault + * @param _strategyHash hash of strategy + */ + function removeStrategy(bytes32 _strategyHash) external; + /** * @notice Retrieve underlying token balance in the vault * @return The balance of underlying token in the vault @@ -310,12 +322,6 @@ interface IVault { */ function calcWithdrawalFeeUT(uint256 _userWithdrawUT) external view returns (uint256); - /** - * @notice Returns next best invest strategy that the vault will execute on next rebalance - * @return the strategy metadata - */ - function getNextBestInvestStrategy() external view returns (DataTypes.StrategyStep[] memory); - /** * @notice function to compute the balance of lptoken of the vault * in the last step of the strategy diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 34e26cb2..78bb5057 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -354,6 +354,20 @@ contract Vault is emit Harvested(_liquidityPool, _rewardTokenAmount, balanceUT() - _underlyingTokenOldBalance); } + /** + * @inheritdoc IVault + */ + function addStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { + _addStrategy(_strategyHash); + } + + /** + * @inheritdoc IVault + */ + function removeStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { + _removeStrategy(_strategyHash); + } + //===Public view functions===// /** @@ -506,17 +520,6 @@ contract Vault is ); } - /** - * @inheritdoc IVault - */ - function getNextBestInvestStrategy() public view override returns (DataTypes.StrategyStep[] memory) { - return - IRiskManager(registryContract.getRiskManager()).getBestStrategy( - uint256(uint8(vaultConfiguration >> 240)), - underlyingTokensHash - ); - } - /** * @inheritdoc IVault */ From d3052e4dc461f50073470ffb26038a241cfc643d Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Fri, 30 Sep 2022 17:13:16 +0300 Subject: [PATCH 07/44] feat(test): tests for multi-strat | WIP --- test/test-opty/4_vault_multi_strategy.spec.ts | 462 ++++++++++++++++++ 1 file changed, 462 insertions(+) create mode 100644 test/test-opty/4_vault_multi_strategy.spec.ts diff --git a/test/test-opty/4_vault_multi_strategy.spec.ts b/test/test-opty/4_vault_multi_strategy.spec.ts new file mode 100644 index 00000000..ae696a3f --- /dev/null +++ b/test/test-opty/4_vault_multi_strategy.spec.ts @@ -0,0 +1,462 @@ +import chai, { expect } from "chai"; +import { deployments, ethers } from "hardhat"; +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; +import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; +import BN from "bignumber.js"; +import { + Registry, + StrategyRegistry, + Vault, + IAdapterFull, + ERC20, + Registry__factory, + Vault__factory, + StrategyRegistry__factory, + ERC20__factory, + StrategyProvider, + RiskManager, + RiskManager__factory, + StrategyProvider__factory, + IComptroller__factory, + CurveSwapPoolAdapter, + CurveSwapETHGateway, + CurveSwapPoolAdapter__factory, + CurveSwapETHGateway__factory, +} from "../../typechain"; +import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; +import { generateStrategyHashV2, generateTokenHashV2 } from "../../helpers/helpers"; +import { setTokenBalanceInStorage } from "./utils"; +import { Signers, to_10powNumber_BN } from "../../helpers/utils"; +import { BigNumber } from "ethers"; +import { TypedTokens } from "../../helpers/data"; +import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; +import { formatUnits, getAddress } from "ethers/lib/utils"; + +const fork = process.env.FORK as eEVMNetwork; +const DEBUG = process.env.DEBUG === "true" ? true : false; + +describe("Multi-Strategy Vault", () => { + before(async function () { + await deployments.fixture(); + const registryProxyAddress = (await deployments.get("RegistryProxy")).address; + this.signers = {} as Signers; + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.deployer = signers[0]; + this.signers.admin = signers[1]; + this.signers.alice = signers[3]; + this.signers.bob = signers[4]; + this.signers.eve = signers[10]; + this.signers.operator = signers[8]; + this.signers.financeOperator = signers[5]; + this.signers.strategyOperator = signers[7]; + this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); + const registryProxy = await deployments.get("RegistryProxy"); + const riskManagerProxy = await deployments.get("RiskManagerProxy"); + const strategyProvider = await deployments.get("StrategyProvider"); + this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxy.address); + this.riskManager = await ethers.getContractAt(RiskManager__factory.abi, riskManagerProxy.address); + this.strategyProvider = ( + await ethers.getContractAt(StrategyProvider__factory.abi, strategyProvider.address) + ); + + this.strategyRegistry = ( + await new StrategyRegistry__factory(this.deployer).deploy(this.registry.address) + ); + await this.registry.setStrategyRegistry(this.strategyRegistry.address); + + this.vaults = {}; + this.tokens = {}; + const governanceAddress = await this.registry.getGovernance(); + this.signers.governance = await ethers.getSigner(governanceAddress); + const governance = await ethers.getSigner(governanceAddress); + const financeOperatorAddress = await this.registry.getFinanceOperator(); + const financeOperator = await ethers.getSigner(financeOperatorAddress); + for (const riskProfile of Object.keys(MultiChainVaults[fork])) { + this.vaults[riskProfile] = {}; + for (const token of Object.keys(MultiChainVaults[fork][riskProfile])) { + this.vaults[riskProfile][token] = ( + await ethers.getContractAt( + Vault__factory.abi, + ( + await deployments.get(MultiChainVaults[fork][riskProfile][token].symbol) + ).address, + ) + ); + expect(await this.vaults[riskProfile][token].symbol()).to.eq(MultiChainVaults[fork][riskProfile][token].symbol); + expect(await this.vaults[riskProfile][token].name()).to.eq(MultiChainVaults[fork][riskProfile][token].name); + expect(getAddress(await this.vaults[riskProfile][token].underlyingToken())).to.eq( + getAddress(MultiChainVaults[fork][riskProfile][token].underlyingToken), + ); + expect(await this.vaults[riskProfile][token].underlyingTokensHash()).to.eq( + MultiChainVaults[fork][riskProfile][token].underlyingTokensHash, + ); + let tx = await this.vaults[riskProfile][token] + .connect(governance) + .setVaultConfiguration(MultiChainVaults[fork][riskProfile][token].vaultConfig); + await tx.wait(1); + tx = await this.vaults[riskProfile][token] + .connect(financeOperator) + .setValueControlParams( + MultiChainVaults[fork][riskProfile][token].userDepositCapUT, + MultiChainVaults[fork][riskProfile][token].minimumDepositValueUT, + MultiChainVaults[fork][riskProfile][token].totalValueLockedLimitUT, + ); + await tx.wait(1); + this.tokens[token] = ( + await ethers.getContractAt(ERC20__factory.abi, MULTI_CHAIN_VAULT_TOKENS[fork][token].address) + ); + let _userDepositInDecimals = await this.vaults[riskProfile][token].minimumDepositValueUT(); + const decimals = await this.tokens[token].decimals(); + if (_userDepositInDecimals.eq(BigNumber.from("0"))) { + _userDepositInDecimals = BigNumber.from("10").pow(decimals); + } + const _userDeposit = new BN(_userDepositInDecimals.toString()).div( + new BN(to_10powNumber_BN(await this.vaults[riskProfile][token].decimals()).toString()), + ); + await setTokenBalanceInStorage( + this.tokens[token], + this.signers.alice.address, + _userDeposit.multipliedBy("3").toString(), + ); + await setTokenBalanceInStorage( + this.tokens[token], + this.signers.bob.address, + _userDeposit.multipliedBy("3").toString(), + ); + } + } + if (fork == eEVMNetwork.mainnet) { + this.tokens["WETH"] = await ethers.getContractAt(ERC20__factory.abi, TypedTokens["WETH"]); + } + }); + + describe("Multi-Vault Strategies", () => { + for (const riskProfile of Object.keys(StrategiesByTokenByChain[fork])) { + for (const token of Object.keys(StrategiesByTokenByChain[fork][riskProfile])) { + describe(`${riskProfile}-${token}`, () => { + before(async function () { + const strategyOperatorAddress = await this.registry.getStrategyOperator(); + const strategyOperator = await ethers.getSigner(strategyOperatorAddress); + //set all strategies/strategy steps in the StrategyRegistry + //additionally set all strategies as avaiable in the vault, with some buffers + for (const strategy of Object.keys(StrategiesByTokenByChain[fork][riskProfile][token])) { + const strategyDetail = StrategiesByTokenByChain[fork][riskProfile][token][strategy]; + const tokenHash = generateTokenHashV2([strategyDetail.token], NETWORKS_CHAIN_ID_HEX[fork]); + const strategyHash = generateStrategyHashV2(strategyDetail.strategy, tokenHash); + const lastPool = strategyDetail.strategy[strategyDetail.strategy.length - 1].contract; + const steps = strategyDetail.strategy.map(item => ({ + pool: item.contract, + outputToken: item.outputToken, + isBorrow: item.isBorrow, + })); + let tx = await this.strategyRegistry.connect(strategyOperator).addStrategySteps(strategyHash, steps); + await tx.wait(1); + tx = await this.vaults[riskProfile][token].connect(strategyOperator).addStrategy(strategyHash); + await tx.wait(1); + + if (fork == eEVMNetwork.mainnet) { + if ( + (await this.registry.getLiquidityPoolToAdapter(lastPool)) == + (await deployments.get("CompoundAdapter")).address + ) { + const comptrollerInstance = await ethers.getContractAt( + IComptroller__factory.abi, + "0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B", + ); + const mintGuardianPaused = await comptrollerInstance.mintGuardianPaused(lastPool); + if (mintGuardianPaused == true) { + console.log("Skipping because Comptroller's mintGuardianPaused is true"); + this.skip(); + } + } + this.curveSwapPoolAdapter = ( + await ethers.getContractAt( + CurveSwapPoolAdapter__factory.abi, + await ( + await deployments.get("CurveSwapPoolAdapter") + ).address, + ) + ); + this.curveSwapEthGateway = ( + await ethers.getContractAt( + CurveSwapETHGateway__factory.abi, + await this.curveSwapPoolAdapter.curveSwapETHGatewayContract(), + ) + ); + if ( + strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV" || + strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" + ) { + DEBUG && console.log("\nLIDO strategy"); + const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); + if (!isConvertToStEth) { + DEBUG && console.log("\nStrategyOperator setting convertToStEth"); + const tx = await this.curveSwapEthGateway.connect(strategyOperator).setConvertToStEth(true); + await tx.wait(1); + } else { + DEBUG && console.log("\nStrategyOperator already set convertToStEth"); + } + } else if ( + strategy === "weth-DEPOSIT-CurveSwapPool-steCRV" || + strategy === "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" + ) { + DEBUG && console.log("\n non-LIDO strategy"); + const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); + if (isConvertToStEth) { + DEBUG && console.log("\nStrategyOperator un-setting convertToStEth"); + const tx = await this.curveSwapEthGateway.connect(strategyOperator).setConvertToStEth(false); + await tx.wait(1); + } else { + DEBUG && console.log("\nStrategyOperator already un-set convertToStEth"); + } + } + } + } + }); + + it(`${riskProfile}-${token} - (first) alice and bob should deposit into Vault successfully`, async function () { + const signers = [this.signers.alice, this.signers.bob]; + if (fork == eEVMNetwork.mainnet) { + await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], this.tokens["WETH"]); + } else { + await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], undefined); + } + }); + }); + } + } + }); +}); + +async function deposit( + signers: SignerWithAddress[], + vaultInstance: Vault, + underlyingTokenInstance: ERC20, + wethTokenInstance: ERC20 | undefined, +) { + let _userDepositInDecimals = await vaultInstance.minimumDepositValueUT(); + const underlyingTokenSymbol = await underlyingTokenInstance.symbol(); + const decimals = await underlyingTokenInstance.decimals(); + if (_userDepositInDecimals.eq(BigNumber.from("0"))) { + _userDepositInDecimals = BigNumber.from("10").pow(decimals); + } + const vaultTokenSymbol = await vaultInstance.symbol(); + if (DEBUG == true) { + console.log("\n"); + console.log("Before user deposit "); + console.log( + `${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(vaultInstance.address), + await underlyingTokenInstance.decimals(), + ), + ); + fork == eEVMNetwork.mainnet && + console.log( + "WETH balance ", + formatUnits( + await (wethTokenInstance as ERC20).balanceOf(vaultInstance.address), + await (wethTokenInstance as ERC20).decimals(), + ), + ); + console.log( + `Alice ${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(signers[0].address), + await underlyingTokenInstance.decimals(), + ), + ); + console.log( + `Bob ${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(signers[1].address), + await underlyingTokenInstance.decimals(), + ), + ); + console.log( + `Alice ${vaultTokenSymbol} balance `, + formatUnits(await vaultInstance.balanceOf(signers[0].address), await vaultInstance.decimals()), + ); + console.log( + `Bob ${vaultTokenSymbol} balance `, + formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), + ); + console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + } + for (let i = 0; i < signers.length; i++) { + const tx1 = await underlyingTokenInstance + .connect(signers[i]) + .approve(vaultInstance.address, _userDepositInDecimals); + await tx1.wait(1); + const _BalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); + const tx2 = await vaultInstance + .connect(signers[i]) + .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", [], []); + await tx2.wait(1); + const _BalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); + expect(_BalanceBefore).gt(_BalanceAfter); + if (DEBUG == true) { + console.log("\n"); + console.log("After user deposit "); + console.log( + `${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(vaultInstance.address), + await underlyingTokenInstance.decimals(), + ), + ); + fork == eEVMNetwork.mainnet && + console.log( + "WETH balance ", + formatUnits( + await (wethTokenInstance as ERC20).balanceOf(vaultInstance.address), + await (wethTokenInstance as ERC20).decimals(), + ), + ); + console.log( + `Alice ${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(signers[0].address), + await underlyingTokenInstance.decimals(), + ), + ); + console.log( + `Bob ${underlyingTokenSymbol} balance `, + formatUnits( + await underlyingTokenInstance.balanceOf(signers[1].address), + await underlyingTokenInstance.decimals(), + ), + ); + console.log( + `Alice ${vaultTokenSymbol} balance `, + formatUnits(await vaultInstance.balanceOf(signers[0].address), await vaultInstance.decimals()), + ); + console.log( + `Bob ${vaultTokenSymbol} balance `, + formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), + ); + console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + } + } +} + +// async function withdraw( +// signers: SignerWithAddress[], +// vaultInstance: Vault, +// underlyingTokenInstance: ERC20, +// steps: StrategyStepType[], +// registryInstance: Registry, +// wethTokenInstance: ERC20 | undefined, +// ) { +// const underlyingTokenSymbol = await underlyingTokenInstance.symbol(); +// const vaultTokenSymbol = await vaultInstance.symbol(); +// for (let i = 0; i < signers.length; i++) { +// const userWithdrawBalance = await vaultInstance.balanceOf(signers[i].address); +// const userBalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); +// const poolBalanceBefore = await getLastStrategyStepBalanceLP( +// steps, +// registryInstance, +// vaultInstance, +// underlyingTokenInstance, +// ); +// if (DEBUG == true) { +// console.log("\n"); +// console.log("Before user withdraw "); +// console.log("poolBalanceBefore ", formatEther(poolBalanceBefore)); +// console.log( +// `${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(vaultInstance.address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// fork == eEVMNetwork.mainnet && +// console.log( +// "WETH balance ", +// formatUnits( +// await (wethTokenInstance as ERC20).balanceOf(vaultInstance.address), +// await (wethTokenInstance as ERC20).decimals(), +// ), +// ); +// console.log( +// `Alice ${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(signers[0].address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// console.log( +// `Bob ${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(signers[1].address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// console.log( +// `Alice ${vaultTokenSymbol} balance `, +// formatUnits(await vaultInstance.balanceOf(signers[0].address), await vaultInstance.decimals()), +// ); +// console.log( +// `Bob ${vaultTokenSymbol} balance `, +// formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), +// ); +// console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); +// } +// const tx = await vaultInstance +// .connect(signers[i]) +// .userWithdrawVault(signers[i].address, userWithdrawBalance.mul(3).div(4), [], []); +// await tx.wait(); +// const userBalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); +// const poolBalanceAfter = await getLastStrategyStepBalanceLP( +// steps as StrategyStepType[], +// registryInstance, +// vaultInstance, +// underlyingTokenInstance, +// ); +// expect(userBalanceBefore).lt(userBalanceAfter); +// expect(poolBalanceBefore).gt(poolBalanceAfter); +// if (DEBUG == true) { +// console.log("\n"); +// console.log("After user withdraw "); +// console.log("poolBalanceAfter ", formatEther(poolBalanceAfter)); +// console.log( +// `${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(vaultInstance.address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// fork == eEVMNetwork.mainnet && +// console.log( +// "WETH balance ", +// formatUnits( +// await (wethTokenInstance as ERC20).balanceOf(vaultInstance.address), +// await (wethTokenInstance as ERC20).decimals(), +// ), +// ); +// console.log( +// `Alice ${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(signers[0].address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// console.log( +// `Bob ${underlyingTokenSymbol} balance `, +// formatUnits( +// await underlyingTokenInstance.balanceOf(signers[1].address), +// await underlyingTokenInstance.decimals(), +// ), +// ); +// console.log( +// `Alice ${vaultTokenSymbol} balance `, +// formatUnits(await vaultInstance.balanceOf(signers[0].address), await vaultInstance.decimals()), +// ); +// console.log( +// `Bob ${vaultTokenSymbol} balance `, +// formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), +// ); +// console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); +// } +// } +// } From 8f759377a9a677b8393046203e5f7508fcf4d05c Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 4 Oct 2022 15:40:45 +0300 Subject: [PATCH 08/44] feat(strategyregistry): remove all invest strategy registry references --- contracts/protocol/tokenization/Vault.sol | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 78bb5057..34b665f8 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -277,25 +277,25 @@ contract Vault is function vaultDepositToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external onlyStrategyOperator { _checkVaultDeposit(); - IStrategyRegistry investStrategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); //add check for minimum LP returned //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) - _vaultDepositToStrategy(investStrategyRegistry.getStrategySteps(_strategyHash), _depositValueUT); + _vaultDepositToStrategy(strategyRegistry.getStrategySteps(_strategyHash), _depositValueUT); } function vaultWithdrawFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external onlyStrategyOperator { - IStrategyRegistry investStrategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); //add check for minimum USDC returned //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) - _vaultWithdrawSomeFromStrategy(investStrategyRegistry.getStrategySteps(_strategyHash), _withdrawAmountLP); + _vaultWithdrawSomeFromStrategy(strategyRegistry.getStrategySteps(_strategyHash), _withdrawAmountLP); } /** @@ -672,15 +672,14 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - IStrategyRegistry investStrategyRegistry = - IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); for (uint256 i; i < _withdrawStrategies.length; i++) { uint256 _oraAmountLP; bytes32 withdrawStrategyHash = _withdrawStrategies[i]; uint256 buffer = withdrawalBuffers[withdrawStrategyHash]; - DataTypes.StrategyStep[] memory _steps = investStrategyRegistry.getStrategySteps(withdrawStrategyHash); + DataTypes.StrategyStep[] memory _steps = strategyRegistry.getStrategySteps(withdrawStrategyHash); if (buffer >= _expectedStratWithdrawUT) { _oraAmountLP = _steps.getOraSomeValueLP( @@ -1065,7 +1064,7 @@ contract Vault is * @param _strategyHash the hash of the strategy */ function _addStrategy(bytes32 _strategyHash) internal { - require(!strategies.contains(_strategyHash), "InvestStrategyRegistry: strategy already set"); + require(!strategies.contains(_strategyHash), "StrategyRegistry: strategy already set"); strategies.add(_strategyHash); @@ -1077,7 +1076,7 @@ contract Vault is * @param _strategyHash the hash of the strategy */ function _removeStrategy(bytes32 _strategyHash) internal { - require(strategies.contains(_strategyHash), "InvestStrategyRegistry: strategy does not exist"); + require(strategies.contains(_strategyHash), "StrategyRegistry: strategy does not exist"); strategies.remove(_strategyHash); emit RemoveStrategy(_strategyHash); From e8a4d0ed08113cebfe24e212f0caf03a97fe54fc Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 4 Oct 2022 15:43:13 +0300 Subject: [PATCH 09/44] feat(bytecode): remove unrequired internals to save space --- contracts/protocol/tokenization/Vault.sol | 51 +++++------------------ 1 file changed, 11 insertions(+), 40 deletions(-) diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 34b665f8..c914c0e4 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -358,14 +358,16 @@ contract Vault is * @inheritdoc IVault */ function addStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { - _addStrategy(_strategyHash); + strategies.add(_strategyHash); + emit AddStrategy(_strategyHash); } /** * @inheritdoc IVault */ function removeStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { - _removeStrategy(_strategyHash); + strategies.remove(_strategyHash); + emit RemoveStrategy(_strategyHash); } //===Public view functions===// @@ -906,8 +908,14 @@ contract Vault is */ function _oraVaultAndStratValueUT() internal view returns (uint256) { uint256 _totalValue = balanceUT(); + uint256 numStrategies = strategies.length(); + DataTypes.StrategyStep[][] memory _allSteps; - DataTypes.StrategyStep[][] memory _allSteps = _getAllStrategySteps(); + for (uint256 i; i < numStrategies; i++) { + _allSteps[i] = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()).getStrategySteps( + strategies.at(i) + ); + } for (uint256 i; i < _allSteps.length; i++) { _totalValue += _oraStratValueUT(_allSteps[i]); //use SafeMath @@ -1045,43 +1053,6 @@ contract Vault is nonce.increment(); } - function _getAllStrategySteps() internal view returns (DataTypes.StrategyStep[][] memory) { - //perhaps add a check for inactivity of strategy - DataTypes.StrategyStep[][] memory allSteps; - uint256 numStrategies = strategies.length(); - - for (uint256 i; i < numStrategies; i++) { - allSteps[i] = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()).getStrategySteps( - strategies.at(i) - ); - } - - return allSteps; - } - - /** - * @notice adds a strategy hash to vault strategies - * @param _strategyHash the hash of the strategy - */ - function _addStrategy(bytes32 _strategyHash) internal { - require(!strategies.contains(_strategyHash), "StrategyRegistry: strategy already set"); - - strategies.add(_strategyHash); - - emit AddStrategy(_strategyHash); - } - - /** - * @notice removes a strategy hash to vault strategies - * @param _strategyHash the hash of the strategy - */ - function _removeStrategy(bytes32 _strategyHash) internal { - require(strategies.contains(_strategyHash), "StrategyRegistry: strategy does not exist"); - strategies.remove(_strategyHash); - - emit RemoveStrategy(_strategyHash); - } - //===Internal pure functions===// /** From 4b228943ef99b35a63c91b83311742c3850e576e Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 4 Oct 2022 15:46:31 +0300 Subject: [PATCH 10/44] feat(bytecode): remove compute strategy hash function --- contracts/interfaces/opty/IVault.sol | 10 --------- contracts/protocol/tokenization/Vault.sol | 27 +---------------------- 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index 78802a14..5429335b 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -333,16 +333,6 @@ interface IVault { view returns (uint256); - /** - * @dev function to compute the keccak256 hash of the strategy steps - * @param _investStrategySteps metadata for invest strategy - * @return keccak256 hash of the invest strategy and underlying tokens hash - */ - function computeInvestStrategyHash(DataTypes.StrategyStep[] memory _investStrategySteps) - external - view - returns (bytes32); - /** * @dev Emitted when emergency shutdown over vault is changed * @param emergencyShutdown true mean vault is in emergency shutdown mode diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index c914c0e4..f2d294dd 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -539,31 +539,6 @@ contract Vault is ); } - /** - * @inheritdoc IVault - */ - function computeInvestStrategyHash(DataTypes.StrategyStep[] memory _investStrategySteps) - public - view - override - returns (bytes32) - { - if (_investStrategySteps.length > 0) { - bytes32[] memory hashes = new bytes32[](_investStrategySteps.length); - for (uint256 _i; _i < _investStrategySteps.length; _i++) { - hashes[_i] = keccak256( - abi.encodePacked( - _investStrategySteps[_i].pool, - _investStrategySteps[_i].outputToken, - _investStrategySteps[_i].isBorrow - ) - ); - } - return keccak256(abi.encodePacked(underlyingTokensHash, hashes)); - } - return Constants.ZERO_BYTES32; - } - //===Internal functions===// /* solhint-disable avoid-low-level-calls*/ @@ -918,7 +893,7 @@ contract Vault is } for (uint256 i; i < _allSteps.length; i++) { - _totalValue += _oraStratValueUT(_allSteps[i]); //use SafeMath + _totalValue.add(_oraStratValueUT(_allSteps[i])); } return _totalValue; From 3b3528b496953fa4e8f9257ff67a70f53e936a6b Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Tue, 4 Oct 2022 16:20:42 +0300 Subject: [PATCH 11/44] feat(vaultactions): add natspec and check to vaultwithdraw/deposit --- contracts/interfaces/opty/IVault.sol | 24 ++++++++++++++++ contracts/protocol/tokenization/Vault.sol | 35 +++++++++++++++++------ contracts/utils/Errors.sol | 2 ++ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index 5429335b..f1843138 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -165,6 +165,30 @@ interface IVault { */ function vaultDepositAllToStrategy() external; + /** + * @notice deposits underlying tokens to a strategy from the vault + * @param _strategyHash hash of strategy to deposit to + * @param _depositValueUT the amount of underlying token to deposit + * @param _minExpectedLP the minimum amount of LP expected in return for deposit + */ + function vaultDepositToStrategy( + bytes32 _strategyHash, + uint256 _depositValueUT, + uint256 _minExpectedLP + ) external; + + /** + * @notice withdraws LP tokens from a strategy from the vault + * @param _strategyHash hash of the strategy to withdraw from + * @param _withdrawAmountLP the amount of LP to withdraw + * @param _minExpectedUT the minimum amount of underlying tokens expected + */ + function vaultWithdrawFromStrategy( + bytes32 _strategyHash, + uint256 _withdrawAmountLP, + uint256 _minExpectedUT + ) external; + /** * @notice A function to be called in case vault needs to claim and harvest tokens in case a strategy * provides multiple reward tokens diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index f2d294dd..92eb0077 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -274,28 +274,47 @@ contract Vault is } } - function vaultDepositToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external onlyStrategyOperator { + /** + * @inheritdoc IVault + */ + function vaultDepositToStrategy( + bytes32 _strategyHash, + uint256 _depositValueUT, + uint256 _minExpectedLP + ) external override onlyStrategyOperator { _checkVaultDeposit(); IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); - //add check for minimum LP returned - //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) + DataTypes.StrategyStep[] memory steps = strategyRegistry.getStrategySteps(_strategyHash); - _vaultDepositToStrategy(strategyRegistry.getStrategySteps(_strategyHash), _depositValueUT); + //CHECK IF THE TOKEN == POOL + uint256 initialLP = IERC20(steps[steps.length - 1].pool).balanceOf(address(this)); + _vaultDepositToStrategy(steps, _depositValueUT); + uint256 finalLP = IERC20(steps[steps.length - 1].pool).balanceOf(address(this)); + + require(finalLP.sub(initialLP) >= _minExpectedLP, Errors.RETURNED_LP_TOO_FEW); } - function vaultWithdrawFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external onlyStrategyOperator { + /** + * @inheritdoc IVault + */ + function vaultWithdrawFromStrategy( + bytes32 _strategyHash, + uint256 _withdrawAmountLP, + uint256 _minExpectedUT + ) external override onlyStrategyOperator { IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); - //add check for minimum USDC returned - //getBalanceBefore and getBalanceAfter => difference larger than MINEXPECTEDAMOUNT (passed in arg) - + uint256 initialUT = IERC20(underlyingToken).balanceOf(address(this)); _vaultWithdrawSomeFromStrategy(strategyRegistry.getStrategySteps(_strategyHash), _withdrawAmountLP); + uint256 finalUT = IERC20(underlyingToken).balanceOf(address(this)); + + require(finalUT.sub(initialUT) >= _minExpectedUT, Errors.RETURNED_UT_TOO_FEW); } /** diff --git a/contracts/utils/Errors.sol b/contracts/utils/Errors.sol index 75dbc257..8a388d1d 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -32,4 +32,6 @@ library Errors { string public constant BUFFER_EXCEEDED = "27"; string public constant INVALID_STRATEGY = "28"; string public constant WITHDRAWAL_TOO_LARGE = "29"; + string public constant RETURNED_UT_TOO_FEW = "30"; + string public constant RETURNED_LP_TOO_FEW = "31"; } From 64b68a6031e4a832515e397f58e30f7a407250bd Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Wed, 5 Oct 2022 17:48:17 +0300 Subject: [PATCH 12/44] feat(modifiers): new vault-specific modifier contract for bytecode size --- contracts/protocol/tokenization/Modifiers.sol | 70 ++++++++++++ contracts/protocol/tokenization/Vault.sol | 100 +++++++++++------- 2 files changed, 132 insertions(+), 38 deletions(-) create mode 100644 contracts/protocol/tokenization/Modifiers.sol diff --git a/contracts/protocol/tokenization/Modifiers.sol b/contracts/protocol/tokenization/Modifiers.sol new file mode 100644 index 00000000..74b32868 --- /dev/null +++ b/contracts/protocol/tokenization/Modifiers.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; + +// libraries +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; + +// interfaces +import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; +import { IModifiers } from "../earn-protocol-configuration/contracts/interfaces/opty/IModifiers.sol"; + +/** + * @title Modifiers Contract + * @author Opty.fi + * @notice Contract used to keep all the modifiers at one place + * @dev Contract is used throughout the contracts expect registry contract + */ +abstract contract Modifiers is IModifiers { + /** + * @notice Registry contract instance address + */ + IRegistry public registryContract; + + using Address for address; + + constructor(address _registry) internal { + registryContract = IRegistry(_registry); + } + + /** + * @inheritdoc IModifiers + */ + function setRegistry(address _registry) external override { + _onlyOperator(); + require(_registry.isContract(), "!isContract"); + registryContract = IRegistry(_registry); + } + + function _onlyGovernance() internal view { + _onlyAuthorizedUser(registryContract.getGovernance(), "caller is not having governance"); + } + + function _onlyFinanceOperator() internal view { + _onlyAuthorizedUser(registryContract.getFinanceOperator(), "caller is not the financeOperator"); + } + + function _onlyRiskOperator() internal view { + _onlyAuthorizedUser(registryContract.getRiskOperator(), "caller is not the riskOperator"); + } + + function _onlyOperator() internal view { + _onlyAuthorizedUser(registryContract.getOperator(), "caller is not the operator"); + } + + function _onlyStrategyOperator() internal view { + _onlyAuthorizedUser(registryContract.getStrategyOperator(), "caller is not the strategyOperator"); + } + + function _onlyOptyDistributor() internal view { + _onlyAuthorizedUser(registryContract.getOPTYDistributor(), "!optyDistributor"); + } + + function _onlyRegistry() internal view { + _onlyAuthorizedUser(address(registryContract), "!Registry Contract"); + } + + function _onlyAuthorizedUser(address authorizedUserAddress, string memory errorMessage) private view { + require(msg.sender == authorizedUserAddress, errorMessage); + } +} diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 04a43b75..d58ecbbc 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -10,7 +10,7 @@ import { MultiCall } from "../../utils/MultiCall.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { VersionedInitializable } from "../../dependencies/openzeppelin/VersionedInitializable.sol"; import { IncentivisedERC20 } from "./IncentivisedERC20.sol"; -import { Modifiers } from "../earn-protocol-configuration/contracts/Modifiers.sol"; +import { Modifiers } from "./Modifiers.sol"; import { VaultStorageV3 } from "./VaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { EIP712Base } from "../../utils/EIP712Base.sol"; @@ -115,7 +115,7 @@ contract Vault is _setName(string(abi.encodePacked("OptyFi ", _symbol, " ", _riskProfile.name, " Vault"))); _setSymbol(string(abi.encodePacked("op", _symbol, "-", _riskProfile.symbol))); _setDecimals(IncentivisedERC20(underlyingToken).decimals()); - _setWhitelistedAccountsRoot(_whitelistedAccountsRoot); + whitelistedAccountsRoot = _whitelistedAccountsRoot; _setVaultConfiguration(_vaultConfiguration); _setValueControlParams(_userDepositCapUT, _minimumDepositValueUT, _totalValueLockedLimitUT); _domainSeparator = _calculateDomainSeparator(); @@ -124,7 +124,8 @@ contract Vault is /** * @inheritdoc IVault */ - function setName(string calldata _name) external override onlyGovernance { + function setName(string calldata _name) external override { + _onlyGovernance(); _setName(_name); _domainSeparator = _calculateDomainSeparator(); } @@ -132,21 +133,24 @@ contract Vault is /** * @inheritdoc IVault */ - function setSymbol(string calldata _symbol) external override onlyGovernance { + function setSymbol(string calldata _symbol) external override { + _onlyGovernance(); _setSymbol(_symbol); } /** * @inheritdoc IVault */ - function setRiskProfileCode(uint256 _riskProfileCode) external override onlyGovernance { + function setRiskProfileCode(uint256 _riskProfileCode) external override { + _onlyGovernance(); _setRiskProfileCode(_riskProfileCode, registryContract.getRiskProfile(_riskProfileCode).exists); } /** * @inheritdoc IVault */ - function setUnderlyingTokensHash(bytes32 _underlyingTokensHash) external override onlyOperator { + function setUnderlyingTokensHash(bytes32 _underlyingTokensHash) external override { + _onlyOperator(); _setUnderlyingTokensHash(_underlyingTokensHash); } @@ -157,56 +161,63 @@ contract Vault is uint256 _userDepositCapUT, uint256 _minimumDepositValueUT, uint256 _totalValueLockedLimitUT - ) external override onlyFinanceOperator { + ) external override { + _onlyFinanceOperator(); _setValueControlParams(_userDepositCapUT, _minimumDepositValueUT, _totalValueLockedLimitUT); } /** * @inheritdoc IVault */ - function setVaultConfiguration(uint256 _vaultConfiguration) external override onlyGovernance { + function setVaultConfiguration(uint256 _vaultConfiguration) external override { + _onlyGovernance(); _setVaultConfiguration(_vaultConfiguration); } /** * @inheritdoc IVault */ - function setUserDepositCapUT(uint256 _userDepositCapUT) external override onlyFinanceOperator { + function setUserDepositCapUT(uint256 _userDepositCapUT) external override { + _onlyFinanceOperator(); _setUserDepositCapUT(_userDepositCapUT); } /** * @inheritdoc IVault */ - function setMinimumDepositValueUT(uint256 _minimumDepositValueUT) external override onlyFinanceOperator { + function setMinimumDepositValueUT(uint256 _minimumDepositValueUT) external override { + _onlyFinanceOperator(); _setMinimumDepositValueUT(_minimumDepositValueUT); } /** * @inheritdoc IVault */ - function setTotalValueLockedLimitUT(uint256 _totalValueLockedLimitUT) external override onlyFinanceOperator { + function setTotalValueLockedLimitUT(uint256 _totalValueLockedLimitUT) external override { + _onlyFinanceOperator(); _setTotalValueLockedLimitUT(_totalValueLockedLimitUT); } /** * @inheritdoc IVault */ - function setWhitelistedAccountsRoot(bytes32 _whitelistedAccountsRoot) external override onlyGovernance { - _setWhitelistedAccountsRoot(_whitelistedAccountsRoot); + function setWhitelistedAccountsRoot(bytes32 _whitelistedAccountsRoot) external override { + _onlyGovernance(); + whitelistedAccountsRoot = _whitelistedAccountsRoot; } /** * @inheritdoc IVault */ - function setEmergencyShutdown(bool _active) external override onlyGovernance { + function setEmergencyShutdown(bool _active) external override { + _onlyGovernance(); vaultConfiguration = vaultConfiguration & 0xFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (_active) { vaultConfiguration = vaultConfiguration | 0x0100000000000000000000000000000000000000000000000000000000000000; if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAllFromStrategy(investStrategySteps); + _vaultWithdrawAll(); investStrategyHash = Constants.ZERO_BYTES32; delete investStrategySteps; } @@ -217,11 +228,12 @@ contract Vault is /** * @inheritdoc IVault */ - function setUnpaused(bool _unpaused) external override onlyGovernance { + function setUnpaused(bool _unpaused) external override { + _onlyGovernance(); vaultConfiguration = (vaultConfiguration & 0xFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); if (!_unpaused) { if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAllFromStrategy(investStrategySteps); + _vaultWithdrawAll(); investStrategyHash = Constants.ZERO_BYTES32; delete investStrategySteps; } @@ -263,6 +275,7 @@ contract Vault is return _withdrawVaultFor(_receiver, _userWithdrawVT, _expectedOutput, _withdrawStrategies, _accountsProof); } + //NOTE: CHANGE THIS FUNC /** * @inheritdoc IVault */ @@ -280,7 +293,8 @@ contract Vault is bytes32 _strategyHash, uint256 _depositValueUT, uint256 _minExpectedLP - ) external override onlyStrategyOperator { + ) external override { + _onlyStrategyOperator(); _checkVaultDeposit(); IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); @@ -304,7 +318,8 @@ contract Vault is bytes32 _strategyHash, uint256 _withdrawAmountLP, uint256 _minExpectedUT - ) external override onlyStrategyOperator { + ) external override { + _onlyStrategyOperator(); IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); @@ -319,14 +334,16 @@ contract Vault is /** * @inheritdoc IVault */ - function adminCall(bytes[] memory _codes) external override onlyGovernance { + function adminCall(bytes[] memory _codes) external override { + _onlyGovernance(); executeCodes(_codes, Errors.ADMIN_CALL); } /** * @inheritdoc IVault */ - function claimRewardToken(address _liquidityPool) external override onlyStrategyOperator { + function claimRewardToken(address _liquidityPool) external override { + _onlyStrategyOperator(); executeCodes( _liquidityPool.getClaimRewardTokenCode(address(registryContract), payable(address(this))), Errors.CLAIM_REWARD_FAILED @@ -336,7 +353,8 @@ contract Vault is /** * @inheritdoc IVault */ - function harvest(address _rewardToken) external override onlyStrategyOperator { + function harvest(address _rewardToken) external override { + _onlyStrategyOperator(); uint256 _underlyingTokenOldBalance = balanceUT(); uint256 _rewardTokenAmount = IERC20(_rewardToken).balanceOf(address(this)); @@ -385,7 +403,8 @@ contract Vault is /** * @inheritdoc IVault */ - function addStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { + function addStrategy(bytes32 _strategyHash) external override { + _onlyStrategyOperator(); strategies.add(_strategyHash); emit AddStrategy(_strategyHash); } @@ -393,7 +412,8 @@ contract Vault is /** * @inheritdoc IVault */ - function removeStrategy(bytes32 _strategyHash) external override onlyStrategyOperator { + function removeStrategy(bytes32 _strategyHash) external override { + _onlyStrategyOperator(); strategies.remove(_strategyHash); emit RemoveStrategy(_strategyHash); } @@ -782,11 +802,16 @@ contract Vault is } /** - * @dev Internal function to withdraw all investments from current strategy - * @param _investStrategySteps array of strategy step tuple + * @dev Internal function to withdraw all investments */ - function _vaultWithdrawAllFromStrategy(DataTypes.StrategyStep[] memory _investStrategySteps) internal { - _vaultWithdrawSomeFromStrategy(_investStrategySteps, getLastStrategyStepBalanceLP(_investStrategySteps)); + function _vaultWithdrawAll() internal { + IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + for (uint256 i; i < strategies.length(); i++) { + _vaultWithdrawSomeFromStrategy( + strategyRegistry.getStrategySteps(strategies.at(i)), + getLastStrategyStepBalanceLP(strategyRegistry.getStrategySteps(strategies.at(i))) + ); + } } /** @@ -888,14 +913,14 @@ contract Vault is vaultConfiguration = _vaultConfiguration; } - /** - * @dev Internal function to control the allowance of user interaction - * only when vault's whitelistedstate is enabled - * @param _whitelistedAccountsRoot whitelisted accounts root hash - */ - function _setWhitelistedAccountsRoot(bytes32 _whitelistedAccountsRoot) internal { - whitelistedAccountsRoot = _whitelistedAccountsRoot; - } + // /** + // * @dev Internal function to control the allowance of user interaction + // * only when vault's whitelistedstate is enabled + // * @param _whitelistedAccountsRoot whitelisted accounts root hash + // */ + // function _setWhitelistedAccountsRoot(bytes32 _whitelistedAccountsRoot) internal { + // whitelistedAccountsRoot = _whitelistedAccountsRoot; + // } /** * @dev Internal function to configure the vault's value control params @@ -922,10 +947,9 @@ contract Vault is */ function _oraVaultAndStratValueUT() internal view returns (uint256) { uint256 _totalValue = balanceUT(); - uint256 numStrategies = strategies.length(); DataTypes.StrategyStep[][] memory _allSteps; - for (uint256 i; i < numStrategies; i++) { + for (uint256 i; i < strategies.length(); i++) { _allSteps[i] = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()).getStrategySteps( strategies.at(i) ); From 7d5381941df9a6dc290d69cd93cc8eddf4c4c1a4 Mon Sep 17 00:00:00 2001 From: NouDaimon Date: Thu, 6 Oct 2022 15:47:53 +0300 Subject: [PATCH 13/44] feat(vault): add checks, remove unneeded libraries, remove unneeded typecast --- contracts/interfaces/opty/IVault.sol | 5 +-- .../protocol/earn-protocol-configuration | 2 +- contracts/protocol/tokenization/Vault.sol | 31 +++++++++---------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index b0cdfd16..ce22e4ab 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -156,9 +156,10 @@ interface IVault { ) external returns (uint256); /** - * @notice function to deposit whole balance of underlying token to current strategy + * @notice function to deposit whole balance of underlying token to strategy + * @param _strategyHash hash of strategy */ - function vaultDepositAllToStrategy() external; + function vaultDepositAllToStrategy(bytes32 _strategyHash) external; /** * @notice deposits underlying tokens to a strategy from the vault diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index b98f8f30..527f110d 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit b98f8f30ed024699625041e1216f35c12a4d313a +Subproject commit 527f110da7f566e9779a0fcd20c6d3718c68a6b5 diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index d58ecbbc..61354fab 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -21,7 +21,6 @@ import { Constants } from "../../utils/Constants.sol"; import { Errors } from "../../utils/Errors.sol"; import { StrategyManager } from "../lib/StrategyManager.sol"; import { MerkleProof } from "@openzeppelin/contracts/cryptography/MerkleProof.sol"; -import { Counters } from "@openzeppelin/contracts/utils/Counters.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; // interfaces @@ -54,7 +53,6 @@ contract Vault is using Address for address; using StrategyManager for address; using StrategyManager for DataTypes.StrategyStep[]; - using Counters for Counters.Counter; using EnumerableSet for EnumerableSet.Bytes32Set; /** @@ -275,14 +273,16 @@ contract Vault is return _withdrawVaultFor(_receiver, _userWithdrawVT, _expectedOutput, _withdrawStrategies, _accountsProof); } - //NOTE: CHANGE THIS FUNC /** * @inheritdoc IVault */ - function vaultDepositAllToStrategy() external override { + function vaultDepositAllToStrategy(bytes32 _strategyHash) external override { _checkVaultDeposit(); if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultDepositToStrategy(investStrategySteps, balanceUT()); + _vaultDepositToStrategy( + IStrategyRegistry(registryContract.getStrategyRegistry()).getStrategySteps(_strategyHash), + balanceUT() + ); } } @@ -297,7 +297,7 @@ contract Vault is _onlyStrategyOperator(); _checkVaultDeposit(); - IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); @@ -320,7 +320,7 @@ contract Vault is uint256 _minExpectedUT ) external override { _onlyStrategyOperator(); - IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); @@ -405,6 +405,10 @@ contract Vault is */ function addStrategy(bytes32 _strategyHash) external override { _onlyStrategyOperator(); + IRiskManager(registryContract.getRiskManager()).isValidStrategy( + _strategyHash, + (vaultConfiguration >> 240) & 0xFF + ); strategies.add(_strategyHash); emit AddStrategy(_strategyHash); } @@ -714,7 +718,7 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); for (uint256 i; i < _withdrawStrategies.length; i++) { uint256 _oraAmountLP; @@ -805,7 +809,7 @@ contract Vault is * @dev Internal function to withdraw all investments */ function _vaultWithdrawAll() internal { - IStrategyRegistry strategyRegistry = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); for (uint256 i; i < strategies.length(); i++) { _vaultWithdrawSomeFromStrategy( strategyRegistry.getStrategySteps(strategies.at(i)), @@ -947,18 +951,13 @@ contract Vault is */ function _oraVaultAndStratValueUT() internal view returns (uint256) { uint256 _totalValue = balanceUT(); - DataTypes.StrategyStep[][] memory _allSteps; for (uint256 i; i < strategies.length(); i++) { - _allSteps[i] = IStrategyRegistry(IRegistry(registryContract).getStrategyRegistry()).getStrategySteps( - strategies.at(i) + _totalValue += _oraStratValueUT( + IStrategyRegistry(registryContract.getStrategyRegistry()).getStrategySteps(strategies.at(i)) ); } - for (uint256 i; i < _allSteps.length; i++) { - _totalValue.add(_oraStratValueUT(_allSteps[i])); - } - return _totalValue; } From 2232d00e6baf85d85dd4cb34bc4d42f109b51f3e Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 17 Oct 2022 19:47:50 -0400 Subject: [PATCH 14/44] chore(earn-protocol-config): bump up module --- contracts/protocol/earn-protocol-configuration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 527f110d..6a948876 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 527f110da7f566e9779a0fcd20c6d3718c68a6b5 +Subproject commit 6a948876c9c0485f55a5b7152501c4590d42815b From 849a73c3425abdcd8f211bcdede4f11dfedab23e Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Thu, 24 Nov 2022 17:19:05 -0500 Subject: [PATCH 15/44] feat(submodule): update earn-protocol-config --- .gitmodules | 7 ++++--- .../adapters/ethereum/sushiswap-pool-adapter-ethereum | 1 - contracts/protocol/adapters/polygon/apeswap-pool-polygon | 1 - contracts/protocol/adapters/polygon/quickswap-pool-polygon | 1 - .../protocol/adapters/polygon/sushiswap_pools_adapter | 1 - 5 files changed, 4 insertions(+), 7 deletions(-) delete mode 160000 contracts/protocol/adapters/ethereum/sushiswap-pool-adapter-ethereum delete mode 160000 contracts/protocol/adapters/polygon/apeswap-pool-polygon delete mode 160000 contracts/protocol/adapters/polygon/quickswap-pool-polygon delete mode 160000 contracts/protocol/adapters/polygon/sushiswap_pools_adapter diff --git a/.gitmodules b/.gitmodules index 312acfe8..7b37dffb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,9 +37,10 @@ [submodule "contracts/protocol/adapters/swap-adapters"] path = contracts/protocol/adapters/swap-adapters url = git@github.com:Opty-Fi/swap-adapters.git -[submodule "contracts/protocol/earn-protocol-configuration"] - path = contracts/protocol/earn-protocol-configuration - url = git@github.com:Opty-Fi/earn-protocol-configuration.git [submodule "contracts/protocol/adapters/ethereum/team-defi-adapters"] path = contracts/protocol/adapters/ethereum/team-defi-adapters url = git@github.com:Opty-Fi/team-defi-adapters.git +[submodule "contracts/protocol/earn-protocol-configuration"] + path = contracts/protocol/earn-protocol-configuration + url = git@github.com:Opty-Fi/earn-protocol-configuration.git + branch = feat/multi-strat diff --git a/contracts/protocol/adapters/ethereum/sushiswap-pool-adapter-ethereum b/contracts/protocol/adapters/ethereum/sushiswap-pool-adapter-ethereum deleted file mode 160000 index cd674098..00000000 --- a/contracts/protocol/adapters/ethereum/sushiswap-pool-adapter-ethereum +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd6740983938b8edcb946390671e3b76d3c15419 diff --git a/contracts/protocol/adapters/polygon/apeswap-pool-polygon b/contracts/protocol/adapters/polygon/apeswap-pool-polygon deleted file mode 160000 index 16a9936c..00000000 --- a/contracts/protocol/adapters/polygon/apeswap-pool-polygon +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 16a9936ccb4ceed0259f125360a18df627807595 diff --git a/contracts/protocol/adapters/polygon/quickswap-pool-polygon b/contracts/protocol/adapters/polygon/quickswap-pool-polygon deleted file mode 160000 index 61c0594d..00000000 --- a/contracts/protocol/adapters/polygon/quickswap-pool-polygon +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 61c0594d654d641ba329a45484e9ece94f46e64c diff --git a/contracts/protocol/adapters/polygon/sushiswap_pools_adapter b/contracts/protocol/adapters/polygon/sushiswap_pools_adapter deleted file mode 160000 index 04537f8c..00000000 --- a/contracts/protocol/adapters/polygon/sushiswap_pools_adapter +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 04537f8cc387cb7d0a808464676b464f44343c63 From a1aaaa4774509cd0bc421367e12e33a249c6a2c7 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 29 Nov 2022 09:56:07 -0500 Subject: [PATCH 16/44] feat(weiroll): add libraries --- .../protocol/tokenization/CommandBuilder.sol | 149 ++++++++++++++++++ contracts/protocol/tokenization/VM.sol | 120 ++++++++++++++ .../protocol/tokenization/VaultWeiroll.sol | 29 ++++ 3 files changed, 298 insertions(+) create mode 100644 contracts/protocol/tokenization/CommandBuilder.sol create mode 100644 contracts/protocol/tokenization/VM.sol create mode 100644 contracts/protocol/tokenization/VaultWeiroll.sol diff --git a/contracts/protocol/tokenization/CommandBuilder.sol b/contracts/protocol/tokenization/CommandBuilder.sol new file mode 100644 index 00000000..12bc0a2d --- /dev/null +++ b/contracts/protocol/tokenization/CommandBuilder.sol @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; + +library CommandBuilder { + uint256 constant IDX_VARIABLE_LENGTH = 0x80; + uint256 constant IDX_VALUE_MASK = 0x7f; + uint256 constant IDX_END_OF_ARGS = 0xff; + uint256 constant IDX_USE_STATE = 0xfe; + + function buildInputs( + bytes[] memory state, + bytes4 selector, + bytes32 indices + ) internal view returns (bytes memory ret) { + uint256 count; // Number of bytes in whole ABI encoded message + uint256 free; // Pointer to first free byte in tail part of message + bytes memory stateData; // Optionally encode the current state if the call requires it + + uint256 idx; + + // Determine the length of the encoded data + for (uint256 i; i < 32; ) { + idx = uint8(indices[i]); + if (idx == IDX_END_OF_ARGS) break; + + if (idx & IDX_VARIABLE_LENGTH != 0) { + if (idx == IDX_USE_STATE) { + if (stateData.length == 0) { + stateData = abi.encode(state); + } + count += stateData.length; + } else { + // Add the size of the value, rounded up to the next word boundary, plus space for pointer and length + uint256 arglen = state[idx & IDX_VALUE_MASK].length; + require(arglen % 32 == 0, "Dynamic state variables must be a multiple of 32 bytes"); + count += arglen + 32; + } + } else { + require(state[idx & IDX_VALUE_MASK].length == 32, "Static state variables must be 32 bytes"); + count += 32; + } + free += 32; + ++i; + } + + // Encode it + ret = new bytes(count + 4); + assembly { + mstore(add(ret, 32), selector) + } + count = 0; + for (uint256 i; i < 32; ) { + idx = uint8(indices[i]); + if (idx == IDX_END_OF_ARGS) break; + + if (idx & IDX_VARIABLE_LENGTH != 0) { + if (idx == IDX_USE_STATE) { + assembly { + mstore(add(add(ret, 36), count), free) + } + memcpy(stateData, 32, ret, free + 4, stateData.length - 32); + free += stateData.length - 32; + } else { + uint256 arglen = state[idx & IDX_VALUE_MASK].length; + + // Variable length data; put a pointer in the slot and write the data at the end + assembly { + mstore(add(add(ret, 36), count), free) + } + memcpy(state[idx & IDX_VALUE_MASK], 0, ret, free + 4, arglen); + free += arglen; + } + } else { + // Fixed length data; write it directly + bytes memory statevar = state[idx & IDX_VALUE_MASK]; + assembly { + mstore(add(add(ret, 36), count), mload(add(statevar, 32))) + } + } + count += 32; + ++i; + } + } + + function writeOutputs( + bytes[] memory state, + bytes1 index, + bytes memory output + ) internal pure returns (bytes[] memory) { + uint256 idx = uint8(index); + if (idx == IDX_END_OF_ARGS) return state; + + if (idx & IDX_VARIABLE_LENGTH != 0) { + if (idx == IDX_USE_STATE) { + state = abi.decode(output, (bytes[])); + } else { + // Check the first field is 0x20 (because we have only a single return value) + uint256 argptr; + assembly { + argptr := mload(add(output, 32)) + } + require(argptr == 32, "Only one return value permitted (variable)"); + + assembly { + // Overwrite the first word of the return data with the length - 32 + mstore(add(output, 32), sub(mload(output), 32)) + // Insert a pointer to the return data, starting at the second word, into state + mstore(add(add(state, 32), mul(and(idx, IDX_VALUE_MASK), 32)), add(output, 32)) + } + } + } else { + // Single word + require(output.length == 32, "Only one return value permitted (static)"); + + state[idx & IDX_VALUE_MASK] = output; + } + + return state; + } + + function writeTuple( + bytes[] memory state, + bytes1 index, + bytes memory output + ) internal view { + uint256 idx = uint256(uint8(index)); + if (idx == IDX_END_OF_ARGS) return; + + bytes memory entry = state[idx] = new bytes(output.length + 32); + memcpy(output, 0, entry, 32, output.length); + assembly { + let l := mload(output) + mstore(add(entry, 32), l) + } + } + + function memcpy( + bytes memory src, + uint256 srcidx, + bytes memory dest, + uint256 destidx, + uint256 len + ) internal view { + assembly { + pop(staticcall(gas(), 4, add(add(src, 32), srcidx), len, add(add(dest, 32), destidx), len)) + } + } +} diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol new file mode 100644 index 00000000..f4a9a8fa --- /dev/null +++ b/contracts/protocol/tokenization/VM.sol @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; + +import "./CommandBuilder.sol"; + +abstract contract VM { + using CommandBuilder for bytes[]; + + uint256 constant FLAG_CT_DELEGATECALL = 0x00; + uint256 constant FLAG_CT_CALL = 0x01; + uint256 constant FLAG_CT_STATICCALL = 0x02; + uint256 constant FLAG_CT_VALUECALL = 0x03; + uint256 constant FLAG_CT_MASK = 0x03; + uint256 constant FLAG_EXTENDED_COMMAND = 0x80; + uint256 constant FLAG_TUPLE_RETURN = 0x40; + + uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; + + address immutable self; + + constructor() public { + self = address(this); + } + + function _execute(bytes32[] memory commands, bytes[] memory state) internal returns (bytes[] memory) { + bytes32 command; + uint256 flags; + bytes32 indices; + + bool success; + bytes memory outdata; + + uint256 commandsLength = commands.length; + for (uint256 i; i < commandsLength; ) { + command = commands[i]; + flags = uint256(uint8(bytes1(command << 32))); + + if (flags & FLAG_EXTENDED_COMMAND != 0) { + indices = commands[i++]; + } else { + indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL); + } + + if (flags & FLAG_CT_MASK == FLAG_CT_DELEGATECALL) { + (success, outdata) = address(uint160(uint256(command))).delegatecall( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + indices + ) + ); + } else if (flags & FLAG_CT_MASK == FLAG_CT_CALL) { + (success, outdata) = address(uint160(uint256(command))).call( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + indices + ) + ); + } else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) { + (success, outdata) = address(uint160(uint256(command))).staticcall( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + indices + ) + ); + } else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) { + uint256 calleth; + bytes memory v = state[uint8(bytes1(indices))]; + require(v.length == 32, "_execute: value call has no value indicated."); + assembly { + calleth := mload(add(v, 0x20)) + } + (success, outdata) = address(uint160(uint256(command))).call{ value: calleth }( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + bytes32(uint256(indices << 8) | CommandBuilder.IDX_END_OF_ARGS) + ) + ); + } else { + revert("Invalid calltype"); + } + + if (!success) { + if (outdata.length > 0) { + assembly { + outdata := add(outdata, 68) + } + } + revert( + string( + abi.encodePacked( + "Execution Failed : command_index=", + string("0"), + ", target=", + address(uint160(uint256(command))), + ", message=", + outdata.length > 0 ? string(outdata) : "Unknown" + ) + ) + ); + } + + if (flags & FLAG_TUPLE_RETURN != 0) { + state.writeTuple(bytes1(command << 88), outdata); + } else { + state = state.writeOutputs(bytes1(command << 88), outdata); + } + ++i; + } + return state; + } +} diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol new file mode 100644 index 00000000..fb5416bd --- /dev/null +++ b/contracts/protocol/tokenization/VaultWeiroll.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +import "./VM.sol"; +import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; + +contract VaultWeiroll is VM, ERC20 { + // command to + // struct Abc { + // mapping(uint256 => bytes32) commands; + // mapping(uint256 => bytes) state; + // } + + // Abc internal abc; + + constructor() public ERC20("Test", "TST") {} + + function userDepositVault(uint256 _amount) external { + // bytes32[] memory _com = new bytes32[](1); + // bytes[] memory _state = new bytes[](1); + // _com[0] = abc.commands[0]; + // _state[0] = abc.state[0]; + bytes32[] memory commands = new bytes32[](1); + bytes[] memory state = new bytes[](1); + _execute(commands, state); + } +} From c8d9207fd8af34947985f8d9cd48b090149a50c8 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 30 Nov 2022 00:16:15 -0500 Subject: [PATCH 17/44] feat(weiroll): test deposit --- contracts/mocks/contracts/TestVault.sol | 4 + contracts/protocol/tokenization/VM.sol | 70 +++ .../protocol/tokenization/VaultWeiroll.sol | 151 +++++- package.json | 3 +- test/test-opty/vaultweiroll.spec.ts | 126 +++++ yarn.lock | 450 ++++++++++++++++++ 6 files changed, 790 insertions(+), 14 deletions(-) create mode 100644 test/test-opty/vaultweiroll.spec.ts diff --git a/contracts/mocks/contracts/TestVault.sol b/contracts/mocks/contracts/TestVault.sol index 970847e5..6976155f 100644 --- a/contracts/mocks/contracts/TestVault.sol +++ b/contracts/mocks/contracts/TestVault.sol @@ -49,4 +49,8 @@ contract TestVault { uint256 _depositFee = _vault.calcDepositFeeUT(_valueUT); return _vault.userDepositPermitted(address(this), true, _valueUT.sub(_depositFee), _depositFee, _accountsProof); } + + function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { + return _arg; + } } diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index f4a9a8fa..d73beb52 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -4,6 +4,8 @@ pragma solidity ^0.6.12; import "./CommandBuilder.sol"; +import "hardhat/console.sol"; + abstract contract VM { using CommandBuilder for bytes[]; @@ -117,4 +119,72 @@ abstract contract VM { } return state; } + + function _readExecute(bytes32[] memory commands, bytes[] memory state) internal view returns (bytes[] memory) { + bytes32 command; + uint256 flags; + bytes32 indices; + + bool success; + bytes memory outdata; + + uint256 commandsLength = commands.length; + for (uint256 i; i < commandsLength; ) { + command = commands[i]; + flags = uint256(uint8(bytes1(command << 32))); + + if (flags & FLAG_EXTENDED_COMMAND != 0) { + indices = commands[i++]; + } else { + indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL); + } + + if (flags & FLAG_CT_MASK == FLAG_CT_DELEGATECALL) { + revert("FLAG_CT_DELEGATECALL"); + } else if (flags & FLAG_CT_MASK == FLAG_CT_CALL) { + revert("FLAG_CT_CALL"); + } else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) { + (success, outdata) = address(uint160(uint256(command))).staticcall( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + indices + ) + ); + } else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) { + revert("FLAG_CT_VALUECALL"); + } else { + revert("Invalid calltype"); + } + + if (!success) { + if (outdata.length > 0) { + assembly { + outdata := add(outdata, 68) + } + } + revert( + string( + abi.encodePacked( + "Execution Failed : command_index=", + string("0"), + ", target=", + address(uint160(uint256(command))), + ", message=", + outdata.length > 0 ? string(outdata) : "Unknown" + ) + ) + ); + } + + if (flags & FLAG_TUPLE_RETURN != 0) { + state.writeTuple(bytes1(command << 88), outdata); + } else { + state = state.writeOutputs(bytes1(command << 88), outdata); + } + ++i; + } + return state; + } } diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol index fb5416bd..0c1f7788 100644 --- a/contracts/protocol/tokenization/VaultWeiroll.sol +++ b/contracts/protocol/tokenization/VaultWeiroll.sol @@ -4,26 +4,151 @@ pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; import "./VM.sol"; +import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { Constants } from "../../utils/Constants.sol"; + +import "hardhat/console.sol"; contract VaultWeiroll is VM, ERC20 { + using SafeMath for uint256; + using SafeERC20 for IERC20; // command to - // struct Abc { - // mapping(uint256 => bytes32) commands; - // mapping(uint256 => bytes) state; - // } + struct Inputs { + bytes32[] commands; + bytes[] state; + } + + Inputs internal _oraValueUT; + Inputs internal _lastStepBalanceLP; + Inputs internal _depositToStrategy; - // Abc internal abc; + ERC20 public underlyingToken; - constructor() public ERC20("Test", "TST") {} + constructor( + ERC20 _underlyingToken, + string memory _name, + string memory _symbol + ) public ERC20(_name, _symbol) { + underlyingToken = _underlyingToken; + _setupDecimals(underlyingToken.decimals()); + } + + function setOraValueUT(bytes32[] calldata commands, bytes[] memory state) external { + delete _oraValueUT.commands; + delete _oraValueUT.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _oraValueUT.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _oraValueUT.commands[_i] = commands[_i]; + } + _oraValueUT.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _oraValueUT.state[_i] = state[_i]; + } + } + + function setLastStepBalanceLP(bytes32[] calldata commands, bytes[] memory state) external { + delete _lastStepBalanceLP.commands; + delete _lastStepBalanceLP.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _lastStepBalanceLP.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _lastStepBalanceLP.commands[_i] = commands[_i]; + } + _lastStepBalanceLP.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _lastStepBalanceLP.state[_i] = state[_i]; + } + } + + function setDepositToStrategy(bytes32[] calldata commands, bytes[] memory state) external { + delete _depositToStrategy.commands; + delete _depositToStrategy.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _depositToStrategy.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _depositToStrategy.commands[_i] = commands[_i]; + } + _depositToStrategy.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _depositToStrategy.state[_i] = state[_i]; + } + } function userDepositVault(uint256 _amount) external { - // bytes32[] memory _com = new bytes32[](1); - // bytes[] memory _state = new bytes[](1); - // _com[0] = abc.commands[0]; - // _state[0] = abc.state[0]; - bytes32[] memory commands = new bytes32[](1); - bytes[] memory state = new bytes[](1); - _execute(commands, state); + underlyingToken.transferFrom(msg.sender, address(this), _amount); + bytes[] memory _ut = _readExecute(_oraValueUT.commands, _oraValueUT.state); + uint256 _oraVaultAndStratValuePreDepositUT = abi.decode(_ut[0], (uint256)); + uint256 _mintAmount; + if (_oraVaultAndStratValuePreDepositUT == 0 || totalSupply() == 0) { + _mintAmount = _amount; + } else { + _mintAmount = (_amount.mul(totalSupply())).div(_oraVaultAndStratValuePreDepositUT); + } + _mint(msg.sender, _mintAmount); + } + + function readFn() external view returns (uint256) { + bytes[] memory _c = _readExecute(_oraValueUT.commands, _oraValueUT.state); + uint256 _cLen = _c.length; + console.log("_c length ", _cLen); + for (uint256 _i; _i < _cLen; _i++) { + console.log("_i=", _i); + console.logBytes(_c[_i]); + } + return abi.decode(_c[0], (uint256)); + } + + function getPPS() external view returns (uint256) { + // read the last strategy step's balance + bytes[] memory _bal = _readExecute(_lastStepBalanceLP.commands, _lastStepBalanceLP.state); + uint256 _lastStepBalance = abi.decode(_bal[0], (uint256)); + uint256 _cashBalance = underlyingToken.balanceOf(address(this)); + // if the last strategt step's balance > 0 then execute lastStepBalanceLP + if (_lastStepBalance > 0) { + // get amount in token + bytes[] memory _amt = _readExecute(_oraValueUT.commands, _oraValueUT.state); + uint256 _amountInToken = abi.decode(_amt[0], (uint256)); + return (_amountInToken.add(_cashBalance)).mul(Constants.WEI_DECIMAL).div(totalSupply()); + } else { + return _cashBalance.mul(Constants.WEI_DECIMAL).div(totalSupply()); + } + } + + function getLastStrategyStepBalance() external view returns (uint256) { + bytes[] memory _bal = _readExecute(_lastStepBalanceLP.commands, _lastStepBalanceLP.state); + uint256 _cLen = _bal.length; + console.log("_bal length ", _cLen); + for (uint256 _i; _i < _cLen; _i++) { + console.log("_i=", _i); + console.logBytes(_bal[_i]); + } + return abi.decode(_bal[0], (uint256)); + } + + function vaultDepositAllToStrategy() external { + _execute(_depositToStrategy.commands, _depositToStrategy.state); + } + + function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, "E1"); + for (uint256 _i; _i < _tokens.length; _i++) { + _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); + } + } + + function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, "E2"); + for (uint256 _i; _i < _tokens.length; _i++) { + _tokens[_i].safeApprove(_spenders[_i], 0); + } } } diff --git a/package.json b/package.json index 7e3e91e9..61e6555d 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@types/node": "^16.10.2", "@typescript-eslint/eslint-plugin": "^4.32.0", "@typescript-eslint/parser": "^4.32.0", + "@weiroll/weiroll.js": "^0.3.0", "bignumber.js": "^9.0.2", "chai": "^4.3.4", "commitizen": "^4.2.4", @@ -112,7 +113,7 @@ "test-fast:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/2_vault.spec.ts --deploy-fixture", "test-fast:emergency-brake": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/emergency-brake.spec.ts --deploy-fixture", "test-fast:vault-strategy:polygon": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=polygon NETWORK_NAME=polygon hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", - "test-fast:vault-strategy:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", + "test-fast:vault-strategy:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/vaultweiroll.spec.ts", "test-fast:vault-strategy:avalanche": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=avalanche NETWORK_NAME=avalanche FORK_BLOCK_NUMBER=13936470 hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", "test-fast:integration:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/integration.spec.ts", "test-fast:strategy-manager:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/strategy-manager.spec.ts", diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts new file mode 100644 index 00000000..dbd85e83 --- /dev/null +++ b/test/test-opty/vaultweiroll.spec.ts @@ -0,0 +1,126 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; +import { deployContract, solidity } from "ethereum-waffle"; +import { artifacts, ethers as ethers } from "hardhat"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { Contract as weirollContract, Planner as weirollPlanner } from "@weiroll/weiroll.js"; +import { Signers } from "../../helpers/utils"; +import { + CompoundAdapter, + CompoundAdapter__factory, + ERC20, + ERC20__factory, + ICompound, + ICompound__factory, + TestVault, + VaultWeiroll, +} from "../../typechain"; +import { setTokenBalanceInStorage } from "./utils"; +import { parseUnits } from "ethers/lib/utils"; +import { Contract } from "ethers"; +import chai, { expect } from "chai"; + +chai.use(solidity); + +describe("VaultWeiroll", async function () { + let vaultWeiroll: VaultWeiroll; + let usdc: ERC20; + let cUSDC: ICompound; + let cToken: ERC20; + let compoundAdapter: CompoundAdapter; + let testVault: TestVault; + let compoundContract: Contract; + let cTokenContract: Contract; + let vaultWeirollContract: Contract; + let compoundAdapterContract: Contract; + let testVaultContract: Contract; + let usdcContract: Contract; + before(async function () { + this.signers = {} as Signers; + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.deployer = signers[0]; + this.signers.alice = signers[3]; + vaultWeiroll = ( + await deployContract(this.signers.deployer, await artifacts.readArtifact("VaultWeiroll"), [ + EthereumTokens.PLAIN_TOKENS.USDC, + "USD Coin", + "USDC", + ]) + ); + testVault = await deployContract(this.signers.deployer, await artifacts.readArtifact("TestVault")); + usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + cUSDC = await ethers.getContractAt(ICompound__factory.abi, "0x39AA39c021dfbaE8faC545936693aC917d5E7563"); + cToken = await ethers.getContractAt(ERC20__factory.abi, "0x39AA39c021dfbaE8faC545936693aC917d5E7563"); + compoundAdapter = ( + await ethers.getContractAt(CompoundAdapter__factory.abi, "0x9680624ad6bf5a34ce496a483400585136c575a4") + ); + await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); + await usdc.connect(this.signers.alice).approve(vaultWeiroll.address, parseUnits("20", "6")); + await setTokenBalanceInStorage(cToken, vaultWeiroll.address, "20"); + await vaultWeiroll.connect(this.signers.alice).giveAllowances([usdc.address], [cUSDC.address]); + compoundContract = weirollContract.createContract(cUSDC); + cTokenContract = weirollContract.createContract(cToken); + vaultWeirollContract = weirollContract.createContract(vaultWeiroll); + compoundAdapterContract = weirollContract.createContract(compoundAdapter); + testVaultContract = weirollContract.createContract(testVault); + usdcContract = weirollContract.createContract(usdc); + }); + + it("userDepositVault", async function () { + // oraValueUT planner + const oraValueUTPlanner = new weirollPlanner(); + const lpTokenBalance = oraValueUTPlanner.add(cTokenContract.balanceOf(vaultWeiroll.address).staticcall()); + const amountInToken = oraValueUTPlanner.add( + compoundAdapterContract.getSomeAmountInToken(usdc.address, cUSDC.address, lpTokenBalance).staticcall(), + ); + oraValueUTPlanner.add(testVaultContract.pureFunctionUint256(amountInToken).staticcall()); + await vaultWeiroll.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state); + + // lastStepBalanceLP planner + const lastStepBalanceLPPlanner = new weirollPlanner(); + const lastStepBalance = lastStepBalanceLPPlanner.add(cTokenContract.balanceOf(vaultWeiroll.address).staticcall()); + lastStepBalanceLPPlanner.add(testVaultContract.pureFunctionUint256(lastStepBalance).staticcall()); + await vaultWeiroll.setLastStepBalanceLP( + lastStepBalanceLPPlanner.plan().commands, + lastStepBalanceLPPlanner.plan().state, + ); + + // depositToStrategy planner + const depositToStrategyPlanner = new weirollPlanner(); + const usdcBalance = depositToStrategyPlanner.add(usdcContract.balanceOf(vaultWeiroll.address).staticcall()); + depositToStrategyPlanner.add(compoundContract["mint(uint256)"](usdcBalance)); + await vaultWeiroll.setDepositToStrategy( + depositToStrategyPlanner.plan().commands, + depositToStrategyPlanner.plan().state, + ); + + // user deposit no strategy + await expect(vaultWeiroll.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) + .to.emit(vaultWeiroll, "Transfer") + .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("20", "6")); + + // deposit all to strategy + await vaultWeiroll.connect(this.signers.alice).vaultDepositAllToStrategy(); + + // last strategy step balance + console.log("last strategy step balance ", (await vaultWeiroll.getLastStrategyStepBalance()).toString()); + + // price per full share + console.log("price per full share ", (await vaultWeiroll.getPPS()).toString()); + + // 2. user deposit + await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); + await usdc.connect(this.signers.alice).approve(vaultWeiroll.address, parseUnits("20", "6")); + await expect(vaultWeiroll.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) + .to.emit(vaultWeiroll, "Transfer") + .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("19.556383", "6")); + + // 2. deposit all to strategy + await vaultWeiroll.connect(this.signers.alice).vaultDepositAllToStrategy(); + + // 2. last strategy step balance + console.log("last strategy step balance ", (await vaultWeiroll.getLastStrategyStepBalance()).toString()); + + // 2. price per full share + console.log("price per full share ", (await vaultWeiroll.getPPS()).toString()); + }); +}); diff --git a/yarn.lock b/yarn.lock index e3c95b1f..95e74980 100644 --- a/yarn.lock +++ b/yarn.lock @@ -507,6 +507,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abi@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: bc6962bb6cb854e4d2a4d65b2c49c716477675b131b1363312234bdbb7e19badb7d9ce66f4ca2a70ae2ea84f7123dbc4e300a1bfe5d58864a7eafabc1466627e + languageName: node + linkType: hard + "@ethersproject/abi@npm:^5.6.3": version: 5.6.4 resolution: "@ethersproject/abi@npm:5.6.4" @@ -554,6 +571,21 @@ __metadata: languageName: node linkType: hard +"@ethersproject/abstract-provider@npm:5.7.0, @ethersproject/abstract-provider@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-provider@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 + checksum: 74cf4696245cf03bb7cc5b6cbf7b4b89dd9a79a1c4688126d214153a938126d4972d42c93182198653ce1de35f2a2cad68be40337d4774b3698a39b28f0228a8 + languageName: node + linkType: hard + "@ethersproject/abstract-provider@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/abstract-provider@npm:5.6.1" @@ -595,6 +627,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/abstract-signer@npm:5.7.0, @ethersproject/abstract-signer@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-signer@npm:5.7.0" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: a823dac9cfb761e009851050ebebd5b229d1b1cc4a75b125c2da130ff37e8218208f7f9d1386f77407705b889b23d4a230ad67185f8872f083143e0073cbfbe3 + languageName: node + linkType: hard + "@ethersproject/abstract-signer@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/abstract-signer@npm:5.6.2" @@ -634,6 +679,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/address@npm:5.7.0, @ethersproject/address@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/address@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + checksum: 64ea5ebea9cc0e845c413e6cb1e54e157dd9fc0dffb98e239d3a3efc8177f2ff798cd4e3206cf3660ee8faeb7bef1a47dc0ebef0d7b132c32e61e550c7d4c843 + languageName: node + linkType: hard + "@ethersproject/address@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/address@npm:5.6.1" @@ -665,6 +723,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/base64@npm:5.7.0, @ethersproject/base64@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/base64@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + checksum: 7dd5d734d623582f08f665434f53685041a3d3b334a0e96c0c8afa8bbcaab934d50e5b6b980e826a8fde8d353e0b18f11e61faf17468177274b8e7c69cd9742b + languageName: node + linkType: hard + "@ethersproject/base64@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/base64@npm:5.6.1" @@ -694,6 +761,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/basex@npm:5.7.0, @ethersproject/basex@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/basex@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: 326087b7e1f3787b5fe6cd1cf2b4b5abfafbc355a45e88e22e5e9d6c845b613ffc5301d629b28d5c4d5e2bfe9ec424e6782c804956dff79be05f0098cb5817de + languageName: node + linkType: hard + "@ethersproject/bignumber@npm:5.4.2, @ethersproject/bignumber@npm:>=5.0.0-beta.130, @ethersproject/bignumber@npm:^5.0.7, @ethersproject/bignumber@npm:^5.4.0, @ethersproject/bignumber@npm:^5.4.1, @ethersproject/bignumber@npm:^5.4.2": version: 5.4.2 resolution: "@ethersproject/bignumber@npm:5.4.2" @@ -716,6 +793,17 @@ __metadata: languageName: node linkType: hard +"@ethersproject/bignumber@npm:5.7.0, @ethersproject/bignumber@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bignumber@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + bn.js: ^5.2.1 + checksum: 8c9a134b76f3feb4ec26a5a27379efb4e156b8fb2de0678a67788a91c7f4e30abe9d948638458e4b20f2e42380da0adacc7c9389d05fce070692edc6ae9b4904 + languageName: node + linkType: hard + "@ethersproject/bignumber@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/bignumber@npm:5.6.2" @@ -745,6 +833,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/bytes@npm:5.7.0, @ethersproject/bytes@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bytes@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 66ad365ceaab5da1b23b72225c71dce472cf37737af5118181fa8ab7447d696bea15ca22e3a0e8836fdd8cfac161afe321a7c67d0dde96f9f645ddd759676621 + languageName: node + linkType: hard + "@ethersproject/bytes@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/bytes@npm:5.6.1" @@ -772,6 +869,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/constants@npm:5.7.0, @ethersproject/constants@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/constants@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + checksum: 6d4b1355747cce837b3e76ec3bde70e4732736f23b04f196f706ebfa5d4d9c2be50904a390d4d40ce77803b98d03d16a9b6898418e04ba63491933ce08c4ba8a + languageName: node + linkType: hard + "@ethersproject/constants@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/constants@npm:5.6.1" @@ -817,6 +923,24 @@ __metadata: languageName: node linkType: hard +"@ethersproject/contracts@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/contracts@npm:5.7.0" + dependencies: + "@ethersproject/abi": ^5.7.0 + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + checksum: 6ccf1121cba01b31e02f8c507cb971ab6bfed85706484a9ec09878ef1594a62215f43c4fdef8f4a4875b99c4a800bc95e3be69b1803f8ce479e07634b5a740c0 + languageName: node + linkType: hard + "@ethersproject/contracts@npm:^5.6.0": version: 5.6.2 resolution: "@ethersproject/contracts@npm:5.6.2" @@ -867,6 +991,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/hash@npm:5.7.0, @ethersproject/hash@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hash@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 6e9fa8d14eb08171cd32f17f98cc108ec2aeca74a427655f0d689c550fee0b22a83b3b400fad7fb3f41cf14d4111f87f170aa7905bcbcd1173a55f21b06262ef + languageName: node + linkType: hard + "@ethersproject/hash@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/hash@npm:5.6.1" @@ -923,6 +1064,26 @@ __metadata: languageName: node linkType: hard +"@ethersproject/hdnode@npm:5.7.0, @ethersproject/hdnode@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hdnode@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: bfe5ca2d89a42de73655f853170ef4766b933c5f481cddad709b3aca18823275b096e572f92d1602a052f80b426edde44ad6b9d028799775a7dad4a5bbed2133 + languageName: node + linkType: hard + "@ethersproject/json-wallets@npm:5.4.0, @ethersproject/json-wallets@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/json-wallets@npm:5.4.0" @@ -965,6 +1126,27 @@ __metadata: languageName: node linkType: hard +"@ethersproject/json-wallets@npm:5.7.0, @ethersproject/json-wallets@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/json-wallets@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + aes-js: 3.0.0 + scrypt-js: 3.0.1 + checksum: f583458d22db62efaaf94d38dd243482776a45bf90f9f3882fbad5aa0b8fd288b41eb7c1ff8ec0b99c9b751088e43d6173530db64dd33c59f9d8daa8d7ad5aa2 + languageName: node + linkType: hard + "@ethersproject/keccak256@npm:5.4.0, @ethersproject/keccak256@npm:>=5.0.0-beta.127, @ethersproject/keccak256@npm:^5.0.3, @ethersproject/keccak256@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/keccak256@npm:5.4.0" @@ -985,6 +1167,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/keccak256@npm:5.7.0, @ethersproject/keccak256@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/keccak256@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + js-sha3: 0.8.0 + checksum: ff70950d82203aab29ccda2553422cbac2e7a0c15c986bd20a69b13606ed8bb6e4fdd7b67b8d3b27d4f841e8222cbaccd33ed34be29f866fec7308f96ed244c6 + languageName: node + linkType: hard + "@ethersproject/keccak256@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/keccak256@npm:5.6.1" @@ -1009,6 +1201,13 @@ __metadata: languageName: node linkType: hard +"@ethersproject/logger@npm:5.7.0, @ethersproject/logger@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/logger@npm:5.7.0" + checksum: 075ab2f605f1fd0813f2e39c3308f77b44a67732b36e712d9bc085f22a84aac4da4f71b39bee50fe78da3e1c812673fadc41180c9970fe5e486e91ea17befe0d + languageName: node + linkType: hard + "@ethersproject/networks@npm:5.4.2, @ethersproject/networks@npm:^5.4.0": version: 5.4.2 resolution: "@ethersproject/networks@npm:5.4.2" @@ -1027,6 +1226,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/networks@npm:5.7.1, @ethersproject/networks@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/networks@npm:5.7.1" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 0339f312304c17d9a0adce550edb825d4d2c8c9468c1634c44172c67a9ed256f594da62c4cda5c3837a0f28b7fabc03aca9b492f68ff1fdad337ee861b27bd5d + languageName: node + linkType: hard + "@ethersproject/networks@npm:^5.6.3": version: 5.6.4 resolution: "@ethersproject/networks@npm:5.6.4" @@ -1056,6 +1264,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/pbkdf2@npm:5.7.0, @ethersproject/pbkdf2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/pbkdf2@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + checksum: b895adb9e35a8a127e794f7aadc31a2424ef355a70e51cde10d457e3e888bb8102373199a540cf61f2d6b9a32e47358f9c65b47d559f42bf8e596b5fd67901e9 + languageName: node + linkType: hard + "@ethersproject/properties@npm:5.4.1, @ethersproject/properties@npm:>=5.0.0-beta.131, @ethersproject/properties@npm:^5.0.3, @ethersproject/properties@npm:^5.4.0": version: 5.4.1 resolution: "@ethersproject/properties@npm:5.4.1" @@ -1074,6 +1292,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/properties@npm:5.7.0, @ethersproject/properties@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/properties@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 6ab0ccf0c3aadc9221e0cdc5306ce6cd0df7f89f77d77bccdd1277182c9ead0202cd7521329ba3acde130820bf8af299e17cf567d0d497c736ee918207bbf59f + languageName: node + linkType: hard + "@ethersproject/providers@npm:5.4.5, @ethersproject/providers@npm:^5.4.4, @ethersproject/providers@npm:^5.4.5": version: 5.4.5 resolution: "@ethersproject/providers@npm:5.4.5" @@ -1128,6 +1355,34 @@ __metadata: languageName: node linkType: hard +"@ethersproject/providers@npm:5.7.2": + version: 5.7.2 + resolution: "@ethersproject/providers@npm:5.7.2" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 + bech32: 1.1.4 + ws: 7.4.6 + checksum: 1754c731a5ca6782ae9677f4a9cd8b6246c4ef21a966c9a01b133750f3c578431ec43ec254e699969c4a0f87e84463ded50f96b415600aabd37d2056aee58c19 + languageName: node + linkType: hard + "@ethersproject/random@npm:5.4.0, @ethersproject/random@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/random@npm:5.4.0" @@ -1148,6 +1403,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/random@npm:5.7.0, @ethersproject/random@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/random@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 017829c91cff6c76470852855108115b0b52c611b6be817ed1948d56ba42d6677803ec2012aa5ae298a7660024156a64c11fcf544e235e239ab3f89f0fff7345 + languageName: node + linkType: hard + "@ethersproject/rlp@npm:5.4.0, @ethersproject/rlp@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/rlp@npm:5.4.0" @@ -1168,6 +1433,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/rlp@npm:5.7.0, @ethersproject/rlp@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/rlp@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: bce165b0f7e68e4d091c9d3cf47b247cac33252df77a095ca4281d32d5eeaaa3695d9bc06b2b057c5015353a68df89f13a4a54a72e888e4beeabbe56b15dda6e + languageName: node + linkType: hard + "@ethersproject/rlp@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/rlp@npm:5.6.1" @@ -1200,6 +1475,17 @@ __metadata: languageName: node linkType: hard +"@ethersproject/sha2@npm:5.7.0, @ethersproject/sha2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/sha2@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + hash.js: 1.1.7 + checksum: 09321057c022effbff4cc2d9b9558228690b5dd916329d75c4b1ffe32ba3d24b480a367a7cc92d0f0c0b1c896814d03351ae4630e2f1f7160be2bcfbde435dbc + languageName: node + linkType: hard + "@ethersproject/sha2@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/sha2@npm:5.6.1" @@ -1239,6 +1525,20 @@ __metadata: languageName: node linkType: hard +"@ethersproject/signing-key@npm:5.7.0, @ethersproject/signing-key@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/signing-key@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + bn.js: ^5.2.1 + elliptic: 6.5.4 + hash.js: 1.1.7 + checksum: 8f8de09b0aac709683bbb49339bc0a4cd2f95598f3546436c65d6f3c3a847ffa98e06d35e9ed2b17d8030bd2f02db9b7bd2e11c5cf8a71aad4537487ab4cf03a + languageName: node + linkType: hard + "@ethersproject/signing-key@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/signing-key@npm:5.6.2" @@ -1280,6 +1580,20 @@ __metadata: languageName: node linkType: hard +"@ethersproject/solidity@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/solidity@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 9a02f37f801c96068c3e7721f83719d060175bc4e80439fe060e92bd7acfcb6ac1330c7e71c49f4c2535ca1308f2acdcb01e00133129aac00581724c2d6293f3 + languageName: node + linkType: hard + "@ethersproject/solidity@npm:^5.6.0": version: 5.6.1 resolution: "@ethersproject/solidity@npm:5.6.1" @@ -1316,6 +1630,17 @@ __metadata: languageName: node linkType: hard +"@ethersproject/strings@npm:5.7.0, @ethersproject/strings@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/strings@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 5ff78693ae3fdf3cf23e1f6dc047a61e44c8197d2408c42719fef8cb7b7b3613a4eec88ac0ed1f9f5558c74fe0de7ae3195a29ca91a239c74b9f444d8e8b50df + languageName: node + linkType: hard + "@ethersproject/strings@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/strings@npm:5.6.1" @@ -1361,6 +1686,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/transactions@npm:5.7.0, @ethersproject/transactions@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/transactions@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + checksum: a31b71996d2b283f68486241bff0d3ea3f1ba0e8f1322a8fffc239ccc4f4a7eb2ea9994b8fd2f093283fd75f87bae68171e01b6265261f821369aca319884a79 + languageName: node + linkType: hard + "@ethersproject/transactions@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/transactions@npm:5.6.2" @@ -1400,6 +1742,17 @@ __metadata: languageName: node linkType: hard +"@ethersproject/units@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/units@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 304714f848cd32e57df31bf545f7ad35c2a72adae957198b28cbc62166daa929322a07bff6e9c9ac4577ab6aa0de0546b065ed1b2d20b19e25748b7d475cb0fc + languageName: node + linkType: hard + "@ethersproject/wallet@npm:5.4.0, @ethersproject/wallet@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/wallet@npm:5.4.0" @@ -1446,6 +1799,29 @@ __metadata: languageName: node linkType: hard +"@ethersproject/wallet@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wallet@npm:5.7.0" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/json-wallets": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: a4009bf7331eddab38e3015b5e9101ef92de7f705b00a6196b997db0e5635b6d83561674d46c90c6f77b87c0500fe4a6b0183ba13749efc22db59c99deb82fbd + languageName: node + linkType: hard + "@ethersproject/web@npm:5.4.0, @ethersproject/web@npm:^5.4.0": version: 5.4.0 resolution: "@ethersproject/web@npm:5.4.0" @@ -1472,6 +1848,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/web@npm:5.7.1, @ethersproject/web@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/web@npm:5.7.1" + dependencies: + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 7028c47103f82fd2e2c197ce0eecfacaa9180ffeec7de7845b1f4f9b19d84081b7a48227aaddde05a4aaa526af574a9a0ce01cc0fc75e3e371f84b38b5b16b2b + languageName: node + linkType: hard + "@ethersproject/web@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/web@npm:5.6.1" @@ -1511,6 +1900,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/wordlists@npm:5.7.0, @ethersproject/wordlists@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wordlists@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 30eb6eb0731f9ef5faa44bf9c0c6e950bcaaef61e4d2d9ce0ae6d341f4e2d6d1f4ab4f8880bfce03b7aac4b862fb740e1421170cfbf8e2aafc359277d49e6e97 + languageName: node + linkType: hard + "@gar/promisify@npm:^1.0.1": version: 1.1.2 resolution: "@gar/promisify@npm:1.1.2" @@ -2998,6 +3400,15 @@ __metadata: languageName: node linkType: hard +"@weiroll/weiroll.js@npm:^0.3.0": + version: 0.3.0 + resolution: "@weiroll/weiroll.js@npm:0.3.0" + dependencies: + ethers: ^5.3.1 + checksum: 571d94ed27edb3433d4b4dcae20ee0489130adaf523f74aa54b2681bc601f6557bb5e9d15db626fef8524b43eee5119f46cac6118290fa936c63237d9bbdace5 + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -6625,6 +7036,7 @@ __metadata: "@uniswap/v2-periphery": ^1.1.0-beta.0 "@uniswap/v3-core": ^1.0.1 "@uniswap/v3-periphery": ^1.4.2 + "@weiroll/weiroll.js": ^0.3.0 bignumber.js: ^9.0.2 chai: ^4.3.4 commitizen: ^4.2.4 @@ -7838,6 +8250,44 @@ __metadata: languageName: node linkType: hard +"ethers@npm:^5.3.1": + version: 5.7.2 + resolution: "ethers@npm:5.7.2" + dependencies: + "@ethersproject/abi": 5.7.0 + "@ethersproject/abstract-provider": 5.7.0 + "@ethersproject/abstract-signer": 5.7.0 + "@ethersproject/address": 5.7.0 + "@ethersproject/base64": 5.7.0 + "@ethersproject/basex": 5.7.0 + "@ethersproject/bignumber": 5.7.0 + "@ethersproject/bytes": 5.7.0 + "@ethersproject/constants": 5.7.0 + "@ethersproject/contracts": 5.7.0 + "@ethersproject/hash": 5.7.0 + "@ethersproject/hdnode": 5.7.0 + "@ethersproject/json-wallets": 5.7.0 + "@ethersproject/keccak256": 5.7.0 + "@ethersproject/logger": 5.7.0 + "@ethersproject/networks": 5.7.1 + "@ethersproject/pbkdf2": 5.7.0 + "@ethersproject/properties": 5.7.0 + "@ethersproject/providers": 5.7.2 + "@ethersproject/random": 5.7.0 + "@ethersproject/rlp": 5.7.0 + "@ethersproject/sha2": 5.7.0 + "@ethersproject/signing-key": 5.7.0 + "@ethersproject/solidity": 5.7.0 + "@ethersproject/strings": 5.7.0 + "@ethersproject/transactions": 5.7.0 + "@ethersproject/units": 5.7.0 + "@ethersproject/wallet": 5.7.0 + "@ethersproject/web": 5.7.1 + "@ethersproject/wordlists": 5.7.0 + checksum: b7c08cf3e257185a7946117dbbf764433b7ba0e77c27298dec6088b3bc871aff711462b0621930c56880ff0a7ceb8b1d3a361ffa259f93377b48e34107f62553 + languageName: node + linkType: hard + "ethjs-unit@npm:0.1.6": version: 0.1.6 resolution: "ethjs-unit@npm:0.1.6" From 8c7f37895b0d0693a31c698badfa72f954934c1f Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Thu, 1 Dec 2022 01:06:03 -0500 Subject: [PATCH 18/44] feat(vaultweiroll): test user withdraw and vault withdraw --- .eslintignore | 2 +- .prettierignore | 1 + contracts/protocol/tokenization/VM.sol | 2 - .../protocol/tokenization/VaultWeiroll.sol | 150 ++++++++++++++---- helper-hardhat-config.ts | 2 +- test/test-opty/vaultweiroll.spec.ts | 108 ++++++++++--- 6 files changed, 202 insertions(+), 63 deletions(-) diff --git a/.eslintignore b/.eslintignore index 375261e4..e7c55c66 100644 --- a/.eslintignore +++ b/.eslintignore @@ -12,6 +12,6 @@ deployments/ contracts/protocol/earn-protocol-configuration contracts/protocol/adapters tenderly-actions/ - +helpers/types/ # files .solcover.js diff --git a/.prettierignore b/.prettierignore index f57a8ae8..f1b79f64 100644 --- a/.prettierignore +++ b/.prettierignore @@ -14,6 +14,7 @@ contracts/protocol/earn-protocol-configuration contracts/protocol/adapters **out/ +helpers/types/ # files *.env diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index d73beb52..f57578f9 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -4,8 +4,6 @@ pragma solidity ^0.6.12; import "./CommandBuilder.sol"; -import "hardhat/console.sol"; - abstract contract VM { using CommandBuilder for bytes[]; diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol index 0c1f7788..d4c28fc3 100644 --- a/contracts/protocol/tokenization/VaultWeiroll.sol +++ b/contracts/protocol/tokenization/VaultWeiroll.sol @@ -10,8 +10,6 @@ import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Constants } from "../../utils/Constants.sol"; -import "hardhat/console.sol"; - contract VaultWeiroll is VM, ERC20 { using SafeMath for uint256; using SafeERC20 for IERC20; @@ -22,18 +20,25 @@ contract VaultWeiroll is VM, ERC20 { } Inputs internal _oraValueUT; + Inputs internal _oraValueLP; Inputs internal _lastStepBalanceLP; Inputs internal _depositToStrategy; + Inputs internal _withdrawSomeFromStrategy; + Inputs internal _withdrawAllFromStrategy; + + uint256 internal _cacheOraAmountLP; + uint256 internal _cacheExpectedStratWithdrawUT; - ERC20 public underlyingToken; + IERC20 public underlyingToken; constructor( - ERC20 _underlyingToken, + IERC20 _underlyingToken, + uint8 _decimals, string memory _name, string memory _symbol ) public ERC20(_name, _symbol) { underlyingToken = _underlyingToken; - _setupDecimals(underlyingToken.decimals()); + _setupDecimals(_decimals); } function setOraValueUT(bytes32[] calldata commands, bytes[] memory state) external { @@ -51,6 +56,21 @@ contract VaultWeiroll is VM, ERC20 { } } + function setOraValueLP(bytes32[] calldata commands, bytes[] memory state) external { + delete _oraValueLP.commands; + delete _oraValueLP.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _oraValueLP.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _oraValueLP.commands[_i] = commands[_i]; + } + _oraValueLP.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _oraValueLP.state[_i] = state[_i]; + } + } + function setLastStepBalanceLP(bytes32[] calldata commands, bytes[] memory state) external { delete _lastStepBalanceLP.commands; delete _lastStepBalanceLP.state; @@ -81,10 +101,39 @@ contract VaultWeiroll is VM, ERC20 { } } + function setWithdrawSomeFromStrategy(bytes32[] calldata commands, bytes[] memory state) external { + delete _withdrawSomeFromStrategy.commands; + delete _withdrawSomeFromStrategy.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _withdrawSomeFromStrategy.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _withdrawSomeFromStrategy.commands[_i] = commands[_i]; + } + _withdrawSomeFromStrategy.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _withdrawSomeFromStrategy.state[_i] = state[_i]; + } + } + + function setWithdrawAllFromStrategy(bytes32[] calldata commands, bytes[] memory state) external { + delete _withdrawAllFromStrategy.commands; + delete _withdrawAllFromStrategy.state; + uint256 _cLen = commands.length; + uint256 _sLen = state.length; + _withdrawAllFromStrategy.commands = new bytes32[](_cLen); + for (uint256 _i; _i < _cLen; _i++) { + _withdrawAllFromStrategy.commands[_i] = commands[_i]; + } + _withdrawAllFromStrategy.state = new bytes[](_sLen); + for (uint256 _i; _i < _sLen; _i++) { + _withdrawAllFromStrategy.state[_i] = state[_i]; + } + } + function userDepositVault(uint256 _amount) external { - underlyingToken.transferFrom(msg.sender, address(this), _amount); - bytes[] memory _ut = _readExecute(_oraValueUT.commands, _oraValueUT.state); - uint256 _oraVaultAndStratValuePreDepositUT = abi.decode(_ut[0], (uint256)); + uint256 _oraVaultAndStratValuePreDepositUT = _oraVaultAndStratValueUT(); + underlyingToken.safeTransferFrom(msg.sender, address(this), _amount); uint256 _mintAmount; if (_oraVaultAndStratValuePreDepositUT == 0 || totalSupply() == 0) { _mintAmount = _amount; @@ -94,42 +143,67 @@ contract VaultWeiroll is VM, ERC20 { _mint(msg.sender, _mintAmount); } - function readFn() external view returns (uint256) { - bytes[] memory _c = _readExecute(_oraValueUT.commands, _oraValueUT.state); - uint256 _cLen = _c.length; - console.log("_c length ", _cLen); - for (uint256 _i; _i < _cLen; _i++) { - console.log("_i=", _i); - console.logBytes(_c[_i]); + function userWithdrawVault(uint256 _amount) external { + uint256 _oraUserWithdrawUT = _amount.mul(_oraVaultAndStratValueUT()).div(totalSupply()); + _burn(msg.sender, _amount); + uint256 _vaultValuePreStratWithdrawUT = balanceUT(); + // if vault does not have sufficient UT, we need to withdraw from strategy + if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { + // withdraw UT shortage from strategy + _cacheExpectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); + _cacheOraAmountLP = _getOraSomeValueLP(); + _execute(_withdrawSomeFromStrategy.commands, _withdrawSomeFromStrategy.state); + uint256 _vaultValuePostStratWithdrawUT = balanceUT(); + uint256 _receivedStratWithdrawUT = _vaultValuePostStratWithdrawUT.sub(_vaultValuePreStratWithdrawUT); + + // If slippage occurs, reduce _oraUserWithdrawUT by slippage amount + if (_receivedStratWithdrawUT < _cacheExpectedStratWithdrawUT) { + _oraUserWithdrawUT = _oraUserWithdrawUT.sub( + _cacheExpectedStratWithdrawUT.sub(_receivedStratWithdrawUT) + ); + } } - return abi.decode(_c[0], (uint256)); + underlyingToken.safeTransfer(msg.sender, _oraUserWithdrawUT); + } + + function vaultWithdrawAllFromStrategy() external { + _execute(_withdrawAllFromStrategy.commands, _withdrawAllFromStrategy.state); } function getPPS() external view returns (uint256) { - // read the last strategy step's balance - bytes[] memory _bal = _readExecute(_lastStepBalanceLP.commands, _lastStepBalanceLP.state); - uint256 _lastStepBalance = abi.decode(_bal[0], (uint256)); - uint256 _cashBalance = underlyingToken.balanceOf(address(this)); // if the last strategt step's balance > 0 then execute lastStepBalanceLP - if (_lastStepBalance > 0) { - // get amount in token - bytes[] memory _amt = _readExecute(_oraValueUT.commands, _oraValueUT.state); - uint256 _amountInToken = abi.decode(_amt[0], (uint256)); - return (_amountInToken.add(_cashBalance)).mul(Constants.WEI_DECIMAL).div(totalSupply()); + if (getLastStrategyStepBalance() > 0) { + return _oraVaultAndStratValueUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); } else { - return _cashBalance.mul(Constants.WEI_DECIMAL).div(totalSupply()); + return balanceUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); } } - function getLastStrategyStepBalance() external view returns (uint256) { + function getLastStrategyStepBalance() public view returns (uint256) { bytes[] memory _bal = _readExecute(_lastStepBalanceLP.commands, _lastStepBalanceLP.state); - uint256 _cLen = _bal.length; - console.log("_bal length ", _cLen); - for (uint256 _i; _i < _cLen; _i++) { - console.log("_i=", _i); - console.logBytes(_bal[_i]); - } - return abi.decode(_bal[0], (uint256)); + return abi.decode(_bal[_bal.length - 1], (uint256)); + } + + function balanceUT() public view returns (uint256) { + return underlyingToken.balanceOf(address(this)); + } + + function _oraVaultAndStratValueUT() internal view returns (uint256) { + return _getOraValueUT().add(balanceUT()); + } + + function _oraStratValueUT() internal view returns (uint256) { + return getLastStrategyStepBalance() > 0 ? _getOraValueUT() : 0; + } + + function _getOraValueUT() internal view returns (uint256) { + bytes[] memory _ut = _readExecute(_oraValueUT.commands, _oraValueUT.state); + return abi.decode(_ut[0], (uint256)); + } + + function _getOraSomeValueLP() internal view returns (uint256) { + bytes[] memory _lp = _readExecute(_oraValueLP.commands, _oraValueLP.state); + return abi.decode(_lp[_lp.length - 1], (uint256)); } function vaultDepositAllToStrategy() external { @@ -151,4 +225,12 @@ contract VaultWeiroll is VM, ERC20 { _tokens[_i].safeApprove(_spenders[_i], 0); } } + + function getCacheOraAmountLP() external view returns (uint256) { + return _cacheOraAmountLP; + } + + function getCacheExpectedStratWithdrawUT() external view returns (uint256) { + return _cacheExpectedStratWithdrawUT; + } } diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index 606f0b17..54d99653 100644 --- a/helper-hardhat-config.ts +++ b/helper-hardhat-config.ts @@ -90,7 +90,7 @@ export const NETWORKS_DEFAULT_GAS: iEVMParamsPerNetwork = { }; export const BLOCK_TO_FORK: iEVMParamsPerNetwork = { - [eEVMNetwork.mainnet]: 15876273, + [eEVMNetwork.mainnet]: 16087193, [eEVMNetwork.kovan]: 29962003, [eEVMNetwork.ropsten]: undefined, [eEVMNetwork.hardhat]: undefined, diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts index dbd85e83..2450ba37 100644 --- a/test/test-opty/vaultweiroll.spec.ts +++ b/test/test-opty/vaultweiroll.spec.ts @@ -22,7 +22,7 @@ import chai, { expect } from "chai"; chai.use(solidity); describe("VaultWeiroll", async function () { - let vaultWeiroll: VaultWeiroll; + let vaultWeirollUSDC: VaultWeiroll; let usdc: ERC20; let cUSDC: ICompound; let cToken: ERC20; @@ -39,9 +39,10 @@ describe("VaultWeiroll", async function () { const signers: SignerWithAddress[] = await ethers.getSigners(); this.signers.deployer = signers[0]; this.signers.alice = signers[3]; - vaultWeiroll = ( + vaultWeirollUSDC = ( await deployContract(this.signers.deployer, await artifacts.readArtifact("VaultWeiroll"), [ EthereumTokens.PLAIN_TOKENS.USDC, + "6", "USD Coin", "USDC", ]) @@ -54,12 +55,11 @@ describe("VaultWeiroll", async function () { await ethers.getContractAt(CompoundAdapter__factory.abi, "0x9680624ad6bf5a34ce496a483400585136c575a4") ); await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); - await usdc.connect(this.signers.alice).approve(vaultWeiroll.address, parseUnits("20", "6")); - await setTokenBalanceInStorage(cToken, vaultWeiroll.address, "20"); - await vaultWeiroll.connect(this.signers.alice).giveAllowances([usdc.address], [cUSDC.address]); + await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("20", "6")); + await vaultWeirollUSDC.connect(this.signers.alice).giveAllowances([usdc.address], [cUSDC.address]); compoundContract = weirollContract.createContract(cUSDC); cTokenContract = weirollContract.createContract(cToken); - vaultWeirollContract = weirollContract.createContract(vaultWeiroll); + vaultWeirollContract = weirollContract.createContract(vaultWeirollUSDC); compoundAdapterContract = weirollContract.createContract(compoundAdapter); testVaultContract = weirollContract.createContract(testVault); usdcContract = weirollContract.createContract(usdc); @@ -68,59 +68,117 @@ describe("VaultWeiroll", async function () { it("userDepositVault", async function () { // oraValueUT planner const oraValueUTPlanner = new weirollPlanner(); - const lpTokenBalance = oraValueUTPlanner.add(cTokenContract.balanceOf(vaultWeiroll.address).staticcall()); + const lpTokenBalance = oraValueUTPlanner.add(cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall()); const amountInToken = oraValueUTPlanner.add( compoundAdapterContract.getSomeAmountInToken(usdc.address, cUSDC.address, lpTokenBalance).staticcall(), ); oraValueUTPlanner.add(testVaultContract.pureFunctionUint256(amountInToken).staticcall()); - await vaultWeiroll.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state); + await vaultWeirollUSDC.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state); // lastStepBalanceLP planner const lastStepBalanceLPPlanner = new weirollPlanner(); - const lastStepBalance = lastStepBalanceLPPlanner.add(cTokenContract.balanceOf(vaultWeiroll.address).staticcall()); + const lastStepBalance = lastStepBalanceLPPlanner.add( + cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall(), + ); lastStepBalanceLPPlanner.add(testVaultContract.pureFunctionUint256(lastStepBalance).staticcall()); - await vaultWeiroll.setLastStepBalanceLP( + await vaultWeirollUSDC.setLastStepBalanceLP( lastStepBalanceLPPlanner.plan().commands, lastStepBalanceLPPlanner.plan().state, ); // depositToStrategy planner const depositToStrategyPlanner = new weirollPlanner(); - const usdcBalance = depositToStrategyPlanner.add(usdcContract.balanceOf(vaultWeiroll.address).staticcall()); + const usdcBalance = depositToStrategyPlanner.add(usdcContract.balanceOf(vaultWeirollUSDC.address).staticcall()); depositToStrategyPlanner.add(compoundContract["mint(uint256)"](usdcBalance)); - await vaultWeiroll.setDepositToStrategy( + await vaultWeirollUSDC.setDepositToStrategy( depositToStrategyPlanner.plan().commands, depositToStrategyPlanner.plan().state, ); + // oraValueLP planner + const oraValueLPPlanner = new weirollPlanner(); + const expectedStratWithdrawUT = oraValueLPPlanner.add( + vaultWeirollContract.getCacheExpectedStratWithdrawUT().staticcall(), + ); + const amountInLP = oraValueLPPlanner.add( + compoundAdapterContract + .calculateAmountInLPToken(usdc.address, cUSDC.address, expectedStratWithdrawUT) + .staticcall(), + ); + oraValueLPPlanner.add(testVaultContract.pureFunctionUint256(amountInLP).staticcall()); + await vaultWeirollUSDC.setOraValueLP(oraValueLPPlanner.plan().commands, oraValueLPPlanner.plan().state); + + // withdrawSomeFromStrategy planner + const withdrawSomeFromStrategyPlanner = new weirollPlanner(); + const oraAmountLP = withdrawSomeFromStrategyPlanner.add(vaultWeirollContract.getCacheOraAmountLP().staticcall()); + withdrawSomeFromStrategyPlanner.add(compoundContract["redeem(uint256)"](oraAmountLP)); + await vaultWeirollUSDC.setWithdrawSomeFromStrategy( + withdrawSomeFromStrategyPlanner.plan().commands, + withdrawSomeFromStrategyPlanner.plan().state, + ); + + // withdrawAllFromStrategy planner + const withdrawAllFromStrategyPlanner = new weirollPlanner(); + const lpTokenBalanceAll = withdrawAllFromStrategyPlanner.add( + vaultWeirollContract.getLastStrategyStepBalance().staticcall(), + ); + withdrawAllFromStrategyPlanner.add(compoundContract["redeem(uint256)"](lpTokenBalanceAll)); + await vaultWeirollUSDC.setWithdrawAllFromStrategy( + withdrawAllFromStrategyPlanner.plan().commands, + withdrawAllFromStrategyPlanner.plan().state, + ); + // user deposit no strategy - await expect(vaultWeiroll.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) - .to.emit(vaultWeiroll, "Transfer") + await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) + .to.emit(vaultWeirollUSDC, "Transfer") .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("20", "6")); + // user withdraw no strategy + await expect(vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("10", "6"))) + .to.emit(vaultWeirollUSDC, "Transfer") + .withArgs(this.signers.alice.address, ethers.constants.AddressZero, parseUnits("10", "6")); + expect(await await usdc.balanceOf(this.signers.alice.address)).to.eq(parseUnits("10", "6")); + // deposit all to strategy - await vaultWeiroll.connect(this.signers.alice).vaultDepositAllToStrategy(); + await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); // last strategy step balance - console.log("last strategy step balance ", (await vaultWeiroll.getLastStrategyStepBalance()).toString()); + console.log("last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); // price per full share - console.log("price per full share ", (await vaultWeiroll.getPPS()).toString()); + console.log("price per full share ", (await vaultWeirollUSDC.getPPS()).toString()); // 2. user deposit - await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); - await usdc.connect(this.signers.alice).approve(vaultWeiroll.address, parseUnits("20", "6")); - await expect(vaultWeiroll.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) - .to.emit(vaultWeiroll, "Transfer") - .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("19.556383", "6")); + await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("10", "6")); + await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("10", "6"))) + .to.emit(vaultWeirollUSDC, "Transfer") + .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("10.000001", "6")); // 2. deposit all to strategy - await vaultWeiroll.connect(this.signers.alice).vaultDepositAllToStrategy(); + await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); // 2. last strategy step balance - console.log("last strategy step balance ", (await vaultWeiroll.getLastStrategyStepBalance()).toString()); + console.log("last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); // 2. price per full share - console.log("price per full share ", (await vaultWeiroll.getPPS()).toString()); + console.log("price per full share ", (await vaultWeirollUSDC.getPPS()).toString()); + + // 2. user withdraw + console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); + await expect(await vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("5", "6"))) + .to.emit(usdc, "Transfer") + .withArgs(vaultWeirollUSDC.address, this.signers.alice.address, parseUnits("4.999999", "6")); + console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); + console.log("PPS ", (await vaultWeirollUSDC.getPPS()).toString()); + console.log("Total Supply ", (await vaultWeirollUSDC.totalSupply()).toString()); + console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); + console.log("withdraw all from strategy"); + await vaultWeirollUSDC.vaultWithdrawAllFromStrategy(); + console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); + console.log("PPS ", (await vaultWeirollUSDC.getPPS()).toString()); + console.log("Total Supply ", (await vaultWeirollUSDC.totalSupply()).toString()); + console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); }); }); + +async function setCompoundUSDCStrategy() {} From bd3bc80e6d99e93c20d84ee940922d535dffa288 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 6 Dec 2022 11:58:18 -0500 Subject: [PATCH 19/44] feat(vaultweiroll): add outputindex --- .../protocol/tokenization/VaultWeiroll.sol | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol index d4c28fc3..0eb2119e 100644 --- a/contracts/protocol/tokenization/VaultWeiroll.sol +++ b/contracts/protocol/tokenization/VaultWeiroll.sol @@ -9,6 +9,7 @@ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Constants } from "../../utils/Constants.sol"; +import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; contract VaultWeiroll is VM, ERC20 { using SafeMath for uint256; @@ -17,8 +18,24 @@ contract VaultWeiroll is VM, ERC20 { struct Inputs { bytes32[] commands; bytes[] state; + uint256 outputIndex; } + struct Strategy { + bool enable; + bytes32 hash; + Inputs oraValueUT; + Inputs oraValueLP; + Inputs lastStepBalanceLP; + Inputs depositToStrategy; + Inputs withdrawSomeFromStrategy; + Inputs withdrawAllFromStrategy; + } + + mapping(uint256 => Strategy) public strategies; + + EnumerableSet.Bytes32Set internal strategiesArr; + Inputs internal _oraValueUT; Inputs internal _oraValueLP; Inputs internal _lastStepBalanceLP; @@ -41,7 +58,15 @@ contract VaultWeiroll is VM, ERC20 { _setupDecimals(_decimals); } - function setOraValueUT(bytes32[] calldata commands, bytes[] memory state) external { + function setTemp(uint256 _index, Strategy memory _strategy) external { + strategies[_index] = _strategy; + } + + function setOraValueUT( + bytes32[] calldata commands, + bytes[] memory state, + uint256 _outputIndex + ) external { delete _oraValueUT.commands; delete _oraValueUT.state; uint256 _cLen = commands.length; @@ -54,6 +79,7 @@ contract VaultWeiroll is VM, ERC20 { for (uint256 _i; _i < _sLen; _i++) { _oraValueUT.state[_i] = state[_i]; } + _oraValueUT.outputIndex = _outputIndex; } function setOraValueLP(bytes32[] calldata commands, bytes[] memory state) external { From d90dff87a832427ba2b4d343831b5a245189e218 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 6 Dec 2022 12:00:28 -0500 Subject: [PATCH 20/44] chore(submodules): update earn-protocol-configuration --- contracts/protocol/earn-protocol-configuration | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 6a948876..598b3eb5 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 6a948876c9c0485f55a5b7152501c4590d42815b +Subproject commit 598b3eb5620c888d32feb3355821d0f6ca7a4530 From 67ff73a62bd024b46dc5c3e986b0be1d9995f610 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 6 Dec 2022 23:59:23 -0500 Subject: [PATCH 21/44] feat(vault.sol): weiroll --- .eslintignore | 2 + .prettierignore | 1 + contracts/interfaces/opty/IVault.sol | 76 ++-- .../mocks/contracts/TestEmergencyBrake.sol | 23 +- contracts/mocks/contracts/TestVault.sol | 4 +- .../{tokenization => lib}/CommandBuilder.sol | 9 +- contracts/protocol/tokenization/VM.sol | 16 +- contracts/protocol/tokenization/Vault.sol | 338 ++++++------------ .../protocol/tokenization/VaultStorage.sol | 8 +- .../protocol/tokenization/VaultWeiroll.sol | 6 +- contracts/utils/Errors.sol | 2 +- hardhat.config.ts | 1 - 12 files changed, 159 insertions(+), 327 deletions(-) rename contracts/protocol/{tokenization => lib}/CommandBuilder.sol (96%) diff --git a/.eslintignore b/.eslintignore index e7c55c66..85d334b9 100644 --- a/.eslintignore +++ b/.eslintignore @@ -13,5 +13,7 @@ contracts/protocol/earn-protocol-configuration contracts/protocol/adapters tenderly-actions/ helpers/types/ +deployments/ + # files .solcover.js diff --git a/.prettierignore b/.prettierignore index f1b79f64..17b2e77c 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,6 +15,7 @@ contracts/protocol/earn-protocol-configuration contracts/protocol/adapters **out/ helpers/types/ +deployments/ # files *.env diff --git a/contracts/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index 9869b516..8ef0194f 100644 --- a/contracts/interfaces/opty/IVault.sol +++ b/contracts/interfaces/opty/IVault.sol @@ -107,7 +107,7 @@ interface IVault { * @param _active If true, the Vault goes into Emergency Shutdown. If false, the Vault * goes back into Normal Operation */ - function setEmergencyShutdown(bool _active) external; + function setEmergencyShutdown(bool _active) external payable; /** * @notice activates or deactivates vault mode where all strategies @@ -119,7 +119,7 @@ interface IVault { * @param _unpaused If true, the vault goes into unpause mode. If false(default), the vault * goes into pause mode */ - function setUnpaused(bool _unpaused) external; + function setUnpaused(bool _unpaused) external payable; /** * @notice Deposit underlying tokens to the vault @@ -141,82 +141,49 @@ interface IVault { * @dev Burn the shares right away as per oracle based price per full share value * @param _receiver the address which will receive the underlying tokens * @param _userWithdrawVT amount in vault token - * @param _expectedOutput minimum amount of underlying tokens that must be received - * to not revert transaction - * @param _withdrawStrategies array of strategy hashes to identify which strategies to withdraw from * @param _accountsProof merkle proof for caller */ function userWithdrawVault( address _receiver, uint256 _userWithdrawVT, - uint256 _expectedOutput, - bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof ) external returns (uint256); - /** - * @notice function to deposit whole balance of underlying token to strategy - * @param _strategyHash hash of strategy - */ - function vaultDepositAllToStrategy(bytes32 _strategyHash) external; - /** * @notice deposits underlying tokens to a strategy from the vault * @param _strategyHash hash of strategy to deposit to * @param _depositValueUT the amount of underlying token to deposit - * @param _minExpectedLP the minimum amount of LP expected in return for deposit */ - function vaultDepositToStrategy( - bytes32 _strategyHash, - uint256 _depositValueUT, - uint256 _minExpectedLP - ) external; + function vaultDepositSomeToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external payable; /** * @notice withdraws LP tokens from a strategy from the vault * @param _strategyHash hash of the strategy to withdraw from * @param _withdrawAmountLP the amount of LP to withdraw - * @param _minExpectedUT the minimum amount of underlying tokens expected */ - function vaultWithdrawFromStrategy( - bytes32 _strategyHash, - uint256 _withdrawAmountLP, - uint256 _minExpectedUT - ) external; + function vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external payable; /** * @notice A function to be called in case vault needs to claim and harvest tokens in case a strategy * provides multiple reward tokens - * @param _codes Array of encoded data in bytes which acts as code to execute + * @param _commands list of values that encode single + * operation for the VM to take + * @param _state list of elements to call a smart contract function + * specified in the command via delegatecall */ - function adminCall(bytes[] memory _codes) external; + function adminCall(bytes32[] memory _commands, bytes[] memory _state) external payable; /** * @notice function to claim the whole balance of reward tokens - * @param _liquidityPool Liquidity pool's contract address from where to claim the reward token - */ - function claimRewardToken(address _liquidityPool) external; - - /** - * @notice function to swap the vault's entire balance of reward token for the vault's underlying token - * @param _rewardToken address of the reward token to harvest - * @param _dex swap router - * @param _isUniV3 whether router is uniswapV3 or not - * @param _minimumUnderlyingTokenAmount minimum underlying after swap that must be received - * for the transaction to not revert - * @param _deadline swap deadline - * @param _path token path for uniswapV2 and its forks - * @param _pathUniV3 path for uniswapV3 - */ - function harvest( - address _rewardToken, - address _dex, - bool _isUniV3, - uint256 _minimumUnderlyingTokenAmount, - uint256 _deadline, - address[] memory _path, - bytes memory _pathUniV3 - ) external; + * @param _strategyHash keccak256 hash of the strategy + */ + function claimRewardToken(bytes32 _strategyHash) external payable; + + /** + * @notice function to swap the vault's reward token for the vault's underlying token + * @param _strategyHash keccak256 hash of the strategy + */ + function harvestRewards(bytes32 _strategyHash) external payable; /** * @notice Allow passing a signed message to approve spending @@ -362,13 +329,10 @@ interface IVault { /** * @notice function to compute the balance of lptoken of the vault * in the last step of the strategy - * @param _strategySteps array of strategy step tuple + * @param _strategyHash keccak256 hash of the strategyHash * @return balance in lptoken */ - function getLastStrategyStepBalanceLP(DataTypes.StrategyStep[] memory _strategySteps) - external - view - returns (uint256); + function getLastStrategyStepBalanceLP(bytes32 _strategyHash) external view returns (uint256); /** * @dev Emitted when emergency shutdown over vault is changed diff --git a/contracts/mocks/contracts/TestEmergencyBrake.sol b/contracts/mocks/contracts/TestEmergencyBrake.sol index bc439280..59b61ea9 100644 --- a/contracts/mocks/contracts/TestEmergencyBrake.sol +++ b/contracts/mocks/contracts/TestEmergencyBrake.sol @@ -24,12 +24,8 @@ contract TestEmergencyBrake { vaultAddr.userDepositVault(address(this), _userDepositUT, _permit, _accountProofs); } - function runUserWithdrawVault( - uint256 _userWithdrawVT, - bytes32[] memory _withdrawStrategies, - bytes32[] calldata _accountProofs - ) external { - vaultAddr.userWithdrawVault(address(this), _userWithdrawVT, uint256(0), _withdrawStrategies, _accountProofs); + function runUserWithdrawVault(uint256 _userWithdrawVT, bytes32[] calldata _accountProofs) external { + vaultAddr.userWithdrawVault(address(this), _userWithdrawVT, _accountProofs); } function runTwoTxnUserDepositVault( @@ -42,26 +38,21 @@ contract TestEmergencyBrake { vaultAddr.userDepositVault(address(this), _userDepositUT, _permit, _accountProofs); } - function runTwoTxnUserWithdrawVault( - uint256 _userDepositUT, - bytes32[] memory _withdrawStrategies, - bytes32[] calldata _accountProofs - ) external { + function runTwoTxnUserWithdrawVault(uint256 _userDepositUT, bytes32[] calldata _accountProofs) external { tokenAddr.approve(address(vaultAddr), (_userDepositUT + _userDepositUT)); tokenAddr.transfer(address(vaultAddr), (_userDepositUT + _userDepositUT)); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, uint256(0), _withdrawStrategies, _accountProofs); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, uint256(0), _withdrawStrategies, _accountProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs); } function runTwoTxnDepositAndWithdraw( uint256 _userDepositUT, bytes calldata _permit, - bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountProofs ) external { tokenAddr.approve(address(vaultAddr), _userDepositUT); - vaultAddr.userDepositVault(address(this), _userDepositUT, uint256(0), _permit, _accountProofs); - vaultAddr.userWithdrawVault(address(this), _userDepositUT, uint256(0), _withdrawStrategies, _accountProofs); + vaultAddr.userDepositVault(address(this), _userDepositUT, _permit, _accountProofs); + vaultAddr.userWithdrawVault(address(this), _userDepositUT, _accountProofs); } function runTwoTxnDepositAndTransfer( diff --git a/contracts/mocks/contracts/TestVault.sol b/contracts/mocks/contracts/TestVault.sol index f5a65882..6976155f 100644 --- a/contracts/mocks/contracts/TestVault.sol +++ b/contracts/mocks/contracts/TestVault.sol @@ -28,11 +28,9 @@ contract TestVault { function withdraw( IVault _vault, uint256 _amountVT, - uint256 _expectedOutput, - bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof ) external { - _vault.userWithdrawVault(msg.sender, _amountVT, _expectedOutput, _withdrawStrategies, _accountsProof); + _vault.userWithdrawVault(msg.sender, _amountVT, _accountsProof); } function withdrawERC20(ERC20 _token, address _recipient) external { diff --git a/contracts/protocol/tokenization/CommandBuilder.sol b/contracts/protocol/lib/CommandBuilder.sol similarity index 96% rename from contracts/protocol/tokenization/CommandBuilder.sol rename to contracts/protocol/lib/CommandBuilder.sol index 12bc0a2d..58704569 100644 --- a/contracts/protocol/tokenization/CommandBuilder.sol +++ b/contracts/protocol/lib/CommandBuilder.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; library CommandBuilder { uint256 constant IDX_VARIABLE_LENGTH = 0x80; @@ -12,7 +13,7 @@ library CommandBuilder { bytes[] memory state, bytes4 selector, bytes32 indices - ) internal view returns (bytes memory ret) { + ) public view returns (bytes memory ret) { uint256 count; // Number of bytes in whole ABI encoded message uint256 free; // Pointer to first free byte in tail part of message bytes memory stateData; // Optionally encode the current state if the call requires it @@ -87,7 +88,7 @@ library CommandBuilder { bytes[] memory state, bytes1 index, bytes memory output - ) internal pure returns (bytes[] memory) { + ) public pure returns (bytes[] memory) { uint256 idx = uint8(index); if (idx == IDX_END_OF_ARGS) return state; @@ -123,7 +124,7 @@ library CommandBuilder { bytes[] memory state, bytes1 index, bytes memory output - ) internal view { + ) public view { uint256 idx = uint256(uint8(index)); if (idx == IDX_END_OF_ARGS) return; @@ -141,7 +142,7 @@ library CommandBuilder { bytes memory dest, uint256 destidx, uint256 len - ) internal view { + ) public view { assembly { pop(staticcall(gas(), 4, add(add(src, 32), srcidx), len, add(add(dest, 32), destidx), len)) } diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index f57578f9..edfd4822 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -2,7 +2,7 @@ pragma solidity ^0.6.12; -import "./CommandBuilder.sol"; +import { CommandBuilder } from "../lib/CommandBuilder.sol"; abstract contract VM { using CommandBuilder for bytes[]; @@ -17,13 +17,13 @@ abstract contract VM { uint256 constant SHORT_COMMAND_FILL = 0x000000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; - address immutable self; + // address immutable self; constructor() public { - self = address(this); + // self = address(this); } - function _execute(bytes32[] memory commands, bytes[] memory state) internal returns (bytes[] memory) { + function _writeExecute(bytes32[] memory commands, bytes[] memory state) internal returns (bytes[] memory) { bytes32 command; uint256 flags; bytes32 indices; @@ -137,11 +137,7 @@ abstract contract VM { indices = bytes32(uint256(command << 40) | SHORT_COMMAND_FILL); } - if (flags & FLAG_CT_MASK == FLAG_CT_DELEGATECALL) { - revert("FLAG_CT_DELEGATECALL"); - } else if (flags & FLAG_CT_MASK == FLAG_CT_CALL) { - revert("FLAG_CT_CALL"); - } else if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) { + if (flags & FLAG_CT_MASK == FLAG_CT_STATICCALL) { (success, outdata) = address(uint160(uint256(command))).staticcall( // target // inputs state.buildInputs( @@ -150,8 +146,6 @@ abstract contract VM { indices ) ); - } else if (flags & FLAG_CT_MASK == FLAG_CT_VALUECALL) { - revert("FLAG_CT_VALUECALL"); } else { revert("Invalid calltype"); } diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 086b55ec..998a1b83 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -6,20 +6,20 @@ pragma experimental ABIEncoderV2; //NOTE: Variable Shadowing // helper contracts -import { MultiCall } from "../../utils/MultiCall.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { VersionedInitializable } from "../../dependencies/openzeppelin/VersionedInitializable.sol"; import { IncentivisedERC20 } from "./IncentivisedERC20.sol"; import { Modifiers } from "./Modifiers.sol"; -import { VaultStorageV3 } from "./VaultStorage.sol"; +import { VaultStorageV4 } from "./VaultStorage.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { EIP712Base } from "../../utils/EIP712Base.sol"; +import { VM } from "./VM.sol"; + // libraries import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { DataTypes } from "../earn-protocol-configuration/contracts/libraries/types/DataTypes.sol"; import { Constants } from "../../utils/Constants.sol"; import { Errors } from "../../utils/Errors.sol"; -import { StrategyManager } from "../lib/StrategyManager.sol"; import { MerkleProof } from "@openzeppelin/contracts/cryptography/MerkleProof.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; @@ -31,8 +31,7 @@ import { IVault } from "../../interfaces/opty/IVault.sol"; import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; import { IRiskManager } from "../earn-protocol-configuration/contracts/interfaces/opty/IRiskManager.sol"; import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; -import { IUniswapV2Router01 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router01.sol"; -import { ISwapRouter } from "../../interfaces/uniswap/ISwapRouter.sol"; +import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; /** * @title Vault contract inspired by AAVE V3's AToken.sol @@ -44,22 +43,20 @@ contract Vault is VersionedInitializable, IVault, IncentivisedERC20, - MultiCall, Modifiers, ReentrancyGuard, - VaultStorageV3, - EIP712Base + VaultStorageV4, + EIP712Base, + VM { using SafeERC20 for IERC20; using Address for address; - using StrategyManager for address; - using StrategyManager for DataTypes.StrategyStep[]; using EnumerableSet for EnumerableSet.Bytes32Set; /** * @dev The version of the Vault implementation */ - uint256 public constant opTOKEN_REVISION = 0x6; + uint256 public constant opTOKEN_REVISION = 0x7; /** * @dev hash of the permit function @@ -72,7 +69,7 @@ contract Vault is /* solhint-disable no-empty-blocks */ constructor(address _registry) public - IncentivisedERC20(string(abi.encodePacked("opTOKEN_IMPL")), string(abi.encodePacked("opTOKEN_IMPL"))) + IncentivisedERC20("opTOKEN_IMPL", "opTOKEN_IMPL") EIP712Base() Modifiers(_registry) { @@ -208,18 +205,14 @@ contract Vault is /** * @inheritdoc IVault */ - function setEmergencyShutdown(bool _active) external override { + function setEmergencyShutdown(bool _active) external payable override { _onlyGovernance(); vaultConfiguration = vaultConfiguration & 0xFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (_active) { vaultConfiguration = vaultConfiguration | 0x0100000000000000000000000000000000000000000000000000000000000000; - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAll(); - investStrategyHash = Constants.ZERO_BYTES32; - delete investStrategySteps; - } + _vaultWithdrawFromAllStrategies(); } emit LogEmergencyShutdown((vaultConfiguration & (1 << 248)) != 0, msg.sender); } @@ -227,15 +220,11 @@ contract Vault is /** * @inheritdoc IVault */ - function setUnpaused(bool _unpaused) external override { + function setUnpaused(bool _unpaused) external payable override { _onlyGovernance(); vaultConfiguration = (vaultConfiguration & 0xFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); if (!_unpaused) { - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAll(); - investStrategyHash = Constants.ZERO_BYTES32; - delete investStrategySteps; - } + _vaultWithdrawFromAllStrategies(); } else { vaultConfiguration = 0x0200000000000000000000000000000000000000000000000000000000000000 | @@ -247,11 +236,8 @@ contract Vault is /** * @inheritdoc IVault */ - function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) - external - override - onlyRiskOperator - { + function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external override { + _onlyRiskOperator(); uint256 _tokensLen = _tokens.length; require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); for (uint256 _i; _i < _tokens.length; _i++) { @@ -262,11 +248,8 @@ contract Vault is /** * @inheritdoc IVault */ - function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) - external - override - onlyRiskOperator - { + function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external override { + _onlyRiskOperator(); uint256 _tokensLen = _tokens.length; require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); for (uint256 _i; _i < _tokens.length; _i++) { @@ -295,124 +278,57 @@ contract Vault is function userWithdrawVault( address _receiver, uint256 _userWithdrawVT, - uint256 _expectedOutput, - bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof ) external override nonReentrant returns (uint256) { _emergencyBrake(false); - return _withdrawVaultFor(_receiver, _userWithdrawVT, _expectedOutput, _withdrawStrategies, _accountsProof); - } - - /** - * @inheritdoc IVault - */ - function vaultDepositAllToStrategy(bytes32 _strategyHash) external override { - _checkVaultDeposit(); - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultDepositToStrategy( - IStrategyRegistry(registryContract.getStrategyRegistry()).getStrategySteps(_strategyHash), - balanceUT() - ); - } + return _withdrawVaultFor(_receiver, _userWithdrawVT, _accountsProof); } /** * @inheritdoc IVault */ - function vaultDepositToStrategy( - bytes32 _strategyHash, - uint256 _depositValueUT, - uint256 _minExpectedLP - ) external override { + function vaultDepositSomeToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external payable override { _onlyStrategyOperator(); _checkVaultDeposit(); - - IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - - require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); - - DataTypes.StrategyStep[] memory steps = strategyRegistry.getStrategySteps(_strategyHash); - - //CHECK IF THE TOKEN == POOL - uint256 initialLP = IERC20(steps[steps.length - 1].pool).balanceOf(address(this)); - _vaultDepositToStrategy(steps, _depositValueUT); - uint256 finalLP = IERC20(steps[steps.length - 1].pool).balanceOf(address(this)); - - require(finalLP.sub(initialLP) >= _minExpectedLP, Errors.RETURNED_LP_TOO_FEW); + _vaultDepositSomeToStrategy(_strategyHash, _depositValueUT); } /** * @inheritdoc IVault */ - function vaultWithdrawFromStrategy( - bytes32 _strategyHash, - uint256 _withdrawAmountLP, - uint256 _minExpectedUT - ) external override { + function vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external payable override { _onlyStrategyOperator(); - IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - - require(strategies.contains(_strategyHash), Errors.INVALID_STRATEGY); - - uint256 initialUT = IERC20(underlyingToken).balanceOf(address(this)); - _vaultWithdrawSomeFromStrategy(strategyRegistry.getStrategySteps(_strategyHash), _withdrawAmountLP); - uint256 finalUT = IERC20(underlyingToken).balanceOf(address(this)); - - require(finalUT.sub(initialUT) >= _minExpectedUT, Errors.RETURNED_UT_TOO_FEW); + _vaultWithdrawSomeFromStrategy(_strategyHash, _withdrawAmountLP); } /** * @inheritdoc IVault */ - function adminCall(bytes[] memory _codes) external override { + function adminCall(bytes32[] calldata _commands, bytes[] memory _state) external payable override { _onlyGovernance(); - executeCodes(_codes, Errors.ADMIN_CALL); + _writeExecute(_commands, _state); } /** * @inheritdoc IVault */ - function claimRewardToken(address _liquidityPool) external override { + function claimRewardToken(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); - executeCodes( - _liquidityPool.getClaimRewardTokenCode(address(registryContract), payable(address(this))), - Errors.CLAIM_REWARD_FAILED - ); + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + _strategyRegistry.getClaimRewardsPlan(address(this), _strategyHash); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } /** * @inheritdoc IVault */ - function harvest( - address _rewardToken, - address _dex, - bool _isUniV3, - uint256 _minimumUnderlyingTokenAmount, - uint256 _deadline, - address[] memory _path, - bytes memory _pathUniV3 - ) external override { + function harvestRewards(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); - uint256 _rewardTokenAmount = IERC20(_rewardToken).balanceOf(address(this)); - if (_isUniV3) { - ISwapRouter(_dex).exactInput( - ISwapRouter.ExactInputParams({ - path: _pathUniV3, - recipient: address(this), - deadline: _deadline, - amountIn: _rewardTokenAmount, - amountOutMinimum: _minimumUnderlyingTokenAmount - }) - ); - } else { - IUniswapV2Router01(_dex).swapExactTokensForTokens( - _rewardTokenAmount, - _minimumUnderlyingTokenAmount, - _path, - address(this), - _deadline - ); - } + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + _strategyRegistry.getClaimRewardsPlan(address(this), _strategyHash); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } /** @@ -603,18 +519,20 @@ contract Vault is /** * @inheritdoc IVault */ - function getLastStrategyStepBalanceLP(DataTypes.StrategyStep[] memory _investStrategySteps) - public - view - override - returns (uint256) - { - return - _investStrategySteps.getLastStrategyStepBalanceLP( - address(registryContract), - payable(address(this)), - underlyingToken - ); + function getLastStrategyStepBalanceLP(bytes32 _strategyHash) public view override returns (uint256) { + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + _strategyRegistry.getLastStepBalanceLPPlan(address(this), _strategyHash); + bytes[] memory _bal = _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + return abi.decode(_bal[_strategyPlanInput.outputIndex], (uint256)); + } + + function getCacheDepositValueUT() external view returns (uint256) { + return _cacheDepositValueUT; + } + + function getCacheWithdrawAmountLP() external view returns (uint256) { + return _cacheWithdrawAmountLP; } //===Internal functions===// @@ -702,16 +620,11 @@ contract Vault is * @dev internal function withdraw for an user * @param _receiver address of the receiver of the underlying token * @param _userWithdrawVT amount in vault token - * @param _expectedOutput the minimum amount of underlying tokens that - * must be received to not revert the transaction - * @param _withdrawStrategies strategies to withdraw from * @param _accountsProof merkle proof for caller */ function _withdrawVaultFor( address _receiver, uint256 _userWithdrawVT, - uint256 _expectedOutput, - bytes32[] memory _withdrawStrategies, bytes32[] calldata _accountsProof ) internal returns (uint256) { _checkUserWithdraw(msg.sender, _userWithdrawVT, _accountsProof); @@ -727,36 +640,36 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + // IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - for (uint256 i; i < _withdrawStrategies.length; i++) { - uint256 _oraAmountLP; + // for (uint256 i; i < _withdrawStrategies.length; i++) { + // uint256 _oraAmountLP; - bytes32 withdrawStrategyHash = _withdrawStrategies[i]; - uint256 buffer = withdrawalBuffers[withdrawStrategyHash]; - DataTypes.StrategyStep[] memory _steps = strategyRegistry.getStrategySteps(withdrawStrategyHash); + // bytes32 withdrawStrategyHash = _withdrawStrategies[i]; + // uint256 buffer = withdrawalBuffers[withdrawStrategyHash]; + // DataTypes.StrategyStep[] memory _steps = strategyRegistry.getStrategySteps(withdrawStrategyHash); - if (buffer >= _expectedStratWithdrawUT) { - _oraAmountLP = _steps.getOraSomeValueLP( - address(registryContract), - underlyingToken, - _expectedStratWithdrawUT - ); + // if (buffer >= _expectedStratWithdrawUT) { + // _oraAmountLP = _steps.getOraSomeValueLP( + // address(registryContract), + // underlyingToken, + // _expectedStratWithdrawUT + // ); - _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); + // _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); - break; - } else { - _expectedStratWithdrawUT -= buffer; - _oraAmountLP = _steps.getOraSomeValueLP(address(registryContract), underlyingToken, buffer); + // break; + // } else { + // _expectedStratWithdrawUT -= buffer; + // _oraAmountLP = _steps.getOraSomeValueLP(address(registryContract), underlyingToken, buffer); - _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); + // _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); - if (i == _withdrawStrategies.length - 1 && _expectedStratWithdrawUT != 0) { - revert(Errors.WITHDRAWAL_TOO_LARGE); - } - } - } + // if (i == _withdrawStrategies.length - 1 && _expectedStratWithdrawUT != 0) { + // revert(Errors.WITHDRAWAL_TOO_LARGE); + // } + // } + // } // Identify Slippage // UT requested from strategy withdraw = _expectedStratWithdrawUT @@ -783,73 +696,41 @@ contract Vault is return _withdrawAmount; } - /** - * @dev Internal function to deposit some balance of underlying token from current strategy - * @param _investStrategySteps array of strategy step tuple - * @param _depositValueUT amount in underlying token - */ - function _vaultDepositToStrategy(DataTypes.StrategyStep[] memory _investStrategySteps, uint256 _depositValueUT) - internal - { - uint256 _internalTransactionCount = - _investStrategySteps.getDepositInternalTransactionCount(address(registryContract)); - for (uint256 _i; _i < _internalTransactionCount; _i++) { - executeCodes( - ( - _investStrategySteps.getPoolDepositCodes( - DataTypes.StrategyConfigurationParams({ - registryContract: address(registryContract), - vault: payable(address(this)), - underlyingToken: underlyingToken, - initialStepInputAmount: _depositValueUT, - internalTransactionIndex: _i, - internalTransactionCount: _internalTransactionCount - }) - ) - ), - Errors.VAULT_DEPOSIT - ); - } - } - /** * @dev Internal function to withdraw all investments */ - function _vaultWithdrawAll() internal { - IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + function _vaultWithdrawFromAllStrategies() internal { for (uint256 i; i < strategies.length(); i++) { - _vaultWithdrawSomeFromStrategy( - strategyRegistry.getStrategySteps(strategies.at(i)), - getLastStrategyStepBalanceLP(strategyRegistry.getStrategySteps(strategies.at(i))) - ); + _vaultWithdrawSomeFromStrategy(strategies.at(i), getLastStrategyStepBalanceLP(strategies.at(i))); } } /** * @dev Internal function to withdraw some investments from current strategy - * @param _investStrategySteps array of strategy step tuple + * @param _strategyHash keccak256 hash of the strategy * @param _withdrawAmountLP amount in lpToken */ - function _vaultWithdrawSomeFromStrategy( - DataTypes.StrategyStep[] memory _investStrategySteps, - uint256 _withdrawAmountLP - ) internal { - uint256 _internalWithdrawTransactionCount = _investStrategySteps.length; - for (uint256 _i; _i < _internalWithdrawTransactionCount; _i++) { - executeCodes( - _investStrategySteps.getPoolWithdrawCodes( - DataTypes.StrategyConfigurationParams({ - registryContract: address(registryContract), - vault: payable(address(this)), - underlyingToken: underlyingToken, - initialStepInputAmount: _withdrawAmountLP, - internalTransactionIndex: _internalWithdrawTransactionCount - 1 - _i, - internalTransactionCount: _internalWithdrawTransactionCount - }) - ), - Errors.VAULT_WITHDRAW - ); - } + function _vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) internal { + require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_WHITELISTED); + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + _cacheWithdrawAmountLP = _withdrawAmountLP; + DataTypes.StrategyPlanInput memory _strategyPlanInput = + _strategyRegistry.getWithdrawSomeFromStrategyPlan(address(this), _strategyHash); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } + + function _vaultDepositSomeToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) internal { + _onlyStrategyOperator(); + _checkVaultDeposit(); + require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_WHITELISTED); + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + IRiskManager _riskManager = IRiskManager(registryContract.getRiskManager()); + // TODO : isValidStrategy should be read only function + _riskManager.isValidStrategy(_strategyHash, (vaultConfiguration >> 240) & 0xFF); + _cacheDepositValueUT = _depositValueUT; + DataTypes.StrategyPlanInput memory _strategyPlanInput = + _strategyRegistry.getDepositSomeToStrategyPlan(address(this), _strategyHash); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } /** @@ -957,28 +838,25 @@ contract Vault is * @return amount in underlying token */ function _oraVaultAndStratValueUT() internal view returns (uint256) { - uint256 _totalValue = balanceUT(); - - for (uint256 i; i < strategies.length(); i++) { - _totalValue += _oraStratValueUT( - IStrategyRegistry(registryContract.getStrategyRegistry()).getStrategySteps(strategies.at(i)) - ); - } - - return _totalValue; + return _oraStratValueUT().add(balanceUT()); } /** * @dev Computes the amount in underlying token for the investment made in strategy - * @param _steps the strategy steps for the strategy to check * @return amount in underlying token */ - function _oraStratValueUT(DataTypes.StrategyStep[] memory _steps) internal view returns (uint256) { - // totaldebt - return - investStrategyHash != Constants.ZERO_BYTES32 - ? _steps.getOraValueUT(address(registryContract), payable(address(this)), underlyingToken) - : 0; + function _oraStratValueUT() internal view returns (uint256) { + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + uint256 _sum; + for (uint256 _i; _i < strategies.length(); _i++) { + _sum += _getUint256(_strategyRegistry.getOraValueUTPlan(address(this), strategies.at(_i))); + } + return _sum; + } + + function _getUint256(DataTypes.StrategyPlanInput memory _strategyPlanInput) internal view returns (uint256) { + bytes[] memory _result = _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + return abi.decode(_result[_strategyPlanInput.outputIndex], (uint256)); } function _verifyWhitelistedAccount(bytes32 _leaf, bytes32[] memory _proof) internal view returns (bool) { diff --git a/contracts/protocol/tokenization/VaultStorage.sol b/contracts/protocol/tokenization/VaultStorage.sol index 01e42fda..de2f60d1 100644 --- a/contracts/protocol/tokenization/VaultStorage.sol +++ b/contracts/protocol/tokenization/VaultStorage.sol @@ -38,7 +38,7 @@ contract VaultStorage { /** * @dev Current vault invest strategy */ - bytes32 public investStrategyHash; + uint256 internal _cacheDepositValueUT; /** * @dev Maximum amount in underlying token allowed to be deposited by user @@ -107,10 +107,14 @@ contract VaultStorageV3 is VaultStorageV2 { /**@dev deposit and withdraw flag*/ mapping(uint256 => bool) public blockTransaction; +} +contract VaultStorageV4 is VaultStorageV3 { /**@dev set of strategy hashes which the vault may operate on */ EnumerableSet.Bytes32Set internal strategies; /**@dev mapping of strategy to withdrawal buffer */ - mapping(bytes32 => uint256) internal withdrawalBuffers; + // mapping(bytes32 => uint256) internal withdrawalBuffers; + + uint256 internal _cacheWithdrawAmountLP; } diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol index 0eb2119e..4e4be362 100644 --- a/contracts/protocol/tokenization/VaultWeiroll.sol +++ b/contracts/protocol/tokenization/VaultWeiroll.sol @@ -178,7 +178,7 @@ contract VaultWeiroll is VM, ERC20 { // withdraw UT shortage from strategy _cacheExpectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); _cacheOraAmountLP = _getOraSomeValueLP(); - _execute(_withdrawSomeFromStrategy.commands, _withdrawSomeFromStrategy.state); + _writeExecute(_withdrawSomeFromStrategy.commands, _withdrawSomeFromStrategy.state); uint256 _vaultValuePostStratWithdrawUT = balanceUT(); uint256 _receivedStratWithdrawUT = _vaultValuePostStratWithdrawUT.sub(_vaultValuePreStratWithdrawUT); @@ -193,7 +193,7 @@ contract VaultWeiroll is VM, ERC20 { } function vaultWithdrawAllFromStrategy() external { - _execute(_withdrawAllFromStrategy.commands, _withdrawAllFromStrategy.state); + _writeExecute(_withdrawAllFromStrategy.commands, _withdrawAllFromStrategy.state); } function getPPS() external view returns (uint256) { @@ -233,7 +233,7 @@ contract VaultWeiroll is VM, ERC20 { } function vaultDepositAllToStrategy() external { - _execute(_depositToStrategy.commands, _depositToStrategy.state); + _writeExecute(_depositToStrategy.commands, _depositToStrategy.state); } function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external { diff --git a/contracts/utils/Errors.sol b/contracts/utils/Errors.sol index b59f3047..9155971a 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -32,7 +32,7 @@ library Errors { string public constant INVALID_SIGNATURE = "27"; string public constant LENGTH_MISMATCH = "28"; string public constant BUFFER_EXCEEDED = "29"; - string public constant INVALID_STRATEGY = "30"; + string public constant STRATEGY_NOT_WHITELISTED = "30"; string public constant WITHDRAWAL_TOO_LARGE = "31"; string public constant RETURNED_UT_TOO_FEW = "32"; string public constant RETURNED_LP_TOO_FEW = "33"; diff --git a/hardhat.config.ts b/hardhat.config.ts index 87f8aa1a..4ee9da1d 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -14,7 +14,6 @@ import "@typechain/hardhat"; import "hardhat-storage-layout"; import "@tenderly/hardhat-tenderly"; import "hardhat-contract-sizer"; -// import "hardhat-storage-layout"; import { NETWORKS_RPC_URL, NETWORKS_DEFAULT_GAS, From 3a5b9517162cb0f6de9cab85f722475b29550ee5 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 7 Dec 2022 01:06:25 -0500 Subject: [PATCH 22/44] refactor(vault.sol): reduce bytecodesize --- contracts/protocol/lib/CommandBuilder.sol | 5 +- contracts/protocol/tokenization/VM.sol | 50 +++++++------- contracts/protocol/tokenization/Vault.sol | 83 ++++++++++++----------- contracts/utils/Errors.sol | 2 +- 4 files changed, 73 insertions(+), 67 deletions(-) diff --git a/contracts/protocol/lib/CommandBuilder.sol b/contracts/protocol/lib/CommandBuilder.sol index 58704569..de3d8953 100644 --- a/contracts/protocol/lib/CommandBuilder.sol +++ b/contracts/protocol/lib/CommandBuilder.sol @@ -1,3 +1,5 @@ +/*solhint-disable no-inline-assembly*/ +/*solhint-disable state-visibility*/ // SPDX-License-Identifier: MIT pragma solidity ^0.6.12; @@ -32,7 +34,8 @@ library CommandBuilder { } count += stateData.length; } else { - // Add the size of the value, rounded up to the next word boundary, plus space for pointer and length + // Add the size of the value, rounded up to the next word boundary, + // plus space for pointer and length uint256 arglen = state[idx & IDX_VALUE_MASK].length; require(arglen % 32 == 0, "Dynamic state variables must be a multiple of 32 bytes"); count += arglen + 32; diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index edfd4822..6ba65f85 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -1,3 +1,7 @@ +/*solhint-disable no-inline-assembly*/ +/*solhint-disable no-empty-blocks*/ +/*solhint-disable avoid-low-level-calls*/ +/*solhint-disable state-visibility */ // SPDX-License-Identifier: MIT pragma solidity ^0.6.12; @@ -94,18 +98,17 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert( - string( - abi.encodePacked( - "Execution Failed : command_index=", - string("0"), - ", target=", - address(uint160(uint256(command))), - ", message=", - outdata.length > 0 ? string(outdata) : "Unknown" - ) - ) - ); + revert(); + // string( + // abi.encodePacked( + // "Execution Failed : command_index=", + // string("0"), + // ", target=", + // address(uint160(uint256(command))), + // ", message=", + // outdata.length > 0 ? string(outdata) : "Unknown" + // ) + // ) } if (flags & FLAG_TUPLE_RETURN != 0) { @@ -156,18 +159,17 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert( - string( - abi.encodePacked( - "Execution Failed : command_index=", - string("0"), - ", target=", - address(uint160(uint256(command))), - ", message=", - outdata.length > 0 ? string(outdata) : "Unknown" - ) - ) - ); + revert(); + // string( + // abi.encodePacked( + // "Execution Failed : command_index=", + // string("0"), + // ", target=", + // address(uint160(uint256(command))), + // ", message=", + // outdata.length > 0 ? string(outdata) : "Unknown" + // ) + // ) } if (flags & FLAG_TUPLE_RETURN != 0) { diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 998a1b83..cc30e4a0 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -16,7 +16,6 @@ import { EIP712Base } from "../../utils/EIP712Base.sol"; import { VM } from "./VM.sol"; // libraries -import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { DataTypes } from "../earn-protocol-configuration/contracts/libraries/types/DataTypes.sol"; import { Constants } from "../../utils/Constants.sol"; import { Errors } from "../../utils/Errors.sol"; @@ -31,7 +30,6 @@ import { IVault } from "../../interfaces/opty/IVault.sol"; import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; import { IRiskManager } from "../earn-protocol-configuration/contracts/interfaces/opty/IRiskManager.sol"; import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; -import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; /** * @title Vault contract inspired by AAVE V3's AToken.sol @@ -50,7 +48,6 @@ contract Vault is VM { using SafeERC20 for IERC20; - using Address for address; using EnumerableSet for EnumerableSet.Bytes32Set; /** @@ -314,9 +311,8 @@ contract Vault is */ function claimRewardToken(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); - IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); DataTypes.StrategyPlanInput memory _strategyPlanInput = - _strategyRegistry.getClaimRewardsPlan(address(this), _strategyHash); + IStrategyRegistry(registryContract.getStrategyRegistry()).getClaimRewardsPlan(address(this), _strategyHash); _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } @@ -325,9 +321,8 @@ contract Vault is */ function harvestRewards(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); - IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); DataTypes.StrategyPlanInput memory _strategyPlanInput = - _strategyRegistry.getClaimRewardsPlan(address(this), _strategyHash); + IStrategyRegistry(registryContract.getStrategyRegistry()).getClaimRewardsPlan(address(this), _strategyHash); _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } @@ -420,11 +415,7 @@ contract Vault is * @inheritdoc IVault */ function getPricePerFullShare() public view override returns (uint256) { - if (totalSupply() != 0) { - return _oraVaultAndStratValueUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); - } else { - return uint256(0); - } + return totalSupply() == 0 ? 0 : _oraVaultAndStratValueUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); } /** @@ -520,11 +511,16 @@ contract Vault is * @inheritdoc IVault */ function getLastStrategyStepBalanceLP(bytes32 _strategyHash) public view override returns (uint256) { - IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); DataTypes.StrategyPlanInput memory _strategyPlanInput = - _strategyRegistry.getLastStepBalanceLPPlan(address(this), _strategyHash); - bytes[] memory _bal = _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state); - return abi.decode(_bal[_strategyPlanInput.outputIndex], (uint256)); + IStrategyRegistry(registryContract.getStrategyRegistry()).getLastStepBalanceLPPlan( + address(this), + _strategyHash + ); + return + abi.decode( + _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state)[_strategyPlanInput.outputIndex], + (uint256) + ); } function getCacheDepositValueUT() external view returns (uint256) { @@ -640,7 +636,10 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - // IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + + // proportionally redeem LP from each strategy + // 1. get the allocation ratio in each strategy // for (uint256 i; i < _withdrawStrategies.length; i++) { // uint256 _oraAmountLP; @@ -700,8 +699,8 @@ contract Vault is * @dev Internal function to withdraw all investments */ function _vaultWithdrawFromAllStrategies() internal { - for (uint256 i; i < strategies.length(); i++) { - _vaultWithdrawSomeFromStrategy(strategies.at(i), getLastStrategyStepBalanceLP(strategies.at(i))); + for (uint256 _i; _i < strategies.length(); _i++) { + _vaultWithdrawSomeFromStrategy(strategies.at(_i), getLastStrategyStepBalanceLP(strategies.at(_i))); } } @@ -711,25 +710,33 @@ contract Vault is * @param _withdrawAmountLP amount in lpToken */ function _vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) internal { - require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_WHITELISTED); - IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - _cacheWithdrawAmountLP = _withdrawAmountLP; - DataTypes.StrategyPlanInput memory _strategyPlanInput = - _strategyRegistry.getWithdrawSomeFromStrategyPlan(address(this), _strategyHash); - _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_SET); + if (_withdrawAmountLP != 0) { + _cacheWithdrawAmountLP = _withdrawAmountLP; + DataTypes.StrategyPlanInput memory _strategyPlanInput = + IStrategyRegistry(registryContract.getStrategyRegistry()).getWithdrawSomeFromStrategyPlan( + address(this), + _strategyHash + ); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } } function _vaultDepositSomeToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) internal { _onlyStrategyOperator(); _checkVaultDeposit(); - require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_WHITELISTED); - IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - IRiskManager _riskManager = IRiskManager(registryContract.getRiskManager()); + require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_SET); // TODO : isValidStrategy should be read only function - _riskManager.isValidStrategy(_strategyHash, (vaultConfiguration >> 240) & 0xFF); + IRiskManager(registryContract.getRiskManager()).isValidStrategy( + _strategyHash, + (vaultConfiguration >> 240) & 0xFF + ); _cacheDepositValueUT = _depositValueUT; DataTypes.StrategyPlanInput memory _strategyPlanInput = - _strategyRegistry.getDepositSomeToStrategyPlan(address(this), _strategyHash); + IStrategyRegistry(registryContract.getStrategyRegistry()).getDepositSomeToStrategyPlan( + address(this), + _strategyHash + ); _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } @@ -805,15 +812,6 @@ contract Vault is vaultConfiguration = _vaultConfiguration; } - // /** - // * @dev Internal function to control the allowance of user interaction - // * only when vault's whitelistedstate is enabled - // * @param _whitelistedAccountsRoot whitelisted accounts root hash - // */ - // function _setWhitelistedAccountsRoot(bytes32 _whitelistedAccountsRoot) internal { - // whitelistedAccountsRoot = _whitelistedAccountsRoot; - // } - /** * @dev Internal function to configure the vault's value control params * @param _userDepositCapUT maximum amount in underlying token allowed to be deposited by user @@ -855,8 +853,11 @@ contract Vault is } function _getUint256(DataTypes.StrategyPlanInput memory _strategyPlanInput) internal view returns (uint256) { - bytes[] memory _result = _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state); - return abi.decode(_result[_strategyPlanInput.outputIndex], (uint256)); + return + abi.decode( + _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state)[_strategyPlanInput.outputIndex], + (uint256) + ); } function _verifyWhitelistedAccount(bytes32 _leaf, bytes32[] memory _proof) internal view returns (bool) { diff --git a/contracts/utils/Errors.sol b/contracts/utils/Errors.sol index 9155971a..eb61202f 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -32,7 +32,7 @@ library Errors { string public constant INVALID_SIGNATURE = "27"; string public constant LENGTH_MISMATCH = "28"; string public constant BUFFER_EXCEEDED = "29"; - string public constant STRATEGY_NOT_WHITELISTED = "30"; + string public constant STRATEGY_NOT_SET = "30"; string public constant WITHDRAWAL_TOO_LARGE = "31"; string public constant RETURNED_UT_TOO_FEW = "32"; string public constant RETURNED_LP_TOO_FEW = "33"; From 77db8a0cd7ee62c3c8887c20f8ab55ab02258a8c Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 7 Dec 2022 13:53:07 -0500 Subject: [PATCH 23/44] feat(vault.sol): user withdraw from strategies proportionally --- contracts/protocol/tokenization/VM.sol | 46 +++++------ contracts/protocol/tokenization/Vault.sol | 77 ++++++++++--------- .../protocol/tokenization/VaultStorage.sol | 9 +-- hardhat.config.ts | 2 +- 4 files changed, 68 insertions(+), 66 deletions(-) diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index 6ba65f85..acbabd29 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -98,17 +98,18 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert(); - // string( - // abi.encodePacked( - // "Execution Failed : command_index=", - // string("0"), - // ", target=", - // address(uint160(uint256(command))), - // ", message=", - // outdata.length > 0 ? string(outdata) : "Unknown" - // ) - // ) + revert( + string( + abi.encodePacked( + "Execution Failed : command_index=", + string("0"), + ", target=", + address(uint160(uint256(command))), + ", message=", + outdata.length > 0 ? string(outdata) : "Unknown" + ) + ) + ); } if (flags & FLAG_TUPLE_RETURN != 0) { @@ -159,17 +160,18 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert(); - // string( - // abi.encodePacked( - // "Execution Failed : command_index=", - // string("0"), - // ", target=", - // address(uint160(uint256(command))), - // ", message=", - // outdata.length > 0 ? string(outdata) : "Unknown" - // ) - // ) + revert( + string( + abi.encodePacked( + "Execution Failed : command_index=", + string("0"), + ", target=", + address(uint160(uint256(command))), + ", message=", + outdata.length > 0 ? string(outdata) : "Unknown" + ) + ) + ); } if (flags & FLAG_TUPLE_RETURN != 0) { diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index cc30e4a0..840a9d50 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -321,6 +321,7 @@ contract Vault is */ function harvestRewards(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); + // TODO : get the harvest rewards code from StrategyRegistry DataTypes.StrategyPlanInput memory _strategyPlanInput = IStrategyRegistry(registryContract.getStrategyRegistry()).getClaimRewardsPlan(address(this), _strategyHash); _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); @@ -523,12 +524,12 @@ contract Vault is ); } - function getCacheDepositValueUT() external view returns (uint256) { - return _cacheDepositValueUT; + function getCacheValueUT() external view returns (uint256) { + return _cacheValueUT; } - function getCacheWithdrawAmountLP() external view returns (uint256) { - return _cacheWithdrawAmountLP; + function getCacheAmountLP() external view returns (uint256) { + return _cacheAmountLP; } //===Internal functions===// @@ -638,37 +639,23 @@ contract Vault is uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - // proportionally redeem LP from each strategy - // 1. get the allocation ratio in each strategy - - // for (uint256 i; i < _withdrawStrategies.length; i++) { - // uint256 _oraAmountLP; - - // bytes32 withdrawStrategyHash = _withdrawStrategies[i]; - // uint256 buffer = withdrawalBuffers[withdrawStrategyHash]; - // DataTypes.StrategyStep[] memory _steps = strategyRegistry.getStrategySteps(withdrawStrategyHash); - - // if (buffer >= _expectedStratWithdrawUT) { - // _oraAmountLP = _steps.getOraSomeValueLP( - // address(registryContract), - // underlyingToken, - // _expectedStratWithdrawUT - // ); - - // _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); - - // break; - // } else { - // _expectedStratWithdrawUT -= buffer; - // _oraAmountLP = _steps.getOraSomeValueLP(address(registryContract), underlyingToken, buffer); - - // _vaultWithdrawSomeFromStrategy(_steps, _oraAmountLP); - - // if (i == _withdrawStrategies.length - 1 && _expectedStratWithdrawUT != 0) { - // revert(Errors.WITHDRAWAL_TOO_LARGE); - // } - // } - // } + for (uint256 _i; _i < strategies.length(); _i++) { + uint256 _a = _oraStratValueUTByStrategy(strategyRegistry, strategies.at(_i)); + _cacheValueUT = _a.sub( + ( + ( + (_oraVaultAndStratValueUT().sub(_expectedStratWithdrawUT)).mul((_a.mul(10000))).div( + _oraVaultAndStratValueUT() + ) + ) + .div(10000) + ) + ); + _cacheAmountLP = _getOraSomeValueLPByStrategy(strategyRegistry, strategies.at(_i)); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + strategyRegistry.getWithdrawSomeFromStrategyPlan(address(this), strategies.at(_i)); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } // Identify Slippage // UT requested from strategy withdraw = _expectedStratWithdrawUT @@ -712,7 +699,7 @@ contract Vault is function _vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) internal { require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_SET); if (_withdrawAmountLP != 0) { - _cacheWithdrawAmountLP = _withdrawAmountLP; + _cacheAmountLP = _withdrawAmountLP; DataTypes.StrategyPlanInput memory _strategyPlanInput = IStrategyRegistry(registryContract.getStrategyRegistry()).getWithdrawSomeFromStrategyPlan( address(this), @@ -731,7 +718,7 @@ contract Vault is _strategyHash, (vaultConfiguration >> 240) & 0xFF ); - _cacheDepositValueUT = _depositValueUT; + _cacheValueUT = _depositValueUT; DataTypes.StrategyPlanInput memory _strategyPlanInput = IStrategyRegistry(registryContract.getStrategyRegistry()).getDepositSomeToStrategyPlan( address(this), @@ -852,6 +839,22 @@ contract Vault is return _sum; } + function _oraStratValueUTByStrategy(IStrategyRegistry _strategyRegistry, bytes32 _strategyHash) + internal + view + returns (uint256) + { + return _getUint256(_strategyRegistry.getOraValueUTPlan(address(this), _strategyHash)); + } + + function _getOraSomeValueLPByStrategy(IStrategyRegistry _strategyRegistry, bytes32 _strategyHash) + internal + view + returns (uint256) + { + return _getUint256(_strategyRegistry.getOraValueLPPlan(address(this), _strategyHash)); + } + function _getUint256(DataTypes.StrategyPlanInput memory _strategyPlanInput) internal view returns (uint256) { return abi.decode( diff --git a/contracts/protocol/tokenization/VaultStorage.sol b/contracts/protocol/tokenization/VaultStorage.sol index de2f60d1..cb0cc890 100644 --- a/contracts/protocol/tokenization/VaultStorage.sol +++ b/contracts/protocol/tokenization/VaultStorage.sol @@ -36,9 +36,9 @@ contract VaultStorage { mapping(uint256 => DataTypes.BlockVaultValue[]) public blockToBlockVaultValues; /** - * @dev Current vault invest strategy + * @dev cache value in UT for intermediate calculations */ - uint256 internal _cacheDepositValueUT; + uint256 internal _cacheValueUT; /** * @dev Maximum amount in underlying token allowed to be deposited by user @@ -113,8 +113,5 @@ contract VaultStorageV4 is VaultStorageV3 { /**@dev set of strategy hashes which the vault may operate on */ EnumerableSet.Bytes32Set internal strategies; - /**@dev mapping of strategy to withdrawal buffer */ - // mapping(bytes32 => uint256) internal withdrawalBuffers; - - uint256 internal _cacheWithdrawAmountLP; + uint256 internal _cacheAmountLP; } diff --git a/hardhat.config.ts b/hardhat.config.ts index 4ee9da1d..99d8bb1a 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -86,7 +86,7 @@ const config: HardhatUserConfig = { // https://hardhat.org/hardhat-network/#solidity-optimizer-support optimizer: { enabled: true, - runs: 200, + runs: 40, }, }, }, From 4611bd1c029bc696ca5d61acf87da67e415bc1d8 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Fri, 9 Dec 2022 23:56:02 -0500 Subject: [PATCH 24/44] feat(adapter): add compound adapter --- contracts/protocol/tokenization/Vault.sol | 10 +- helpers/adapters/ethereum/AaveV1Adapter.ts | 0 helpers/adapters/ethereum/CompoundAdapter.ts | 191 +++++++++++++++++++ helpers/type.d.ts | 25 +++ test/test-opty/vaultweiroll.spec.ts | 4 +- 5 files changed, 222 insertions(+), 8 deletions(-) create mode 100644 helpers/adapters/ethereum/AaveV1Adapter.ts create mode 100644 helpers/adapters/ethereum/CompoundAdapter.ts diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 840a9d50..4639e515 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -628,7 +628,8 @@ contract Vault is // burning should occur at pre userwithdraw price UNLESS there is slippage // if there is slippage, the withdrawing user should absorb that cost (see below) // i.e. get less underlying tokens than calculated by pre userwithdraw price - uint256 _oraUserWithdrawUT = _userWithdrawVT.mul(_oraVaultAndStratValueUT()).div(totalSupply()); + uint256 _poolOraValueUT = _oraVaultAndStratValueUT(); + uint256 _oraUserWithdrawUT = _userWithdrawVT.mul(_poolOraValueUT).div(totalSupply()); _burn(msg.sender, _userWithdrawVT); uint256 _vaultValuePreStratWithdrawUT = balanceUT(); @@ -643,12 +644,9 @@ contract Vault is uint256 _a = _oraStratValueUTByStrategy(strategyRegistry, strategies.at(_i)); _cacheValueUT = _a.sub( ( - ( - (_oraVaultAndStratValueUT().sub(_expectedStratWithdrawUT)).mul((_a.mul(10000))).div( - _oraVaultAndStratValueUT() - ) + ((_poolOraValueUT.sub(_expectedStratWithdrawUT)).mul((_a.mul(10000))).div(_poolOraValueUT)).div( + 10000 ) - .div(10000) ) ); _cacheAmountLP = _getOraSomeValueLPByStrategy(strategyRegistry, strategies.at(_i)); diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts new file mode 100644 index 00000000..e69de29b diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts new file mode 100644 index 00000000..5b205317 --- /dev/null +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -0,0 +1,191 @@ +import { Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { ReturnValue } from "../../type"; +import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; + +export class CompoundAdapter { + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + amountInInputTokenContractInstance: Contract, + amountInInputTokenMethod: string, + ): ReturnValue { + const amountUT = planner.add( + amountInInputTokenContractInstance[amountInInputTokenMethod]( + inputTokenInstance.address, + poolInstance.address, + outputTokenAmount, + ).staticcall(), + ); + return amountUT as ReturnValue; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + amountInOutputTokenContractInstance: Contract, + amountInOutputTokenMethod: string, + ): ReturnValue { + const amountLP = planner.add( + amountInOutputTokenContractInstance[amountInOutputTokenMethod]( + inputTokenInstance.address, + poolInstance.address, + inputTokenAmount, + ).staticcall(), + ); + return amountLP as ReturnValue; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + ): ReturnValue { + const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + _outputTokenInstance: Contract, + ): weirollPlanner { + const comptrollerContract = new ethers.Contract(Compound.comptroller.address, comptrollerAbi); + const comptrollerInstance = weirollContract.createContract(comptrollerContract); + planner.add( + comptrollerInstance["claimComp(address[],address[],bool,bool)"]( + [vaultInstance.address], + [poolInstance.address], + false, + true, + ).staticcall(), + ); + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + ): weirollPlanner { + EthereumTokens.REWARD_TOKENS.COMP; + // add read call to oracle for computing minimum expected + const uniswapV3RouterContract = new ethers.Contract( + "0xE592427A0AEce92De3Edee1F18E0157C05861564", + ISwapRouter__factory.abi, + ); + const uniswapV3RouterInstance = weirollContract.createContract(uniswapV3RouterContract); + const rewardContract = new ethers.Contract(EthereumTokens.REWARD_TOKENS.COMP, ERC20__factory.abi); + const rewardInstance = weirollContract.createContract(rewardContract); + const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const uniV3SwapPathForCOMPtoUT = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + ethereumTokens.REWARD_TOKENS.COMP, + 10000, + ethereumTokens.WRAPPED_TOKENS.WETH, + 500, + ethereumTokens.PLAIN_TOKENS.USDT, + ], + ); + planner.add( + uniswapV3RouterInstance["exactInput((bytes,address,uint256,uint256,uint256))"]([ + path, + recipient, + deadline, + rewardAmount, + 0, + ]), + ); + return planner; + } +} + +const comptrollerAbi = [ + { + constant: false, + inputs: [ + { + internalType: "address[]", + name: "holders", + type: "address[]", + }, + { + internalType: "contract CToken[]", + name: "cTokens", + type: "address[]", + }, + { + internalType: "bool", + name: "borrowers", + type: "bool", + }, + { + internalType: "bool", + name: "suppliers", + type: "bool", + }, + ], + name: "claimComp", + outputs: [], + payable: false, + stateMutability: "nonpayable", + type: "function", + }, + { + constant: true, + inputs: [], + name: "getCompAddress", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + payable: false, + stateMutability: "view", + type: "function", + }, +]; diff --git a/helpers/type.d.ts b/helpers/type.d.ts index cdaf838b..a411a4ba 100644 --- a/helpers/type.d.ts +++ b/helpers/type.d.ts @@ -1,5 +1,7 @@ import { BigNumberish, Contract } from "ethers"; import { MockContract } from "@defi-wonderland/smock"; +import { Value, FunctionCall } from "@weiroll/weiroll.js"; +import { ParamType } from "@ethersproject/abi"; export type ESSENTIAL_CONTRACTS = { registry: Contract; @@ -212,3 +214,26 @@ export type PoolRate = { pool: string; rate: number; }; + +export type WeirollPlan = { + commands: string[]; + state: string[]; +}; + +export declare class ReturnValue implements Value { + readonly param: ParamType; + readonly command: Command; + constructor(param: ParamType, command: Command); +} + +export declare class Command { + readonly call: FunctionCall; + readonly type: CommandType; + constructor(call: FunctionCall, type: CommandType); +} + +export declare enum CommandType { + CALL = 0, + RAWCALL = 1, + SUBPLAN = 2, +} diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts index 2450ba37..35669a0e 100644 --- a/test/test-opty/vaultweiroll.spec.ts +++ b/test/test-opty/vaultweiroll.spec.ts @@ -1,6 +1,6 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { deployContract, solidity } from "ethereum-waffle"; -import { artifacts, ethers as ethers } from "hardhat"; +import { artifacts, ethers } from "hardhat"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { Contract as weirollContract, Planner as weirollPlanner } from "@weiroll/weiroll.js"; import { Signers } from "../../helpers/utils"; @@ -73,7 +73,7 @@ describe("VaultWeiroll", async function () { compoundAdapterContract.getSomeAmountInToken(usdc.address, cUSDC.address, lpTokenBalance).staticcall(), ); oraValueUTPlanner.add(testVaultContract.pureFunctionUint256(amountInToken).staticcall()); - await vaultWeirollUSDC.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state); + await vaultWeirollUSDC.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state, 0); // lastStepBalanceLP planner const lastStepBalanceLPPlanner = new weirollPlanner(); From cbe731843188ee56a76720d98a2ec23d57add644 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Sat, 10 Dec 2022 01:02:02 -0500 Subject: [PATCH 25/44] feat(adapter): add AaveV2Adapter --- helpers/adapters/ethereum/AaveV1Adapter.ts | 96 ++++++++++++++++ helpers/adapters/ethereum/AaveV2Adapter.ts | 111 +++++++++++++++++++ helpers/adapters/ethereum/CompoundAdapter.ts | 45 +++++--- 3 files changed, 234 insertions(+), 18 deletions(-) create mode 100644 helpers/adapters/ethereum/AaveV2Adapter.ts diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index e69de29b..6562e2a5 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -0,0 +1,96 @@ +import { Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import Aave from "@optyfi/defi-legos/ethereum/aave/index"; +import { ReturnValue } from "../../type"; + +export class AaveV1Adapter { + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + // TODO handle ETH deposit + const lendingPoolContract = new ethers.Contract("0x398eC7346DcD622eDc5ae82352F02bE94C62d119", Aave.LendingPool.abi); + const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); + planner.add( + lendingPoolInstance["deposit(address,uint256,uint16)"](inputTokenInstance.address, inputTokenAmount, "0"), + ); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + // TODO handle ETH withdraw + const lpTokenContract = new ethers.Contract(outputTokenInstance.address, Aave.ATokenAbi); + const lpTokenInstance = weirollContract.createContract(lpTokenContract); + planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + _amountInInputTokenContractInstance: Contract, + _amountInInputTokenMethod: string, + ): ReturnValue { + return outputTokenAmount; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + _amountInOutputTokenContractInstance: Contract, + _amountInOutputTokenMethod: string, + ): ReturnValue { + return inputTokenAmount; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + ): ReturnValue { + const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + planner: weirollPlanner, + _vaultInstance: Contract, + _inputTokenInstance: Contract, + _poolInstance: Contract, + _outputTokenInstance: Contract, + ): weirollPlanner { + // TODO verify any rewards and write claim plan for reward token + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + _vaultInstance: Contract, + _vaulltUnderlyingTokenInstance: Contract, + ): weirollPlanner { + // TODO write harvest plan if reward token + return planner; + } +} diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts new file mode 100644 index 00000000..6c5cbee7 --- /dev/null +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -0,0 +1,111 @@ +import { Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import { ReturnValue } from "../../type"; + +export class Aavev2Adapter { + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const lendingPoolContract = new ethers.Contract( + "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", + AaveV2.LendingPool.abi, + ); + const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); + planner.add( + lendingPoolInstance["deposit(address,uint256,address,uint16)"]( + inputTokenInstance.address, + inputTokenAmount, + vaultInstance.address, + "0", + ), + ); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const lendingPoolContract = new ethers.Contract( + "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", + AaveV2.LendingPool.abi, + ); + const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); + planner.add( + lendingPoolInstance["withdraw(address,uint256,address)"]( + inputTokenInstance.address, + outputTokenAmount, + vaultInstance.address, + ), + ); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + outputTokenAmount: ReturnValue, + _amountInInputTokenContractInstance: Contract, + _amountInInputTokenMethod: string, + ): ReturnValue { + return outputTokenAmount; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + inputTokenAmount: ReturnValue, + _amountInOutputTokenContractInstance: Contract, + _amountInOutputTokenMethod: string, + ): ReturnValue { + return inputTokenAmount; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputTokenInstance: Contract, + poolInstance: Contract, + outputTokenInstance: Contract, + ): ReturnValue { + const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + planner: weirollPlanner, + _vaultInstance: Contract, + _inputTokenInstance: Contract, + _poolInstance: Contract, + _outputTokenInstance: Contract, + ): weirollPlanner { + // TODO verify any rewards and write claim plan for reward token + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + _vaultInstance: Contract, + _vaulltUnderlyingTokenInstance: Contract, + ): weirollPlanner { + // TODO write harvest plan if reward token + return planner; + } +} diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index 5b205317..b2db932c 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -5,6 +5,7 @@ import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; +import { getAddress } from "ethers/lib/utils"; export class CompoundAdapter { getDepositPlan( @@ -15,6 +16,7 @@ export class CompoundAdapter { outputTokenInstance: Contract, inputTokenAmount: ReturnValue, ): weirollPlanner { + // TODO handle ETH deposits planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); return planner; } @@ -27,6 +29,7 @@ export class CompoundAdapter { outputTokenInstance: Contract, outputTokenAmount: ReturnValue, ): weirollPlanner { + // TODO handle ETH withdraws planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); return planner; } @@ -105,12 +108,10 @@ export class CompoundAdapter { getHarvestRewardsPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + vaulltUnderlyingTokenInstance: Contract, ): weirollPlanner { - EthereumTokens.REWARD_TOKENS.COMP; - // add read call to oracle for computing minimum expected + // TODO add read call to oracle for computing minimum expected + // TODO consider uniswapV2 / sushiswap router for swapping const uniswapV3RouterContract = new ethers.Contract( "0xE592427A0AEce92De3Edee1F18E0157C05861564", ISwapRouter__factory.abi, @@ -119,21 +120,29 @@ export class CompoundAdapter { const rewardContract = new ethers.Contract(EthereumTokens.REWARD_TOKENS.COMP, ERC20__factory.abi); const rewardInstance = weirollContract.createContract(rewardContract); const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); - const uniV3SwapPathForCOMPtoUT = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [ - ethereumTokens.REWARD_TOKENS.COMP, - 10000, - ethereumTokens.WRAPPED_TOKENS.WETH, - 500, - ethereumTokens.PLAIN_TOKENS.USDT, - ], - ); + let univ3Path; + if (getAddress(vaulltUnderlyingTokenInstance.address) === getAddress(EthereumTokens.PLAIN_TOKENS.USDC)) { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.COMP, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDC, + ], + ); + } else if (getAddress(vaulltUnderlyingTokenInstance.address) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.COMP, 3000, EthereumTokens.WRAPPED_TOKENS.WETH], + ); + } planner.add( uniswapV3RouterInstance["exactInput((bytes,address,uint256,uint256,uint256))"]([ - path, - recipient, - deadline, + univ3Path, + vaultInstance.address, + ethers.constants.MaxUint256, rewardAmount, 0, ]), From 043ac0eb3e5fed5134663f718e981feaf57624af Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 12 Dec 2022 13:14:27 -0500 Subject: [PATCH 26/44] refactor(harvest): use v6 type --- helpers/constants/adapters.ts | 8 + .../harvest/002_swap_LDO_CRV_CVX_to_WETH.ts | 5 +- .../003_claim_and_swap_CRV_CVX_to_USDC.ts | 5 +- scripts/harvest/004_claim_and_stake_NEWO.ts | 5 +- ...aim_and_harvest_opUSDC-Save_opWETH-Save.ts | 7 +- .../006_opUSDC-Save_opWETH-Save_multisig.ts | 11 +- scripts/harvest/007_BDAMM.ts | 19 +- .../harvest/008_vault_repurpose_harvest.ts | 39 +- scripts/harvest/009_mim_harvest.ts | 348 +++++++++--------- test/test-opty/4_vault_multi_strategy.spec.ts | 10 +- test/test-opty/vaultweiroll.spec.ts | 2 - 11 files changed, 236 insertions(+), 223 deletions(-) diff --git a/helpers/constants/adapters.ts b/helpers/constants/adapters.ts index c11320a1..25a8269a 100644 --- a/helpers/constants/adapters.ts +++ b/helpers/constants/adapters.ts @@ -1,3 +1,5 @@ +import { CompoundAdapter } from "../adapters/ethereum/CompoundAdapter"; + export const AAVE_V1_ADAPTER_NAME: string = "AaveV1Adapter"; export const AAVE_V2_ADAPTER_NAME: string = "AaveV2Adapter"; export const COMPOUND_ADAPTER_NAME: string = "CompoundAdapter"; @@ -25,3 +27,9 @@ export const ADAPTERS = [ CONVEX_ADAPTER_NAME, ]; export const PROTOCOLS = ADAPTERS.map(item => (item === "ConvexFinanceAdapter" ? "Convex" : item.split("Adapter")[0])); + +export const liquidityPoolToAdapter = { + "0x39AA39c021dfbaE8faC545936693aC917d5E7563": new CompoundAdapter(), + "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643": new CompoundAdapter(), + "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9": new CompoundAdapter(), +}; diff --git a/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts b/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts index a246ffc0..c1d72b3d 100644 --- a/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts +++ b/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts @@ -1,7 +1,8 @@ import { BigNumber } from "ethers"; import { ethers } from "hardhat"; import { ESSENTIAL_CONTRACTS } from "../../helpers/constants/essential-contracts-name"; -import { ERC20, IUniswapV2Router02, Registry, Vault } from "../../typechain"; +import { VaultV6 } from "../../helpers/types/vaultv6"; +import { ERC20, IUniswapV2Router02, Registry } from "../../typechain"; async function main() { const uniswapV2Router02Address = ethers.utils.getAddress("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"); @@ -12,7 +13,7 @@ async function main() { const WETH = ethers.utils.getAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); const opWETHearnProxyAddress = ethers.utils.getAddress("0xFf2fbd9Fbc6d03BAA77cf97A3D5671bEA183b9a8"); const registryProxyAddress = ethers.utils.getAddress("0x99fa011E33A8c6196869DeC7Bc407E896BA67fE3"); - const vaultInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.VAULT, opWETHearnProxyAddress); + const vaultInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.VAULT, opWETHearnProxyAddress); const registryInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const uniswapRouterInstance = ( await ethers.getContractAt("IUniswapV2Router02", uniswapV2Router02Address) diff --git a/scripts/harvest/003_claim_and_swap_CRV_CVX_to_USDC.ts b/scripts/harvest/003_claim_and_swap_CRV_CVX_to_USDC.ts index 94b24664..d1dd95aa 100644 --- a/scripts/harvest/003_claim_and_swap_CRV_CVX_to_USDC.ts +++ b/scripts/harvest/003_claim_and_swap_CRV_CVX_to_USDC.ts @@ -2,8 +2,9 @@ import { parseUnits } from "ethers/lib/utils"; import { ethers } from "hardhat"; import Convex from "@optyfi/defi-legos/ethereum/convex"; import { ESSENTIAL_CONTRACTS } from "../../helpers/constants/essential-contracts-name"; -import { ERC20, IConvexStake, IUniswapV2Router02, Registry, Vault } from "../../typechain"; +import { ERC20, IConvexStake, IUniswapV2Router02, Registry } from "../../typechain"; import { BigNumber } from "ethers"; +import { VaultV6 } from "../../helpers/types/vaultv6"; export function getCVXUnclaimed( _amount: BigNumber, @@ -42,7 +43,7 @@ async function main() { const WETH = ethers.utils.getAddress("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"); const opUSDCearnProxyAddress = ethers.utils.getAddress("0x6d8BfdB4c4975bB086fC9027e48D5775f609fF88"); const registryProxyAddress = ethers.utils.getAddress("0x99fa011E33A8c6196869DeC7Bc407E896BA67fE3"); - const vaultInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.VAULT, opUSDCearnProxyAddress); + const vaultInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.VAULT, opUSDCearnProxyAddress); const registryInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const uniswapRouterInstance = ( await ethers.getContractAt("IUniswapV2Router02", uniswapV2Router02Address) diff --git a/scripts/harvest/004_claim_and_stake_NEWO.ts b/scripts/harvest/004_claim_and_stake_NEWO.ts index c56b1f97..3ec03c81 100644 --- a/scripts/harvest/004_claim_and_stake_NEWO.ts +++ b/scripts/harvest/004_claim_and_stake_NEWO.ts @@ -1,7 +1,8 @@ import { formatUnits } from "ethers/lib/utils"; import { ethers, network } from "hardhat"; import { ESSENTIAL_CONTRACTS } from "../../helpers/constants/essential-contracts-name"; -import { ERC20, INewoStaking, Registry, Vault } from "../../typechain"; +import { VaultV6 } from "../../helpers/types/vaultv6"; +import { ERC20, INewoStaking, Registry } from "../../typechain"; async function main() { const newoLPStaking = ethers.utils.getAddress("0xdb36b23964FAB32dCa717c99D6AEFC9FB5748f3a"); @@ -10,7 +11,7 @@ async function main() { const registryProxyAddress = ethers.utils.getAddress("0x99fa011e33a8c6196869dec7bc407e896ba67fe3"); const registryInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const governanceSigner = await ethers.getSigner(await registryInstance.governance()); - const vaultInstance = ( + const vaultInstance = ( await ethers.getContractAt(ESSENTIAL_CONTRACTS.VAULT, opNEWOinvstProxyAddress, governanceSigner) ); const newoLPStakingInstance = await ethers.getContractAt("INewoStaking", newoLPStaking); diff --git a/scripts/harvest/005_claim_and_harvest_opUSDC-Save_opWETH-Save.ts b/scripts/harvest/005_claim_and_harvest_opUSDC-Save_opWETH-Save.ts index c4c28a20..6a8c69a4 100644 --- a/scripts/harvest/005_claim_and_harvest_opUSDC-Save_opWETH-Save.ts +++ b/scripts/harvest/005_claim_and_harvest_opUSDC-Save_opWETH-Save.ts @@ -15,9 +15,8 @@ import { ITokenMinter, ITokenMinter__factory, IUniswapV2Router02, - Vault, - Vault__factory, } from "../../typechain"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const curveGuageABI = [ { @@ -118,8 +117,8 @@ async function main() { // const STK_AAVE = ethers.utils.getAddress("0x4da27a545c0c5B758a6BA100e3a049001de870f5") const opUSDCSaveProxyAddress = ethers.utils.getAddress("0x6d8BfdB4c4975bB086fC9027e48D5775f609fF88"); const opWETHSaveProxyAddress = ethers.utils.getAddress("0xff2fbd9fbc6d03baa77cf97a3d5671bea183b9a8"); - const opUSDCSavevaultInstance = await ethers.getContractAt(Vault__factory.abi, opUSDCSaveProxyAddress); - const opWETHSavevaultInstance = await ethers.getContractAt(Vault__factory.abi, opWETHSaveProxyAddress); + const opUSDCSavevaultInstance = await ethers.getContractAt(VaultV6__factory.abi, opUSDCSaveProxyAddress); + const opWETHSavevaultInstance = await ethers.getContractAt(VaultV6__factory.abi, opWETHSaveProxyAddress); const aaveIncentiveControlleInstance = await ethers.getContractAt( aaveIncentiveController, "0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5", diff --git a/scripts/harvest/006_opUSDC-Save_opWETH-Save_multisig.ts b/scripts/harvest/006_opUSDC-Save_opWETH-Save_multisig.ts index 925b1471..5032a6bb 100644 --- a/scripts/harvest/006_opUSDC-Save_opWETH-Save_multisig.ts +++ b/scripts/harvest/006_opUSDC-Save_opWETH-Save_multisig.ts @@ -15,11 +15,10 @@ import { IUniswapV2Router02__factory, Registry, Registry__factory, - Vault, - Vault__factory, } from "../../typechain"; import { BigNumber } from "ethers"; import { hexlify, parseEther, parseUnits } from "ethers/lib/utils"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; // const uniswapV2Router02Address = ethers.utils.getAddress("0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D"); const sushiswapRouterAddress = ethers.utils.getAddress("0xd9e1cE17f2641f24aE83637ab66a2cca9C378B9F"); @@ -55,11 +54,11 @@ const comptrollerABI = [ ]; async function main() { - const opUSDCSave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + const opUSDCSave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); - const opWETHSave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) + const opWETHSave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) ); const registry = ( await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) diff --git a/scripts/harvest/007_BDAMM.ts b/scripts/harvest/007_BDAMM.ts index 07995243..0bd597d8 100644 --- a/scripts/harvest/007_BDAMM.ts +++ b/scripts/harvest/007_BDAMM.ts @@ -10,11 +10,10 @@ import { ICompound__factory, StrategyProvider, StrategyProvider__factory, - Vault, - Vault__factory, } from "../../typechain"; import { BigNumber } from "ethers"; import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const cryptoPoolGaugeABI = [ { @@ -153,17 +152,17 @@ const bdamm = ethers.utils.getAddress("0xfa372fF1547fa1a283B5112a4685F1358CE5574 const bLUSDLUSD3FactoryGaugeAddress = "0xdA0DD1798BE66E17d5aB1Dc476302b56689C2DB4"; async function main() { - const opUSDCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + const opUSDCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); - const opWETHEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) + const opWETHEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) ); - const opWBTCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) + const opWBTCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) ); - const opUSD3Earn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) + const opUSD3Earn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) ); const cUSDCInstanceopUSDCEarn = ( await ethers.getContractAt(ICompound__factory.abi, "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5") diff --git a/scripts/harvest/008_vault_repurpose_harvest.ts b/scripts/harvest/008_vault_repurpose_harvest.ts index 5be916a9..d7aa4330 100644 --- a/scripts/harvest/008_vault_repurpose_harvest.ts +++ b/scripts/harvest/008_vault_repurpose_harvest.ts @@ -11,12 +11,11 @@ import { Registry__factory, StrategyProvider, StrategyProvider__factory, - Vault, - Vault__factory, } from "../../typechain"; import { getRiskProfileCode } from "../../helpers/utils"; import { parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const compoundLensABI = [ { @@ -165,35 +164,35 @@ async function main() { ); // vault instances - const opUSDCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + const opUSDCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); // const opWETHEarn = ( // await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) // ); - const opDAISave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opDAI-Save_Proxy")).address) + const opDAISave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opDAI-Save_Proxy")).address) ); - const opUSDTSave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDT-Save_Proxy")).address) + const opUSDTSave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDT-Save_Proxy")).address) ); - const opWBTCSave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWBTC-Save_Proxy")).address) + const opWBTCSave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Save_Proxy")).address) ); - const opUSD3Earn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) + const opUSD3Earn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) ); - const opWETHSave = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) + const opWETHSave = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) ); - const opWBTCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) + const opWBTCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) ); - const opUSDCInvst = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Invst_Proxy")).address) + const opUSDCInvst = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Invst_Proxy")).address) ); - const opWETHInvst = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) + const opWETHInvst = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) ); // strategy provider diff --git a/scripts/harvest/009_mim_harvest.ts b/scripts/harvest/009_mim_harvest.ts index dda51d27..7ffdefd0 100644 --- a/scripts/harvest/009_mim_harvest.ts +++ b/scripts/harvest/009_mim_harvest.ts @@ -14,30 +14,29 @@ import { ICurveSwap__factory, IUniswapV2Router02, IUniswapV2Router02__factory, - Vault, - Vault__factory, } from "../../typechain"; import { formatUnits, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import { getCVXUnclaimed } from "./003_claim_and_swap_CRV_CVX_to_USDC"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; async function main() { - const opUSDCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + const opUSDCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); - const opUSD3Earn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) + const opUSD3Earn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) ); - const opWETHEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) + const opWETHEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) ); - const opUSDCInvst = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Invst_Proxy")).address) + const opUSDCInvst = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Invst_Proxy")).address) ); - const opWETHInvst = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) + const opWETHInvst = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) ); const sushiswapRouter = ( await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) @@ -117,7 +116,7 @@ async function main() { ); const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - const ldoUnclaimedForopWETHEarn = parseEther("7.022800921361593268"); + const ldoUnclaimedForopWETHEarn = parseEther("15.510000000000000000"); const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); const crvUnclaimedForopUSD3Earn = await cvxMIM3lp3GaugeInstance.earned(opUSD3Earn.address); @@ -136,10 +135,10 @@ async function main() { const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); const sushiBalanceForopWETHInvst = await sushiInstance.balanceOf(opWETHInvst.address); - const oneCRVToUSDC = parseUnits("0.684784", "6"); // crv - usdc - const oneCVXToUSDC = parseUnits("3.9", "6"); - const oneLDOToWETH = parseEther("0.000974892"); - const oneSUSHIToUSDC = parseUnits("1", "6"); + const oneCRVToUSDC = parseUnits("0.59", "6"); // crv - usdc + const oneCVXToUSDC = parseUnits("3.7", "6"); + const oneLDOToWETH = parseEther("0.0008"); + const oneSUSHIToUSDC = parseUnits("1.33", "6"); const oneSUSHIToWETH = parseEther("0.001"); // sushi-dai-weth const expectedMinUSDCFromCRVForopUSDCEarn = BigNumber.from(crvBalanceForopUSDCEarn.add(crvUnclaimedForopUSDCEarn)) @@ -176,169 +175,176 @@ async function main() { ); const transactions: MetaTransactionData[] = [ - // // claim CRV, CVX for opUSDC Earn - // { - // to: cvxMIM3lp3GaugeInstance.address, - // value: "0", - // data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSDCEarn.address, true]), - // }, - // ---- + // claim CRV, CVX for opUSDC Earn + { + to: cvxMIM3lp3GaugeInstance.address, + value: "0", + data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSDCEarn.address, true]), + }, + // // ---- // claim CRV, CVX for opUSD3Earn { to: cvxMIM3lp3GaugeInstance.address, value: "0", data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSD3Earn.address, true]), }, - // ---- - // // claim LDO,CRV,CVX for opWETHEarn - // { - // to: cvxStEthGaugeInstance.address, - // value: "0", - // data: cvxStEthGaugeInstance.interface.encodeFunctionData("getReward", [opWETHEarn.address, true]), - // }, - // // harvest CRV for opUSDCEarn + // // ---- + // claim LDO,CRV,CVX for opWETHEarn + { + to: cvxStEthGaugeInstance.address, + value: "0", + data: cvxStEthGaugeInstance.interface.encodeFunctionData("getReward", [opWETHEarn.address, true]), + }, + // harvest CRV for opUSDCEarn + { + to: opUSDCEarn.address, + value: "0", + data: opUSDCEarn.interface.encodeFunctionData("harvest", [ + EthereumTokens.REWARD_TOKENS.CRV, + uniswapV3Router, + true, + expectedMinUSDCFromCRVForopUSDCEarn, + BigNumber.from(timestamp).add("600"), + [ + /*EthereumTokens.REWARD_TOKENS.CRV, EthereumTokens.PLAIN_TOKENS.DAI, EthereumTokens.PLAIN_TOKENS.USDC*/ + ], + uniV3SwapPathForCRVtoUSDC, + ]), + }, + // // ---- + // // swap CRV to USDC // { - // to: opUSDCEarn.address, + // to: opUSD3Earn.address, // value: "0", - // data: opUSDCEarn.interface.encodeFunctionData("harvest", [ - // EthereumTokens.REWARD_TOKENS.CRV, - // uniswapV3Router, - // true, - // expectedMinUSDCFromCRVForopUSDCEarn, - // BigNumber.from(timestamp).add("600"), + // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ // [ - // /*EthereumTokens.REWARD_TOKENS.CRV, EthereumTokens.PLAIN_TOKENS.DAI, EthereumTokens.PLAIN_TOKENS.USDC*/ + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // sushiswapRouter.address, + // sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ + // crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn), + // expectedMinUSDCFromCRVForopUSD3Earn, + // [ + // EthereumTokens.REWARD_TOKENS.CRV, + // EthereumTokens.PLAIN_TOKENS.DAI, + // EthereumTokens.PLAIN_TOKENS.USDT, + // EthereumTokens.PLAIN_TOKENS.USDC, + // ], + // opUSD3Earn.address, + // BigNumber.from(timestamp).add(600), + // ]), + // ], + // ), // ], - // uniV3SwapPathForCRVtoUSDC, // ]), // }, - // ---- - // swap CRV to USDC + // // ---- + // harvest CVX for opUSDCEarn { - to: opUSD3Earn.address, + to: opUSDCEarn.address, value: "0", - data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ + data: opUSDCEarn.interface.encodeFunctionData("harvest", [ + EthereumTokens.REWARD_TOKENS.CVX, + uniswapV3Router, + true, + expectedMInUSDCFromCVXForopUSDCEarn, + BigNumber.from(timestamp).add("600"), [ - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - sushiswapRouter.address, - sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ - crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn), - expectedMinUSDCFromCRVForopUSD3Earn, - [ - EthereumTokens.REWARD_TOKENS.CRV, - EthereumTokens.WRAPPED_TOKENS.WETH, - EthereumTokens.REWARD_TOKENS.SUSHI, - EthereumTokens.PLAIN_TOKENS.USDC, - ], - opUSD3Earn.address, - BigNumber.from(timestamp).add(600), - ]), - ], - ), + /*EthereumTokens.REWARD_TOKENS.CVX, EthereumTokens.PLAIN_TOKENS.USDC*/ ], + uniV3SwapPathForCVXtoUSDC, ]), }, - // ---- - // // harvest CVX for opUSDCEarn + // // ---- + // // harvest CVX for opUSD3Earn + // // swap CVX to USDC // { - // to: opUSDCEarn.address, + // to: opUSD3Earn.address, // value: "0", - // data: opUSDCEarn.interface.encodeFunctionData("harvest", [ - // EthereumTokens.REWARD_TOKENS.CVX, - // uniswapV3Router, - // true, - // expectedMInUSDCFromCVXForopUSDCEarn, - // BigNumber.from(timestamp).add("600"), - // [/*EthereumTokens.REWARD_TOKENS.CVX, EthereumTokens.PLAIN_TOKENS.USDC*/], - // uniV3SwapPathForCVXtoUSDC, + // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ + // [ + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // sushiswapRouter.address, + // sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ + // cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn), + // expectedMinUSDCFromCVXForopUSD3Earn, + // [ + // EthereumTokens.REWARD_TOKENS.CVX, + // EthereumTokens.WRAPPED_TOKENS.WETH, + // EthereumTokens.REWARD_TOKENS.SUSHI, + // EthereumTokens.PLAIN_TOKENS.USDC, + // ], + // opUSD3Earn.address, + // BigNumber.from(timestamp).add(600), + // ]), + // ], + // ), + // ], // ]), // }, - // ---- - // harvest CVX for opUSD3Earn - // swap CVX to USDC + // // swap USDC to 3crv + // { + // to: opUSD3Earn.address, + // value: "0", + // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ + // [ + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // threeCrvPool.address, + // threeCrvPool.interface.encodeFunctionData("add_liquidity(uint256[3],uint256)", [ + // [ + // 0, + // expectedMinUSDCFromCRVForopUSD3Earn + // .add(expectedMinUSDCFromCVXForopUSD3Earn) + // .add(usdcBalanceForopUSD3Earn), + // 0, + // ], + // "18000000000000000000", + // ]), + // ], + // ), + // ], + // ]), + // }, + // // ---- + // harvest CVX for opWETHEarn + // harvest LDO for opWETHEarn { - to: opUSD3Earn.address, + to: opWETHEarn.address, value: "0", - data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ - [ - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - sushiswapRouter.address, - sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ - cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn), - expectedMinUSDCFromCVXForopUSD3Earn, - [ - EthereumTokens.REWARD_TOKENS.CVX, - EthereumTokens.WRAPPED_TOKENS.WETH, - EthereumTokens.REWARD_TOKENS.SUSHI, - EthereumTokens.PLAIN_TOKENS.USDC, - ], - opUSD3Earn.address, - BigNumber.from(timestamp).add(600), - ]), - ], - ), - ], + data: opWETHEarn.interface.encodeFunctionData("harvest", [ + ldoInstance.address, + sushiswapRouter.address, + false, + expectedMinWETHFromLDOForopWETHEarn, + BigNumber.from(timestamp).add("600"), + [ldoInstance.address, EthereumTokens.WRAPPED_TOKENS.WETH], + "0x", ]), }, - // swap USDC to 3crv + // harvest SUSHI for opWETHInvst { - to: opUSD3Earn.address, + to: opWETHInvst.address, value: "0", - data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ + data: opWETHInvst.interface.encodeFunctionData("harvest", [ + sushiInstance.address, + sushiswapRouter.address, + false, + expectMinWETHFromSUSHIForopWETHInvst, + BigNumber.from(timestamp).add("600"), [ - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - threeCrvPool.address, - threeCrvPool.interface.encodeFunctionData("add_liquidity(uint256[3],uint256)", [ - [ - 0, - expectedMinUSDCFromCRVForopUSD3Earn - .add(expectedMinUSDCFromCVXForopUSD3Earn) - .add(usdcBalanceForopUSD3Earn), - 0, - ], - "11000000000000000000", - ]), - ], - ), + sushiInstance.address, + EthereumTokens.PLAIN_TOKENS.USDT, + EthereumTokens.PLAIN_TOKENS.DAI, + EthereumTokens.WRAPPED_TOKENS.WETH, ], + "0x", ]), }, - // ---- - // harvest CVX for opWETHEarn - // // harvest LDO for opWETHEarn - // { - // to: opWETHEarn.address, - // value: "0", - // data: opWETHEarn.interface.encodeFunctionData("harvest", [ - // ldoInstance.address, - // sushiswapRouter.address, - // false, - // expectedMinWETHFromLDOForopWETHEarn, - // BigNumber.from(timestamp).add("600"), - // [ldoInstance.address, EthereumTokens.WRAPPED_TOKENS.WETH], - // "0x", - // ]), - // }, - // // harvest SUSHI for opWETHInvst - // { - // to: opWETHInvst.address, - // value: "0", - // data: opWETHInvst.interface.encodeFunctionData("harvest", [ - // sushiInstance.address, - // sushiswapRouter.address, - // false, - // expectMinWETHFromSUSHIForopWETHInvst, - // BigNumber.from(timestamp).add("600"), - // [sushiInstance.address, EthereumTokens.PLAIN_TOKENS.DAI, EthereumTokens.WRAPPED_TOKENS.WETH], - // "0x", - // ]), - // }, // // harvest SUSHI for opUSDCInvst // { // to: opUSDCInvst.address, @@ -349,36 +355,36 @@ async function main() { // false, // expectedMinUSDCFromSUSHIForopUSDCInvst, // BigNumber.from(timestamp).add("600"), - // [sushiInstance.address, EthereumTokens.PLAIN_TOKENS.USDC], + // [sushiInstance.address, EthereumTokens.PLAIN_TOKENS.USDT,EthereumTokens.PLAIN_TOKENS.DAI, EthereumTokens.PLAIN_TOKENS.USDC], // "0x", // ]), // }, - // // vaultDepositAllToStrategy for opUSDCEarn - // { - // to: opUSDCEarn.address, - // value: "0", - // data: opUSDCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), - // }, - // ---- - // vaultDepositAllToStrategy for opUSD3Earn + // vaultDepositAllToStrategy for opUSDCEarn { - to: opUSD3Earn.address, + to: opUSDCEarn.address, value: "0", - data: opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy"), + data: opUSDCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), }, - // ---- - // // vaultDepositAllToStrategy for opWETHEarn + // // ---- + // // vaultDepositAllToStrategy for opUSD3Earn // { - // to: opWETHEarn.address, + // to: opUSD3Earn.address, // value: "0", - // data: opWETHEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), - // }, - // // vaultDepositAllToStrategy for opWETHInvst - // { - // to: opWETHInvst.address, - // value: "0", - // data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + // data: opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy"), // }, + // // ---- + // vaultDepositAllToStrategy for opWETHEarn + { + to: opWETHEarn.address, + value: "0", + data: opWETHEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, + // vaultDepositAllToStrategy for opWETHInvst + { + to: opWETHInvst.address, + value: "0", + data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, // // vaultDepositAllToStrategy for opUSDCInvst // { // to: opUSDCInvst.address, @@ -388,8 +394,8 @@ async function main() { ]; const safeOwner = ethers.provider.getSigner(0); - // const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; - const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; + const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; + // const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; const ethAdapter = new EthersAdapter({ ethers, signer: safeOwner, diff --git a/test/test-opty/4_vault_multi_strategy.spec.ts b/test/test-opty/4_vault_multi_strategy.spec.ts index ae696a3f..04c185b4 100644 --- a/test/test-opty/4_vault_multi_strategy.spec.ts +++ b/test/test-opty/4_vault_multi_strategy.spec.ts @@ -1,4 +1,4 @@ -import chai, { expect } from "chai"; +import { expect } from "chai"; import { deployments, ethers } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; @@ -7,7 +7,6 @@ import { Registry, StrategyRegistry, Vault, - IAdapterFull, ERC20, Registry__factory, Vault__factory, @@ -31,10 +30,13 @@ import { BigNumber } from "ethers"; import { TypedTokens } from "../../helpers/data"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; import { formatUnits, getAddress } from "ethers/lib/utils"; +import { solidity } from "ethereum-waffle"; const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; +chai.use(solidity); + describe("Multi-Strategy Vault", () => { before(async function () { await deployments.fixture(); @@ -147,7 +149,7 @@ describe("Multi-Strategy Vault", () => { const steps = strategyDetail.strategy.map(item => ({ pool: item.contract, outputToken: item.outputToken, - isBorrow: item.isBorrow, + isBorrow: item.isSwap, })); let tx = await this.strategyRegistry.connect(strategyOperator).addStrategySteps(strategyHash, steps); await tx.wait(1); @@ -291,7 +293,7 @@ async function deposit( const _BalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); const tx2 = await vaultInstance .connect(signers[i]) - .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", [], []); + .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", []); await tx2.wait(1); const _BalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); expect(_BalanceBefore).gt(_BalanceAfter); diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts index 35669a0e..f5c1d4f0 100644 --- a/test/test-opty/vaultweiroll.spec.ts +++ b/test/test-opty/vaultweiroll.spec.ts @@ -180,5 +180,3 @@ describe("VaultWeiroll", async function () { console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); }); }); - -async function setCompoundUSDCStrategy() {} From 376bbdfa322e235fe4fc7a2ae2438132c7784015 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 13 Dec 2022 19:57:17 -0500 Subject: [PATCH 27/44] feat(strategymanager): add strategy managet class and required interface --- helpers/adapters/AdapterInterface.ts | 64 ++++++ helpers/adapters/ethereum/AaveV1Adapter.ts | 81 ++++---- helpers/adapters/ethereum/AaveV2Adapter.ts | 191 ++++++++++++++---- helpers/adapters/ethereum/CompoundAdapter.ts | 195 ++++++++++++------- helpers/constants/adapters.ts | 8 - helpers/strategy-manager.ts | 114 +++++++++++ helpers/type.d.ts | 1 + scripts/harvest/007_BDAMM.ts | 16 +- scripts/harvest/010_harvest_bDAMM_wbtc.ts | 169 +++++++++------- 9 files changed, 622 insertions(+), 217 deletions(-) create mode 100644 helpers/adapters/AdapterInterface.ts create mode 100644 helpers/strategy-manager.ts diff --git a/helpers/adapters/AdapterInterface.ts b/helpers/adapters/AdapterInterface.ts new file mode 100644 index 00000000..da10e187 --- /dev/null +++ b/helpers/adapters/AdapterInterface.ts @@ -0,0 +1,64 @@ +import { Contract } from "ethers"; +import { Planner as weirollPlanner } from "@weiroll/weiroll.js"; +import { ReturnValue } from "../type"; + +export interface AdapterInterface { + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner; + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner; + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue; + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue; + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + ): ReturnValue; + + getClaimRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + ): weirollPlanner; + + getHarvestRewardsPlan(planner: weirollPlanner, vaultInstance: Contract, vaultUnderlyingToken: string): weirollPlanner; +} diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index 6562e2a5..1abe897a 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -2,36 +2,49 @@ import { Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import Aave from "@optyfi/defi-legos/ethereum/aave/index"; +import { getAddress } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; - -export class AaveV1Adapter { +import { AdapterInterface } from "../AdapterInterface"; +import { ERC20__factory, IWETH9__factory } from "../../../typechain"; +export class AaveV1Adapter implements AdapterInterface { getDepositPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { // TODO handle ETH deposit const lendingPoolContract = new ethers.Contract("0x398eC7346DcD622eDc5ae82352F02bE94C62d119", Aave.LendingPool.abi); const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); - planner.add( - lendingPoolInstance["deposit(address,uint256,uint16)"](inputTokenInstance.address, inputTokenAmount, "0"), - ); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); + planner.add(wethContract["withdraw(uint256)"](inputTokenAmount)); + planner.add( + lendingPoolInstance["deposit(address,uint256,uint16)"](inputToken, inputTokenAmount, "0").withValue( + inputTokenAmount, + ), + ); + } else { + planner.add(lendingPoolInstance["deposit(address,uint256,uint16)"](inputToken, inputTokenAmount, "0")); + } return planner; } getWithdrawPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { // TODO handle ETH withdraw - const lpTokenContract = new ethers.Contract(outputTokenInstance.address, Aave.ATokenAbi); + const lpTokenContract = new ethers.Contract(outputToken, Aave.ATokenAbi); const lpTokenInstance = weirollContract.createContract(lpTokenContract); planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); return planner; @@ -40,12 +53,11 @@ export class AaveV1Adapter { getAmountInInputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, - _amountInInputTokenContractInstance: Contract, - _amountInInputTokenMethod: string, ): ReturnValue { return outputTokenAmount; } @@ -53,12 +65,11 @@ export class AaveV1Adapter { getAmountInOutputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, - _amountInOutputTokenContractInstance: Contract, - _amountInOutputTokenMethod: string, ): ReturnValue { return inputTokenAmount; } @@ -66,31 +77,31 @@ export class AaveV1Adapter { getOutputTokenBalancePlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, ): ReturnValue { + const outputTokenInstance = new ethers.Contract(outputToken, ERC20__factory.abi); const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); return amountLP as ReturnValue; } getClaimRewardsPlan( - planner: weirollPlanner, + _planner: weirollPlanner, _vaultInstance: Contract, - _inputTokenInstance: Contract, - _poolInstance: Contract, - _outputTokenInstance: Contract, + _inputToken: string, + _pool: string, + _outputToken: string, ): weirollPlanner { - // TODO verify any rewards and write claim plan for reward token - return planner; + throw new Error("not implemented"); } getHarvestRewardsPlan( - planner: weirollPlanner, + _planner: weirollPlanner, _vaultInstance: Contract, - _vaulltUnderlyingTokenInstance: Contract, + _vaulltUnderlyingToken: string, ): weirollPlanner { - // TODO write harvest plan if reward token - return planner; + throw new Error("not implemented"); } } diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index 6c5cbee7..59e61056 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -2,15 +2,21 @@ import { Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; +import { AdapterInterface } from "../AdapterInterface"; +import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; +import { getAddress } from "ethers/lib/utils"; -export class Aavev2Adapter { +const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; +export class Aavev2Adapter implements AdapterInterface { getDepositPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { const lendingPoolContract = new ethers.Contract( @@ -20,7 +26,7 @@ export class Aavev2Adapter { const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); planner.add( lendingPoolInstance["deposit(address,uint256,address,uint16)"]( - inputTokenInstance.address, + inputToken, inputTokenAmount, vaultInstance.address, "0", @@ -32,9 +38,10 @@ export class Aavev2Adapter { getWithdrawPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { const lendingPoolContract = new ethers.Contract( @@ -43,11 +50,7 @@ export class Aavev2Adapter { ); const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); planner.add( - lendingPoolInstance["withdraw(address,uint256,address)"]( - inputTokenInstance.address, - outputTokenAmount, - vaultInstance.address, - ), + lendingPoolInstance["withdraw(address,uint256,address)"](inputToken, outputTokenAmount, vaultInstance.address), ); return planner; } @@ -55,12 +58,11 @@ export class Aavev2Adapter { getAmountInInputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, - _amountInInputTokenContractInstance: Contract, - _amountInInputTokenMethod: string, ): ReturnValue { return outputTokenAmount; } @@ -68,12 +70,11 @@ export class Aavev2Adapter { getAmountInOutputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, - _amountInOutputTokenContractInstance: Contract, - _amountInOutputTokenMethod: string, ): ReturnValue { return inputTokenAmount; } @@ -81,31 +82,153 @@ export class Aavev2Adapter { getOutputTokenBalancePlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, ): ReturnValue { + const outputTokenInstance = weirollContract.createContract(new ethers.Contract(outputToken, ERC20__factory.abi)); const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); return amountLP as ReturnValue; } getClaimRewardsPlan( planner: weirollPlanner, - _vaultInstance: Contract, - _inputTokenInstance: Contract, - _poolInstance: Contract, - _outputTokenInstance: Contract, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, ): weirollPlanner { - // TODO verify any rewards and write claim plan for reward token + const aaveIncentiveControllerInstance = weirollContract.createContract( + new ethers.Contract("0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5", aaveIncentiveControllerAbi), + ); + planner.add( + aaveIncentiveControllerInstance["claimRewards(address[],uint256,address)"]( + [outputToken], + ethers.constants.MaxUint256, + vaultInstance.address, + ), + ); return planner; } getHarvestRewardsPlan( planner: weirollPlanner, - _vaultInstance: Contract, - _vaulltUnderlyingTokenInstance: Contract, + vaultInstance: Contract, + vaultUnderlyingToken: string, ): weirollPlanner { - // TODO write harvest plan if reward token + // TODO consider adding minimum amount to receive + const uniswapV3RouterContract = new ethers.Contract( + "0xE592427A0AEce92De3Edee1F18E0157C05861564", + ISwapRouter__factory.abi, + ); + const uniswapV3RouterInstance = weirollContract.createContract(uniswapV3RouterContract); + const rewardContract = new ethers.Contract(STK_AAVE, ERC20__factory.abi); + const rewardInstance = weirollContract.createContract(rewardContract); + const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + let univ3Path; + switch (getAddress(vaultUnderlyingToken)) { + case getAddress(EthereumTokens.PLAIN_TOKENS.USDT): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + STK_AAVE, + 500, + EthereumTokens.REWARD_TOKENS.AAVE, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDT, + ], + ); + break; + } + case getAddress(EthereumTokens.BTC_TOKENS.WBTC): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + STK_AAVE, + 500, + EthereumTokens.REWARD_TOKENS.AAVE, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.BTC_TOKENS.WBTC, + ], + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + STK_AAVE, + 500, + EthereumTokens.REWARD_TOKENS.AAVE, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDC, + ], + ); + break; + } + case getAddress(EthereumTokens.WRAPPED_TOKENS.WETH): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [STK_AAVE, 500, EthereumTokens.REWARD_TOKENS.AAVE, 3000, EthereumTokens.WRAPPED_TOKENS.WETH], + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.DAI): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + STK_AAVE, + 500, + EthereumTokens.REWARD_TOKENS.AAVE, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.DAI, + ], + ); + break; + } + default: { + throw new Error(`not implemented for ${vaultUnderlyingToken}`); + } + } + planner.add( + uniswapV3RouterInstance["exactInput((bytes,address,uint256,uint256,uint256))"]([ + univ3Path, + vaultInstance.address, + ethers.constants.MaxUint256, + rewardAmount, + 0, + ]), + ); return planner; } } + +const aaveIncentiveControllerAbi = [ + { + inputs: [ + { internalType: "address[]", name: "assets", type: "address[]" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "to", type: "address" }, + ], + name: "claimRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_user", type: "address" }], + name: "getUserUnclaimedRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +]; diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index b2db932c..14d0de20 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -3,51 +3,74 @@ import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; import { ReturnValue } from "../../type"; -import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; +import { CompoundAdapter__factory, ERC20__factory, ICompound__factory, IWETH9__factory } from "../../../typechain"; import { getAddress } from "ethers/lib/utils"; +import { AdapterInterface } from "../AdapterInterface"; + +export class CompoundAdapter implements AdapterInterface { + compoundAdapterInstance; + + constructor(compoundAdapterAddress: string) { + this.compoundAdapterInstance = weirollContract.createContract( + new ethers.Contract(compoundAdapterAddress, CompoundAdapter__factory.abi), + ); + } -export class CompoundAdapter { getDepositPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { - // TODO handle ETH deposits - planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); + const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); + planner.add(wethContract["withdraw(uint256)"](inputTokenAmount)); + planner.add(poolInstance["mint()"].withValue(inputTokenAmount)); + } else { + planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); + } return planner; } getWithdrawPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { - // TODO handle ETH withdraws - planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); + const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); + const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); + planner.add(wethContract["deposit(uint256)"].withValue(outputTokenAmount)); + } else { + planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); + } return planner; } getAmountInInputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, outputTokenAmount: ReturnValue, - amountInInputTokenContractInstance: Contract, - amountInInputTokenMethod: string, ): ReturnValue { const amountUT = planner.add( - amountInInputTokenContractInstance[amountInInputTokenMethod]( - inputTokenInstance.address, - poolInstance.address, + this.compoundAdapterInstance["getSomeAmountInToken(address,address,uint256)"]( + inputToken, + pool, outputTokenAmount, ).staticcall(), ); @@ -57,17 +80,16 @@ export class CompoundAdapter { getAmountInOutputTokenPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, inputTokenAmount: ReturnValue, - amountInOutputTokenContractInstance: Contract, - amountInOutputTokenMethod: string, ): ReturnValue { const amountLP = planner.add( - amountInOutputTokenContractInstance[amountInOutputTokenMethod]( - inputTokenInstance.address, - poolInstance.address, + this.compoundAdapterInstance["calculateAmountInLPToken(address,address,uint256)"]( + inputToken, + pool, inputTokenAmount, ).staticcall(), ); @@ -77,10 +99,12 @@ export class CompoundAdapter { getOutputTokenBalancePlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - outputTokenInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, ): ReturnValue { + const outputTokenInstance = weirollContract.createContract(new ethers.Contract(outputToken, ERC20__factory.abi)); const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); return amountLP as ReturnValue; } @@ -88,16 +112,16 @@ export class CompoundAdapter { getClaimRewardsPlan( planner: weirollPlanner, vaultInstance: Contract, - inputTokenInstance: Contract, - poolInstance: Contract, - _outputTokenInstance: Contract, + inputToken: string, + pool: string, + _outputToken: string, ): weirollPlanner { const comptrollerContract = new ethers.Contract(Compound.comptroller.address, comptrollerAbi); const comptrollerInstance = weirollContract.createContract(comptrollerContract); planner.add( comptrollerInstance["claimComp(address[],address[],bool,bool)"]( [vaultInstance.address], - [poolInstance.address], + [pool], false, true, ).staticcall(), @@ -108,45 +132,80 @@ export class CompoundAdapter { getHarvestRewardsPlan( planner: weirollPlanner, vaultInstance: Contract, - vaulltUnderlyingTokenInstance: Contract, + vaultUnderlyingToken: string, ): weirollPlanner { // TODO add read call to oracle for computing minimum expected - // TODO consider uniswapV2 / sushiswap router for swapping - const uniswapV3RouterContract = new ethers.Contract( - "0xE592427A0AEce92De3Edee1F18E0157C05861564", - ISwapRouter__factory.abi, - ); - const uniswapV3RouterInstance = weirollContract.createContract(uniswapV3RouterContract); + const uniswapV2RouterContract = new ethers.Contract(UniswapV2.router02.address, UniswapV2.router02.abi); + const uniswapV2RouterInstance = weirollContract.createContract(uniswapV2RouterContract); const rewardContract = new ethers.Contract(EthereumTokens.REWARD_TOKENS.COMP, ERC20__factory.abi); const rewardInstance = weirollContract.createContract(rewardContract); const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); - let univ3Path; - if (getAddress(vaulltUnderlyingTokenInstance.address) === getAddress(EthereumTokens.PLAIN_TOKENS.USDC)) { - univ3Path = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [ - EthereumTokens.REWARD_TOKENS.COMP, - 3000, - EthereumTokens.WRAPPED_TOKENS.WETH, - 500, - EthereumTokens.PLAIN_TOKENS.USDC, - ], - ); - } else if (getAddress(vaulltUnderlyingTokenInstance.address) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - univ3Path = ethers.utils.solidityPack( - ["address", "uint24", "address"], - [EthereumTokens.REWARD_TOKENS.COMP, 3000, EthereumTokens.WRAPPED_TOKENS.WETH], - ); + + switch (getAddress(vaultUnderlyingToken)) { + case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { + planner.add( + uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( + rewardAmount, + 0, + [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], + vaultInstance.address, + ethers.constants.MaxUint256, + ), + ); + break; + } + case getAddress(EthereumTokens.WRAPPED_TOKENS.WETH): { + planner.add( + uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( + rewardAmount, + 0, + [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH], + vaultInstance.address, + ethers.constants.MaxUint256, + ), + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.DAI): { + planner.add( + uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( + rewardAmount, + 0, + [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.DAI], + vaultInstance.address, + ethers.constants.MaxUint256, + ), + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.USDT): { + planner.add( + uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( + rewardAmount, + 0, + [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDT], + vaultInstance.address, + ethers.constants.MaxUint256, + ), + ); + break; + } + case getAddress(EthereumTokens.BTC_TOKENS.WBTC): { + planner.add( + uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( + rewardAmount, + 0, + [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.BTC_TOKENS.WBTC], + vaultInstance.address, + ethers.constants.MaxUint256, + ), + ); + break; + } + default: { + throw new Error(`not implemented for ${vaultUnderlyingToken}`); + } } - planner.add( - uniswapV3RouterInstance["exactInput((bytes,address,uint256,uint256,uint256))"]([ - univ3Path, - vaultInstance.address, - ethers.constants.MaxUint256, - rewardAmount, - 0, - ]), - ); return planner; } } diff --git a/helpers/constants/adapters.ts b/helpers/constants/adapters.ts index 25a8269a..c11320a1 100644 --- a/helpers/constants/adapters.ts +++ b/helpers/constants/adapters.ts @@ -1,5 +1,3 @@ -import { CompoundAdapter } from "../adapters/ethereum/CompoundAdapter"; - export const AAVE_V1_ADAPTER_NAME: string = "AaveV1Adapter"; export const AAVE_V2_ADAPTER_NAME: string = "AaveV2Adapter"; export const COMPOUND_ADAPTER_NAME: string = "CompoundAdapter"; @@ -27,9 +25,3 @@ export const ADAPTERS = [ CONVEX_ADAPTER_NAME, ]; export const PROTOCOLS = ADAPTERS.map(item => (item === "ConvexFinanceAdapter" ? "Convex" : item.split("Adapter")[0])); - -export const liquidityPoolToAdapter = { - "0x39AA39c021dfbaE8faC545936693aC917d5E7563": new CompoundAdapter(), - "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643": new CompoundAdapter(), - "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9": new CompoundAdapter(), -}; diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts new file mode 100644 index 00000000..df130e0b --- /dev/null +++ b/helpers/strategy-manager.ts @@ -0,0 +1,114 @@ +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; +import { AdapterInterface } from "./adapters/AdapterInterface"; +import { AaveV1Adapter } from "./adapters/ethereum/AaveV1Adapter"; +import { Aavev2Adapter } from "./adapters/ethereum/AaveV2Adapter"; +import { CompoundAdapter } from "./adapters/ethereum/CompoundAdapter"; +import { ReturnValue, STRATEGY_DATA, WeirollPlan } from "./type"; + +export class StrategyManager { + compoundAdapterObj: AdapterInterface; + aaveV1AdapterObj: AdapterInterface; + aaveV2AdapterObj: AdapterInterface; + + public readonly liquidityPoolToAdapter: { [key: string]: AdapterInterface } = {}; + + constructor(compoundAdapterAddress: string) { + this.compoundAdapterObj = new CompoundAdapter(compoundAdapterAddress); + this.aaveV1AdapterObj = new AaveV1Adapter(); + this.aaveV2AdapterObj = new Aavev2Adapter(); + this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; + this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0xC11b1268C1A384e55C48c2391d8d480264A3A7F4"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x39AA39c021dfbaE8faC545936693aC917d5E7563"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x24a42fD28C976A61Df5D00D0599C34c4f90748c8"] = this.aaveV1AdapterObj; + this.liquidityPoolToAdapter["0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; + } + + getDepositPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { + let planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + new ethers.Contract(vaultInstance.address, Vault__factory.abi), + ); + for (const [index, strategyStep] of strategySteps.entries()) { + const adapterObj = this.liquidityPoolToAdapter[strategyStep.contract]; + + if (index > 0) { + const inputTokenContract = weirollContract.createContract( + new ethers.Contract(strategySteps[index - 1].outputToken, ERC20__factory.abi), + ); + const inputTokenAmount = ( + planner.add(inputTokenContract["balanceOf(uint256)"](vaultContract.address).staticcall()) + ); + planner = adapterObj.getDepositPlan( + planner, + vaultInstance, + strategySteps[index - 1].outputToken, + strategyStep.contract, + strategyStep.outputToken, + strategyStep.isSwap, + inputTokenAmount, + ); + } else { + const amountUT = planner.add(vaultContract["getCacheValueUT()"].staticcall()); + planner = adapterObj.getDepositPlan( + planner, + vaultInstance, + underlyingTokenInstance.address, + strategyStep.contract, + strategyStep.outputToken, + strategyStep.isSwap, + amountUT, + ); + } + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getWithdrawPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { + let planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + new ethers.Contract(vaultInstance.address, Vault__factory.abi), + ); + for (const [index, _strategyStep] of strategySteps.entries()) { + const iteratorIndex = strategySteps.length - index - 1; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].contract]; + if (iteratorIndex === strategySteps.length - 1) { + // last step + const amountLP = planner.add(vaultContract.getCacheOraAmountLP().staticcall()); + planner = adapterObj.getWithdrawPlan( + planner, + vaultInstance, + underlyingTokenInstance.address, + strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP as ReturnValue, + ); + } else { + const outputTokenContract = weirollContract.createContract( + new ethers.Contract(strategySteps[iteratorIndex].outputToken, ERC20__factory.abi), + ); + const amountLP = ( + planner.add(outputTokenContract["balanceOf(uint256)"](vaultContract.address).staticcall()) + ); + planner = adapterObj.getWithdrawPlan( + planner, + vaultInstance, + underlyingTokenInstance.address, + strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP, + ); + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + } +} diff --git a/helpers/type.d.ts b/helpers/type.d.ts index a411a4ba..0607cfda 100644 --- a/helpers/type.d.ts +++ b/helpers/type.d.ts @@ -218,6 +218,7 @@ export type PoolRate = { export type WeirollPlan = { commands: string[]; state: string[]; + outputIndex: number; }; export declare class ReturnValue implements Value { diff --git a/scripts/harvest/007_BDAMM.ts b/scripts/harvest/007_BDAMM.ts index 0bd597d8..115d2834 100644 --- a/scripts/harvest/007_BDAMM.ts +++ b/scripts/harvest/007_BDAMM.ts @@ -51,7 +51,7 @@ const cryptoPoolGaugeABI = [ }, ]; -const comptrollerABI = [ +export const comptrollerABI = [ { constant: true, inputs: [{ internalType: "address", name: "", type: "address" }], @@ -204,13 +204,13 @@ async function main() { const unclaimedLUSDInopUSD3Earn = await bLUSDLUSD3FactoryGauge.claimable_reward(opUSD3Earn.address, lusd.address); const totalLusdInopUSD3Earn = lusdBalanceInopUSD3Earn.add(unclaimedLUSDInopUSD3Earn); - console.log("opUSDCEarn ", formatEther(unclaimedCompopUSDCEarn)); - console.log("opWETHEarn ", formatEther(unclaimedCompopWETHEarn)); - console.log("opWBTCEarn ", formatEther(unclaimedCompopWBTCEarn)); + // console.log("opUSDCEarn ", formatEther(unclaimedCompopUSDCEarn)); + // console.log("opWETHEarn ", formatEther(unclaimedCompopWETHEarn)); + // console.log("opWBTCEarn ", formatEther(unclaimedCompopWBTCEarn)); - console.log("accrue opUSDCEarn", formatEther(compBalanceopUSDCEarn)); - console.log("accrue opWETHEarn ", formatEther(compBalanceopWETHEarn)); - console.log("accrue opWBTCEarn ", formatEther(compBalanceopWBTCEarn)); + // console.log("accrue opUSDCEarn", formatEther(compBalanceopUSDCEarn)); + // console.log("accrue opWETHEarn ", formatEther(compBalanceopWETHEarn)); + // console.log("accrue opWBTCEarn ", formatEther(compBalanceopWBTCEarn)); const timestamp = await (await ethers.provider.getBlock("latest")).timestamp; @@ -218,6 +218,8 @@ async function main() { const totalUnharvestedBDAMMopWETHEarn = BigNumber.from(unclaimedCompopWETHEarn).add(compBalanceopWETHEarn); const totalUnharvestedBDAMMopWBTCEarn = BigNumber.from(unclaimedCompopWBTCEarn).add(compBalanceopWBTCEarn); + console.log("totalUnharvestedBDAMMopWBTCEarn ", totalUnharvestedBDAMMopWBTCEarn.toString()); + const oneBDAMMToUSDC = parseUnits("0.26", "6"); const oneBDAMMToWETH = parseEther("0.0001"); const oneBDAMMToWBTC = parseUnits("0.00001", "8"); diff --git a/scripts/harvest/010_harvest_bDAMM_wbtc.ts b/scripts/harvest/010_harvest_bDAMM_wbtc.ts index 87019f92..54e7c627 100644 --- a/scripts/harvest/010_harvest_bDAMM_wbtc.ts +++ b/scripts/harvest/010_harvest_bDAMM_wbtc.ts @@ -8,13 +8,15 @@ import { MetaTransactionData, EthAdapter } from "@gnosis.pm/safe-core-sdk-types" import { ERC20, ERC20__factory, + ISwapRouter, + ISwapRouter__factory, IUniswapV2Router02, IUniswapV2Router02__factory, - Vault, - Vault__factory, } from "../../typechain"; -import { parseUnits } from "ethers/lib/utils"; +import { parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; +import { comptrollerABI } from "./007_BDAMM"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const redemptionABI = [ { @@ -37,19 +39,25 @@ const redemptionABI = [ }, ]; +const uniswapV3Router = ethers.utils.getAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"); async function main() { + const uniswapV3RouterInstance = await ethers.getContractAt(ISwapRouter__factory.abi, uniswapV3Router); + const compoundComptrollerInstance = await ethers.getContractAt( + comptrollerABI, + "0x4F96AB61520a6636331a48A11eaFBA8FB51f74e4", + ); const bdammRedemption = await ethers.getContractAt(redemptionABI, "0x46b825C8Ff734D534e6E40b2fE2C21643eCBF493"); const sushiswapRouter = ( await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) ); - const opWBTCEarn = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) + const opWBTCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) ); const usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); const safeOwner = ethers.provider.getSigner(0); - const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; - // const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; + // const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; + const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; const ethAdapter = new EthersAdapter({ ethers, signer: safeOwner, @@ -68,6 +76,18 @@ async function main() { ], ); const transactions: MetaTransactionData[] = [ + { + to: compoundComptrollerInstance.address, + value: "0", + data: compoundComptrollerInstance.interface.encodeFunctionData("claimComp", [ + [opWBTCEarn.address], + [ + "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80", // dAMM finance - dWBTC + ], + false, + true, + ]), + }, // { // to: opWBTCEarn.address, // value: "0", @@ -80,69 +100,88 @@ async function main() { // ], // ]), // }, - // { - // to: opWBTCEarn.address, - // value: "0", - // data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ - // [ - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // sushiswapRouter.address, - // sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ - // parseUnits("7.516050", "6"), - // parseUnits("0.00047", "8"), - // [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], - // opWBTCEarn.address, - // BigNumber.from(timestamp).add("600"), - // ]), - // ], - // ), - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // EthereumTokens.PLAIN_TOKENS.USDC, - // usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("7.516050", "6")]), - // ], - // ), - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", - // usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("50.107000000000000000")]), - // ], - // ), - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // bdammRedemption.address, - // bdammRedemption.interface.encodeFunctionData("redeem", [ - // parseEther("50.107000000000000000"),// 50.107374749199942111 - // parseUnits("7.516050", "6"), - // ]), - // ], - // ), - // ], - // ]), - // }, { to: opWBTCEarn.address, value: "0", - data: opWBTCEarn.interface.encodeFunctionData("harvest", [ - "0xb3207935FF56120f3499e8aD08461Dd403bF16b8", - "0xE592427A0AEce92De3Edee1F18E0157C05861564", - true, - parseUnits("0.0004", "8"), - BigNumber.from(timestamp).add("600"), - [], - uniV3SwapPathForDAMMtoWBTC, + data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ + [ + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + sushiswapRouter.address, + sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ + parseUnits("28.05", "6"), + parseUnits("0.0016", "8"), + [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], + opWBTCEarn.address, + BigNumber.from(timestamp).add("600"), + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + EthereumTokens.PLAIN_TOKENS.USDC, + usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("28.05", "6")]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", + usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("255")]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + bdammRedemption.address, + bdammRedemption.interface.encodeFunctionData("redeem", [ + parseEther("255"), // 50.107374749199942111 + parseUnits("28.05", "6"), + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + uniswapV3Router, + uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ + { + path: uniV3SwapPathForDAMMtoWBTC, + recipient: opWBTCEarn.address, + deadline: BigNumber.from(timestamp).add("600"), + amountIn: parseEther("255"), + amountOutMinimum: parseUnits("0", "6"), + }, + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [opWBTCEarn.address, opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy")], + ), + ], ]), }, - { - to: opWBTCEarn.address, - value: "0", - data: opWBTCEarn.interface.encodeFunctionData("rebalance"), - }, + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("harvest", [ + // "0xb3207935FF56120f3499e8aD08461Dd403bF16b8", + // "0xE592427A0AEce92De3Edee1F18E0157C05861564", + // true, + // parseUnits("0.0004", "8"), + // BigNumber.from(timestamp).add("600"), + // [], + // uniV3SwapPathForDAMMtoWBTC, + // ]), + // }, + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("rebalance"), + // }, ]; const safeTransaction = await safeSdk.createTransaction(transactions); From 1ca01aabd568f03f1e74016025d774e47b761f7b Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 13 Dec 2022 23:51:34 -0500 Subject: [PATCH 28/44] feat(strategy-manager): add read plans --- .../protocol/earn-protocol-configuration | 2 +- helpers/strategy-manager.ts | 147 +++++++++++++++++- 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/contracts/protocol/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 598b3eb5..1fe7c2b1 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 598b3eb5620c888d32feb3355821d0f6ca7a4530 +Subproject commit 1fe7c2b1fa86d54cff66c8ad31d05f2a86253a83 diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index df130e0b..ba754356 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -42,7 +42,7 @@ export class StrategyManager { new ethers.Contract(strategySteps[index - 1].outputToken, ERC20__factory.abi), ); const inputTokenAmount = ( - planner.add(inputTokenContract["balanceOf(uint256)"](vaultContract.address).staticcall()) + planner.add(inputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) ); planner = adapterObj.getDepositPlan( planner, @@ -95,7 +95,7 @@ export class StrategyManager { new ethers.Contract(strategySteps[iteratorIndex].outputToken, ERC20__factory.abi), ); const amountLP = ( - planner.add(outputTokenContract["balanceOf(uint256)"](vaultContract.address).staticcall()) + planner.add(outputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) ); planner = adapterObj.getWithdrawPlan( planner, @@ -107,8 +107,147 @@ export class StrategyManager { amountLP, ); } - const { commands, state } = planner.plan(); - return { commands, state, outputIndex: 0 }; } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getOraValueUTPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { + const planner = new weirollPlanner(); + const outputTokenContract = weirollContract.createContract( + new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), + ); + const amountLP = planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address)); + let amountUT; + for (const [index, _strategyStep] of strategySteps.entries()) { + const iteratorIndex = strategySteps.length - index - 1; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].contract]; + let inputToken = underlyingTokenInstance.address; + if (iteratorIndex !== 0) { + inputToken = strategySteps[iteratorIndex - 1].outputToken; + } + if (iteratorIndex === strategySteps.length - 1) { + amountUT = ( + adapterObj.getAmountInInputTokenPlan( + planner, + vaultInstance, + inputToken, + strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP, + ) + ); + } else { + amountUT = ( + adapterObj.getAmountInInputTokenPlan( + planner, + vaultInstance, + inputToken, + strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountUT as ReturnValue, + ) + ); + } + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getOraSomeValueLPPlan( + underlyingTokenInstance: ERC20, + strategySteps: STRATEGY_DATA[], + vaultInstance: Vault, + ): WeirollPlan { + const planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + new ethers.Contract(vaultInstance.address, Vault__factory.abi), + ); + const valueUT = planner.add(vaultContract.getCacheExpectedStratWithdrawUT().staticcall()); + let amountLP; + for (const [index, strategyStep] of strategySteps.entries()) { + const adapterObj = this.liquidityPoolToAdapter[strategyStep.contract]; + let inputToken = underlyingTokenInstance.address; + if (index !== 0) { + inputToken = strategySteps[index - 1].outputToken; + } + if (index === 0) { + amountLP = adapterObj.getAmountInOutputTokenPlan( + planner, + vaultInstance, + inputToken, + strategyStep.contract, + strategyStep.outputToken, + strategyStep.isSwap, + amountLP as ReturnValue, + ); + } else { + amountLP = adapterObj.getAmountInOutputTokenPlan( + planner, + vaultInstance, + inputToken, + strategyStep.contract, + strategyStep.outputToken, + strategyStep.isSwap, + valueUT, + ); + } + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getLastStrategyStepBalancePlan( + underlyingTokenInstance: ERC20, + strategySteps: STRATEGY_DATA[], + vaultInstance: Vault, + ): WeirollPlan { + const planner = new weirollPlanner(); + const outputTokenContract = weirollContract.createContract( + new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), + ); + planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address)); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getClaimRewardsPlan( + underlyingTokenInstance: ERC20, + strategySteps: STRATEGY_DATA[], + vaultInstance: Vault, + ): WeirollPlan { + let planner = new weirollPlanner(); + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].contract]; + let inputToken = underlyingTokenInstance.address; + if (strategySteps.length > 1) { + inputToken = strategySteps[strategySteps.length - 2].outputToken; + } + planner = adapterObj.getClaimRewardsPlan( + planner, + vaultInstance, + inputToken, + strategySteps[strategySteps.length - 1].contract, + strategySteps[strategySteps.length - 1].outputToken, + ); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getHarvestRewardsPlan( + underlyingTokenInstance: ERC20, + strategySteps: STRATEGY_DATA[], + vaultInstance: Vault, + ): WeirollPlan { + let planner = new weirollPlanner(); + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].contract]; + let inputToken = underlyingTokenInstance.address; + if (strategySteps.length > 1) { + inputToken = strategySteps[strategySteps.length - 2].outputToken; + } + planner = adapterObj.getHarvestRewardsPlan(planner, vaultInstance, inputToken); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; } } From ee6dde9962abcf6fe068c5addd7484bfaf17fb93 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 14 Dec 2022 23:44:39 -0500 Subject: [PATCH 29/44] refactor(strategy-manager): change argument types --- .../protocol/tokenization/VaultHelper.sol | 9 + deploy/007_deploy_strategyregistry.ts | 92 +++++++ deploy/008_deploy_commandbuilder.ts | 64 +++++ deploy_avalanche/005_deploy_opUSDC-Earn.ts | 4 +- deploy_avalanche/007_deploy_opWAVAX-Earn.ts | 4 +- deploy_avalanche/009_deploy_opUSDC.e-Earn.ts | 4 +- deploy_mainnet/015_deploy_opWETH-Save.ts | 4 +- deploy_mainnet/016_deploy_opWETH-Earn.ts | 4 +- deploy_mainnet/017_deploy_opNEWO-Invst.ts | 4 +- deploy_mainnet/018_deploy_opAAVE-Invst.ts | 4 +- deploy_mainnet/019_deploy_opAPE-Invst.ts | 4 +- deploy_mainnet/020_deploy_opMANA-Invst.ts | 4 +- deploy_mainnet/021_deploy_opSUSHI-Invst.ts | 4 +- deploy_mainnet/022_deploy_opLINK-Invst.ts | 4 +- deploy_mainnet/023_deploy_opENS-Invst.ts | 4 +- deploy_mainnet/024_deploy_opCOMP-Invst.ts | 4 +- deploy_mainnet/025_deploy_opIMX-Invst.ts | 4 +- deploy_mainnet/026_deploy_opALCX-Invst.ts | 4 +- deploy_mainnet/027_deploy_opCRV-Invst.ts | 4 +- deploy_mainnet/028_deploy_opCVX-Invst.ts | 4 +- deploy_mainnet/029_deploy_opYFI-Invst.ts | 4 +- deploy_mainnet/030_deploy_opDAI-Save.ts | 4 +- deploy_mainnet/031_deploy_opUSDT-Save.ts | 4 +- deploy_mainnet/032_deploy_opWBTC-Save.ts | 4 +- deploy_mainnet/033_deploy_opUSD3-Earn.ts | 4 +- deploy_mainnet/034_deploy_opWBTC-Earn.ts | 4 +- deploy_mainnet/035_deploy_opUSDC-Earn.ts | 4 +- deploy_mainnet/036_deploy_opUSDC-Save.ts | 4 +- deploy_mainnet/037_deploy_opUSDC-Invst.ts | 4 +- deploy_mainnet/038_deploy_opWETH-Invst.ts | 4 +- deploy_polygon/011_deploy_opUSDC-Earn.ts | 4 +- deploy_polygon/013_deploy_opWMATIC-Earn.ts | 4 +- helpers/adapters/AdapterInterface.ts | 12 +- helpers/adapters/ethereum/AaveV1Adapter.ts | 19 +- helpers/adapters/ethereum/AaveV2Adapter.ts | 21 +- helpers/adapters/ethereum/CompoundAdapter.ts | 23 +- helpers/constants/essential-contracts-name.ts | 2 + helpers/strategy-manager.ts | 145 ++++++----- helpers/utils.ts | 8 +- test/test-opty/3_vault_strategies.spec.ts | 232 ++++++++++++------ 40 files changed, 535 insertions(+), 208 deletions(-) create mode 100644 contracts/protocol/tokenization/VaultHelper.sol create mode 100644 deploy/007_deploy_strategyregistry.ts create mode 100644 deploy/008_deploy_commandbuilder.ts diff --git a/contracts/protocol/tokenization/VaultHelper.sol b/contracts/protocol/tokenization/VaultHelper.sol new file mode 100644 index 00000000..3fcc97fe --- /dev/null +++ b/contracts/protocol/tokenization/VaultHelper.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +contract VaultHelper { + function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { + return _arg; + } +} diff --git a/deploy/007_deploy_strategyregistry.ts b/deploy/007_deploy_strategyregistry.ts new file mode 100644 index 00000000..58ee4131 --- /dev/null +++ b/deploy/007_deploy_strategyregistry.ts @@ -0,0 +1,92 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { waitforme } from "../helpers/utils"; +import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { Registry } from "../typechain"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.STRATEGY_REGISTRY); + const chainId = await getChainId(); + const networkName = network.name; + const { getAddress } = ethers.utils; + + const registryProxyAddress = (await deployments.get("RegistryProxy")).address; + let feeData = await ethers.provider.getFeeData(); + const result = await deploy("StrategyRegistry", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [registryProxyAddress], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + const registryV2Instance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); + const operatorAddress = await registryV2Instance.operator(); + const operatorSigner = await ethers.getSigner(operatorAddress); + + const oldStrategyRegistry = await registryV2Instance.getStrategyRegistry(); + const strategyRegistry = await deployments.get("StrategyRegistry"); + + console.log("==StrategyRegistry registration=="); + console.log("\n"); + if (getAddress(oldStrategyRegistry) !== getAddress(strategyRegistry.address)) { + console.log("operator registering StrategyRegistry.."); + feeData = await ethers.provider.getFeeData(); + const setStrategyRegistryTx = await registryV2Instance + .connect(operatorSigner) + .setStrategyRegistry(strategyRegistry.address, { + type: 2, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await setStrategyRegistryTx.wait(1); + } else { + console.log("StrategyRegistry already registered"); + console.log("\n"); + } + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const strategyRegistry = await deployments.get("StrategyRegistry"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "StrategyRegistry", + address: strategyRegistry.address, + constructorArguments: [registryProxyAddress], + contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyRegistry.sol:StrategyRegistry", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "StrategyRegistry", + address: strategyRegistry.address, + constructorArguments: [registryProxyAddress], + contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyRegistry.sol:StrategyRegistry", + }); + } + } + } +}; +export default func; +func.tags = ["StrategyRegistry"]; +func.dependencies = ["Registry"]; diff --git a/deploy/008_deploy_commandbuilder.ts b/deploy/008_deploy_commandbuilder.ts new file mode 100644 index 00000000..45ef43e0 --- /dev/null +++ b/deploy/008_deploy_commandbuilder.ts @@ -0,0 +1,64 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { waitforme } from "../helpers/utils"; +import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + network, + tenderly, + run, + ethers, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.COMMAND_BUILDER); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + + const result = await deploy("CommandBuilder", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY === "true") { + if (result.newlyDeployed) { + const commandBuilder = await deployments.get("CommandBuilder"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "CommandBuilder", + address: commandBuilder.address, + constructorArguments: [], + contract: "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "CommandBuilder", + address: commandBuilder.address, + constructorArguments: [], + contract: "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", + }); + } + } + } +}; +export default func; +func.tags = ["CommandBuilder"]; diff --git a/deploy_avalanche/005_deploy_opUSDC-Earn.ts b/deploy_avalanche/005_deploy_opUSDC-Earn.ts index 247aff06..8bb582f7 100644 --- a/deploy_avalanche/005_deploy_opUSDC-Earn.ts +++ b/deploy_avalanche/005_deploy_opUSDC-Earn.ts @@ -20,7 +20,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const chainId = await getChainId(); const networkName = network.name; const feeData = await ethers.provider.getFeeData(); @@ -35,7 +35,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_avalanche/007_deploy_opWAVAX-Earn.ts b/deploy_avalanche/007_deploy_opWAVAX-Earn.ts index b02a177b..40f9a299 100644 --- a/deploy_avalanche/007_deploy_opWAVAX-Earn.ts +++ b/deploy_avalanche/007_deploy_opWAVAX-Earn.ts @@ -20,7 +20,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const chainId = await getChainId(); const networkName = network.name; const feeData = await ethers.provider.getFeeData(); @@ -35,7 +35,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_avalanche/009_deploy_opUSDC.e-Earn.ts b/deploy_avalanche/009_deploy_opUSDC.e-Earn.ts index 8b0d318c..975b8ee8 100644 --- a/deploy_avalanche/009_deploy_opUSDC.e-Earn.ts +++ b/deploy_avalanche/009_deploy_opUSDC.e-Earn.ts @@ -20,7 +20,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const chainId = await getChainId(); const networkName = network.name; const feeData = await ethers.provider.getFeeData(); @@ -36,7 +36,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/015_deploy_opWETH-Save.ts b/deploy_mainnet/015_deploy_opWETH-Save.ts index 29dada57..2fe6f225 100644 --- a/deploy_mainnet/015_deploy_opWETH-Save.ts +++ b/deploy_mainnet/015_deploy_opWETH-Save.ts @@ -24,7 +24,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/016_deploy_opWETH-Earn.ts b/deploy_mainnet/016_deploy_opWETH-Earn.ts index 5be90cea..755e4edf 100644 --- a/deploy_mainnet/016_deploy_opWETH-Earn.ts +++ b/deploy_mainnet/016_deploy_opWETH-Earn.ts @@ -26,7 +26,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/017_deploy_opNEWO-Invst.ts b/deploy_mainnet/017_deploy_opNEWO-Invst.ts index 8c2fc87a..ab7c069d 100644 --- a/deploy_mainnet/017_deploy_opNEWO-Invst.ts +++ b/deploy_mainnet/017_deploy_opNEWO-Invst.ts @@ -25,7 +25,7 @@ const func: DeployFunction = async ({ let chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/018_deploy_opAAVE-Invst.ts b/deploy_mainnet/018_deploy_opAAVE-Invst.ts index 33882949..44b1ae38 100644 --- a/deploy_mainnet/018_deploy_opAAVE-Invst.ts +++ b/deploy_mainnet/018_deploy_opAAVE-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/019_deploy_opAPE-Invst.ts b/deploy_mainnet/019_deploy_opAPE-Invst.ts index 8977e6e4..e0c64a53 100644 --- a/deploy_mainnet/019_deploy_opAPE-Invst.ts +++ b/deploy_mainnet/019_deploy_opAPE-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/020_deploy_opMANA-Invst.ts b/deploy_mainnet/020_deploy_opMANA-Invst.ts index da0e66ef..f5d9a391 100644 --- a/deploy_mainnet/020_deploy_opMANA-Invst.ts +++ b/deploy_mainnet/020_deploy_opMANA-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/021_deploy_opSUSHI-Invst.ts b/deploy_mainnet/021_deploy_opSUSHI-Invst.ts index 2b91cb9e..af017728 100644 --- a/deploy_mainnet/021_deploy_opSUSHI-Invst.ts +++ b/deploy_mainnet/021_deploy_opSUSHI-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/022_deploy_opLINK-Invst.ts b/deploy_mainnet/022_deploy_opLINK-Invst.ts index 7e39dc0b..5bf461d6 100644 --- a/deploy_mainnet/022_deploy_opLINK-Invst.ts +++ b/deploy_mainnet/022_deploy_opLINK-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/023_deploy_opENS-Invst.ts b/deploy_mainnet/023_deploy_opENS-Invst.ts index 8c160ab9..279ce4ae 100644 --- a/deploy_mainnet/023_deploy_opENS-Invst.ts +++ b/deploy_mainnet/023_deploy_opENS-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/024_deploy_opCOMP-Invst.ts b/deploy_mainnet/024_deploy_opCOMP-Invst.ts index de9ee93a..766a16e1 100644 --- a/deploy_mainnet/024_deploy_opCOMP-Invst.ts +++ b/deploy_mainnet/024_deploy_opCOMP-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/025_deploy_opIMX-Invst.ts b/deploy_mainnet/025_deploy_opIMX-Invst.ts index afa1ac5a..10d65921 100644 --- a/deploy_mainnet/025_deploy_opIMX-Invst.ts +++ b/deploy_mainnet/025_deploy_opIMX-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/026_deploy_opALCX-Invst.ts b/deploy_mainnet/026_deploy_opALCX-Invst.ts index 1a568a7f..1967aebd 100644 --- a/deploy_mainnet/026_deploy_opALCX-Invst.ts +++ b/deploy_mainnet/026_deploy_opALCX-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/027_deploy_opCRV-Invst.ts b/deploy_mainnet/027_deploy_opCRV-Invst.ts index b583acfd..c0bcc637 100644 --- a/deploy_mainnet/027_deploy_opCRV-Invst.ts +++ b/deploy_mainnet/027_deploy_opCRV-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/028_deploy_opCVX-Invst.ts b/deploy_mainnet/028_deploy_opCVX-Invst.ts index bc28c4d2..da64a56d 100644 --- a/deploy_mainnet/028_deploy_opCVX-Invst.ts +++ b/deploy_mainnet/028_deploy_opCVX-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/029_deploy_opYFI-Invst.ts b/deploy_mainnet/029_deploy_opYFI-Invst.ts index 17ad7882..a7f01005 100644 --- a/deploy_mainnet/029_deploy_opYFI-Invst.ts +++ b/deploy_mainnet/029_deploy_opYFI-Invst.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -111,7 +111,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/030_deploy_opDAI-Save.ts b/deploy_mainnet/030_deploy_opDAI-Save.ts index 737000b7..64317a50 100644 --- a/deploy_mainnet/030_deploy_opDAI-Save.ts +++ b/deploy_mainnet/030_deploy_opDAI-Save.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/031_deploy_opUSDT-Save.ts b/deploy_mainnet/031_deploy_opUSDT-Save.ts index cd1fc17f..51ccd157 100644 --- a/deploy_mainnet/031_deploy_opUSDT-Save.ts +++ b/deploy_mainnet/031_deploy_opUSDT-Save.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/032_deploy_opWBTC-Save.ts b/deploy_mainnet/032_deploy_opWBTC-Save.ts index ae753fff..1202ecf5 100644 --- a/deploy_mainnet/032_deploy_opWBTC-Save.ts +++ b/deploy_mainnet/032_deploy_opWBTC-Save.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/033_deploy_opUSD3-Earn.ts b/deploy_mainnet/033_deploy_opUSD3-Earn.ts index afb2cf8c..7a014908 100644 --- a/deploy_mainnet/033_deploy_opUSD3-Earn.ts +++ b/deploy_mainnet/033_deploy_opUSD3-Earn.ts @@ -25,7 +25,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(Registry__factory.abi, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -115,7 +115,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/034_deploy_opWBTC-Earn.ts b/deploy_mainnet/034_deploy_opWBTC-Earn.ts index af1e6732..1873fc9b 100644 --- a/deploy_mainnet/034_deploy_opWBTC-Earn.ts +++ b/deploy_mainnet/034_deploy_opWBTC-Earn.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -113,7 +113,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/035_deploy_opUSDC-Earn.ts b/deploy_mainnet/035_deploy_opUSDC-Earn.ts index 375014ad..2aabc805 100644 --- a/deploy_mainnet/035_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/035_deploy_opUSDC-Earn.ts @@ -26,7 +26,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -116,7 +116,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/036_deploy_opUSDC-Save.ts b/deploy_mainnet/036_deploy_opUSDC-Save.ts index 53b11218..e0e3783a 100644 --- a/deploy_mainnet/036_deploy_opUSDC-Save.ts +++ b/deploy_mainnet/036_deploy_opUSDC-Save.ts @@ -23,7 +23,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_mainnet/037_deploy_opUSDC-Invst.ts b/deploy_mainnet/037_deploy_opUSDC-Invst.ts index 99d343d6..9ec03610 100644 --- a/deploy_mainnet/037_deploy_opUSDC-Invst.ts +++ b/deploy_mainnet/037_deploy_opUSDC-Invst.ts @@ -26,7 +26,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_mainnet/038_deploy_opWETH-Invst.ts b/deploy_mainnet/038_deploy_opWETH-Invst.ts index acc50c8a..32788478 100644 --- a/deploy_mainnet/038_deploy_opWETH-Invst.ts +++ b/deploy_mainnet/038_deploy_opWETH-Invst.ts @@ -26,7 +26,7 @@ const func: DeployFunction = async ({ const chainId = await getChainId(); const artifact = await deployments.getArtifact("Vault"); const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const operatorAddress = await registryInstance.getOperator(); const operator = await hre.ethers.getSigner(operatorAddress); @@ -114,7 +114,7 @@ const func: DeployFunction = async ({ }, args: [registryProxyAddress], libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, log: true, skipIfAlreadyDeployed: true, diff --git a/deploy_polygon/011_deploy_opUSDC-Earn.ts b/deploy_polygon/011_deploy_opUSDC-Earn.ts index f1a94e98..f0261f1b 100644 --- a/deploy_polygon/011_deploy_opUSDC-Earn.ts +++ b/deploy_polygon/011_deploy_opUSDC-Earn.ts @@ -27,7 +27,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const registryInstance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const chainId = await getChainId(); const networkName = network.name; @@ -43,7 +43,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/deploy_polygon/013_deploy_opWMATIC-Earn.ts b/deploy_polygon/013_deploy_opWMATIC-Earn.ts index 34983c9e..54243c56 100644 --- a/deploy_polygon/013_deploy_opWMATIC-Earn.ts +++ b/deploy_polygon/013_deploy_opWMATIC-Earn.ts @@ -20,7 +20,7 @@ const func: DeployFunction = async ({ const artifact = await deployments.getArtifact("Vault"); const artifactVaultProxyV2 = await deployments.getArtifact("AdminUpgradeabilityProxy"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); const chainId = await getChainId(); const networkName = network.name; const feeData = await ethers.provider.getFeeData(); @@ -35,7 +35,7 @@ const func: DeployFunction = async ({ log: true, skipIfAlreadyDeployed: true, libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, }, proxy: { owner: admin, diff --git a/helpers/adapters/AdapterInterface.ts b/helpers/adapters/AdapterInterface.ts index da10e187..b2555219 100644 --- a/helpers/adapters/AdapterInterface.ts +++ b/helpers/adapters/AdapterInterface.ts @@ -1,5 +1,6 @@ -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { Planner as weirollPlanner } from "@weiroll/weiroll.js"; +import { JsonRpcProvider } from "@ethersproject/providers"; import { ReturnValue } from "../type"; export interface AdapterInterface { @@ -61,4 +62,13 @@ export interface AdapterInterface { ): weirollPlanner; getHarvestRewardsPlan(planner: weirollPlanner, vaultInstance: Contract, vaultUnderlyingToken: string): weirollPlanner; + + getLPTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise; } diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index 1abe897a..ff2e19bd 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -1,4 +1,4 @@ -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import Aave from "@optyfi/defi-legos/ethereum/aave/index"; @@ -7,6 +7,7 @@ import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; import { ERC20__factory, IWETH9__factory } from "../../../typechain"; +import { JsonRpcProvider } from "@ethersproject/providers"; export class AaveV1Adapter implements AdapterInterface { getDepositPlan( planner: weirollPlanner, @@ -104,4 +105,20 @@ export class AaveV1Adapter implements AdapterInterface { ): weirollPlanner { throw new Error("not implemented"); } + + async getLPTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + const outputTokenInstance = new ethers.Contract( + outputToken, + ERC20__factory.abi, + _provider, + ); + return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + } } diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index 59e61056..00c22943 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -1,12 +1,13 @@ -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { getAddress } from "ethers/lib/utils"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; -import { getAddress } from "ethers/lib/utils"; +import { JsonRpcProvider } from "@ethersproject/providers"; const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; export class Aavev2Adapter implements AdapterInterface { @@ -210,6 +211,22 @@ export class Aavev2Adapter implements AdapterInterface { ); return planner; } + + async getLPTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + const outputTokenInstance = new ethers.Contract( + outputToken, + ERC20__factory.abi, + _provider, + ); + return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + } } const aaveIncentiveControllerAbi = [ diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index 14d0de20..6475f7fd 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -1,4 +1,4 @@ -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import Compound from "@optyfi/defi-legos/ethereum/compound/index"; @@ -8,13 +8,14 @@ import { ReturnValue } from "../../type"; import { CompoundAdapter__factory, ERC20__factory, ICompound__factory, IWETH9__factory } from "../../../typechain"; import { getAddress } from "ethers/lib/utils"; import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; export class CompoundAdapter implements AdapterInterface { compoundAdapterInstance; - constructor(compoundAdapterAddress: string) { + constructor(compoundAdapterInstance: Contract) { this.compoundAdapterInstance = weirollContract.createContract( - new ethers.Contract(compoundAdapterAddress, CompoundAdapter__factory.abi), + new ethers.Contract(compoundAdapterInstance.address, compoundAdapterInstance.interface), ); } @@ -208,6 +209,22 @@ export class CompoundAdapter implements AdapterInterface { } return planner; } + + async getLPTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + const outputTokenInstance = new ethers.Contract( + outputToken, + ERC20__factory.abi, + _provider, + ); + return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + } } const comptrollerAbi = [ diff --git a/helpers/constants/essential-contracts-name.ts b/helpers/constants/essential-contracts-name.ts index e4784479..3d586cc4 100644 --- a/helpers/constants/essential-contracts-name.ts +++ b/helpers/constants/essential-contracts-name.ts @@ -17,4 +17,6 @@ export const ESSENTIAL_CONTRACTS: DATA_OBJECT = { ERC20: "@openzeppelin/contracts-0.8.x/token/ERC20/ERC20.sol:ERC20", ADAPTER: "IAdapterFull", STRATEGY_MANAGER: "contracts/protocol/lib/StrategyManager.sol:StrategyManager", + STRATEGY_REGISTRY: "contracts/protocol/earn-protocol-configuration/contracts/StrategyRegistry.sol:StrategyRegistry", + COMMAND_BUILDER: "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", }; diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index ba754356..c65eb6d9 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -1,21 +1,26 @@ import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; -import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; +import { Contract } from "ethers"; +import { ERC20__factory } from "../typechain"; import { AdapterInterface } from "./adapters/AdapterInterface"; import { AaveV1Adapter } from "./adapters/ethereum/AaveV1Adapter"; import { Aavev2Adapter } from "./adapters/ethereum/AaveV2Adapter"; import { CompoundAdapter } from "./adapters/ethereum/CompoundAdapter"; -import { ReturnValue, STRATEGY_DATA, WeirollPlan } from "./type"; +import { ReturnValue, StrategyStepType, WeirollPlan } from "./type"; export class StrategyManager { - compoundAdapterObj: AdapterInterface; - aaveV1AdapterObj: AdapterInterface; - aaveV2AdapterObj: AdapterInterface; + public readonly compoundAdapterObj: AdapterInterface; + public readonly aaveV1AdapterObj: AdapterInterface; + public readonly aaveV2AdapterObj: AdapterInterface; + public readonly vaultHelperContract: weirollContract; public readonly liquidityPoolToAdapter: { [key: string]: AdapterInterface } = {}; - constructor(compoundAdapterAddress: string) { - this.compoundAdapterObj = new CompoundAdapter(compoundAdapterAddress); + constructor(vaultHelperInstance: Contract, compoundAdapterInstance: Contract) { + this.vaultHelperContract = weirollContract.createContract( + new ethers.Contract(vaultHelperInstance.address, vaultHelperInstance.interface), + ); + this.compoundAdapterObj = new CompoundAdapter(compoundAdapterInstance); this.aaveV1AdapterObj = new AaveV1Adapter(); this.aaveV2AdapterObj = new Aavev2Adapter(); this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; @@ -29,13 +34,13 @@ export class StrategyManager { this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; } - getDepositPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { - let planner = new weirollPlanner(); + getDepositPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { + const planner = new weirollPlanner(); const vaultContract = weirollContract.createContract( - new ethers.Contract(vaultInstance.address, Vault__factory.abi), + new ethers.Contract(vaultInstance.address, vaultInstance.interface), ); for (const [index, strategyStep] of strategySteps.entries()) { - const adapterObj = this.liquidityPoolToAdapter[strategyStep.contract]; + const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; if (index > 0) { const inputTokenContract = weirollContract.createContract( @@ -44,22 +49,22 @@ export class StrategyManager { const inputTokenAmount = ( planner.add(inputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) ); - planner = adapterObj.getDepositPlan( + adapterObj.getDepositPlan( planner, vaultInstance, strategySteps[index - 1].outputToken, - strategyStep.contract, + strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, inputTokenAmount, ); } else { const amountUT = planner.add(vaultContract["getCacheValueUT()"].staticcall()); - planner = adapterObj.getDepositPlan( + adapterObj.getDepositPlan( planner, vaultInstance, - underlyingTokenInstance.address, - strategyStep.contract, + underlyingToken, + strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, amountUT, @@ -70,22 +75,22 @@ export class StrategyManager { return { commands, state, outputIndex: 0 }; } - getWithdrawPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { - let planner = new weirollPlanner(); + getWithdrawPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { + const planner = new weirollPlanner(); const vaultContract = weirollContract.createContract( - new ethers.Contract(vaultInstance.address, Vault__factory.abi), + new ethers.Contract(vaultInstance.address, vaultInstance.interface), ); for (const [index, _strategyStep] of strategySteps.entries()) { const iteratorIndex = strategySteps.length - index - 1; - const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].contract]; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].pool]; if (iteratorIndex === strategySteps.length - 1) { // last step const amountLP = planner.add(vaultContract.getCacheOraAmountLP().staticcall()); - planner = adapterObj.getWithdrawPlan( + adapterObj.getWithdrawPlan( planner, vaultInstance, - underlyingTokenInstance.address, - strategySteps[iteratorIndex].contract, + underlyingToken, + strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, amountLP as ReturnValue, @@ -97,11 +102,11 @@ export class StrategyManager { const amountLP = ( planner.add(outputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) ); - planner = adapterObj.getWithdrawPlan( + adapterObj.getWithdrawPlan( planner, vaultInstance, - underlyingTokenInstance.address, - strategySteps[iteratorIndex].contract, + underlyingToken, + strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, amountLP, @@ -112,7 +117,7 @@ export class StrategyManager { return { commands, state, outputIndex: 0 }; } - getOraValueUTPlan(underlyingTokenInstance: ERC20, strategySteps: STRATEGY_DATA[], vaultInstance: Vault): WeirollPlan { + getOraValueUTPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { const planner = new weirollPlanner(); const outputTokenContract = weirollContract.createContract( new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), @@ -121,8 +126,8 @@ export class StrategyManager { let amountUT; for (const [index, _strategyStep] of strategySteps.entries()) { const iteratorIndex = strategySteps.length - index - 1; - const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].contract]; - let inputToken = underlyingTokenInstance.address; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].pool]; + let inputToken = underlyingToken; if (iteratorIndex !== 0) { inputToken = strategySteps[iteratorIndex - 1].outputToken; } @@ -132,7 +137,7 @@ export class StrategyManager { planner, vaultInstance, inputToken, - strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, amountLP, @@ -144,7 +149,7 @@ export class StrategyManager { planner, vaultInstance, inputToken, - strategySteps[iteratorIndex].contract, + strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, amountUT as ReturnValue, @@ -152,24 +157,25 @@ export class StrategyManager { ); } } + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } getOraSomeValueLPPlan( - underlyingTokenInstance: ERC20, - strategySteps: STRATEGY_DATA[], - vaultInstance: Vault, + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, ): WeirollPlan { const planner = new weirollPlanner(); const vaultContract = weirollContract.createContract( - new ethers.Contract(vaultInstance.address, Vault__factory.abi), + new ethers.Contract(vaultInstance.address, vaultInstance.interface), ); const valueUT = planner.add(vaultContract.getCacheExpectedStratWithdrawUT().staticcall()); let amountLP; for (const [index, strategyStep] of strategySteps.entries()) { - const adapterObj = this.liquidityPoolToAdapter[strategyStep.contract]; - let inputToken = underlyingTokenInstance.address; + const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; + let inputToken = underlyingToken; if (index !== 0) { inputToken = strategySteps[index - 1].outputToken; } @@ -178,7 +184,7 @@ export class StrategyManager { planner, vaultInstance, inputToken, - strategyStep.contract, + strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, amountLP as ReturnValue, @@ -188,61 +194,70 @@ export class StrategyManager { planner, vaultInstance, inputToken, - strategyStep.contract, + strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, valueUT, ); } } + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } getLastStrategyStepBalancePlan( - underlyingTokenInstance: ERC20, - strategySteps: STRATEGY_DATA[], - vaultInstance: Vault, + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, ): WeirollPlan { const planner = new weirollPlanner(); const outputTokenContract = weirollContract.createContract( new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), ); - planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address)); + const amountLP = planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address).staticcall()); + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } getClaimRewardsPlan( - underlyingTokenInstance: ERC20, - strategySteps: STRATEGY_DATA[], - vaultInstance: Vault, + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, ): WeirollPlan { - let planner = new weirollPlanner(); - const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].contract]; - let inputToken = underlyingTokenInstance.address; - if (strategySteps.length > 1) { - inputToken = strategySteps[strategySteps.length - 2].outputToken; + try { + let planner = new weirollPlanner(); + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].pool]; + let inputToken = underlyingToken; + if (strategySteps.length > 1) { + inputToken = strategySteps[strategySteps.length - 2].outputToken; + } + planner = adapterObj.getClaimRewardsPlan( + planner, + vaultInstance, + inputToken, + strategySteps[strategySteps.length - 1].pool, + strategySteps[strategySteps.length - 1].outputToken, + ); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } catch (error: any) { + if (error.message === "not implemented") { + return { commands: [], state: [], outputIndex: 0 }; + } + throw error; } - planner = adapterObj.getClaimRewardsPlan( - planner, - vaultInstance, - inputToken, - strategySteps[strategySteps.length - 1].contract, - strategySteps[strategySteps.length - 1].outputToken, - ); - const { commands, state } = planner.plan(); - return { commands, state, outputIndex: 0 }; } getHarvestRewardsPlan( - underlyingTokenInstance: ERC20, - strategySteps: STRATEGY_DATA[], - vaultInstance: Vault, + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, ): WeirollPlan { let planner = new weirollPlanner(); - const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].contract]; - let inputToken = underlyingTokenInstance.address; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].pool]; + let inputToken = underlyingToken; if (strategySteps.length > 1) { inputToken = strategySteps[strategySteps.length - 2].outputToken; } diff --git a/helpers/utils.ts b/helpers/utils.ts index 55d138fa..2ada7297 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -30,6 +30,7 @@ import { TestStrategyManager, UniswapV2ExchangeAdapter, CurveExchangeAdapter, + StrategyRegistry, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -40,6 +41,8 @@ import { VaultV6 } from "./types/vaultv6"; import { RiskManagerProxyV1 } from "./types/riskManagerv1"; import { RegistryV2 } from "./types/registryV2/RegistryV2"; import { RiskManagerV2 } from "./types/riskManagerv2"; +import { StrategyManager } from "./strategy-manager"; +import { VaultHelper } from "../typechain/VaultHelper"; // function to get the equivalent hash (as generated by the solidity) of data passed in args export function getSoliditySHA3Hash(argTypes: string[], args: any[]): string { @@ -92,13 +95,16 @@ declare module "mocha" { curveDepositPoolAdapter: CurveDepositPoolAdapter; curveSwapPoolAdapter: CurveSwapPoolAdapter; curveSwapEthGateway: CurveSwapETHGateway; - strategyProvider: StrategyProvider; + // strategyProvider: StrategyProvider; + strategyRegistry: StrategyRegistry; + strategyManager: StrategyManager; strategyProviderV1: StrategyProviderV1; riskManagerProxy: RiskManagerProxyV1; riskManager: RiskManager; riskManagerV2: RiskManagerV2; vault: Vault; vaultProxy: InitializableImmutableAdminUpgradeabilityProxy; + vaultHelper: VaultHelper; opUSDCearn: Vault; opUSDCearnProxy: InitializableImmutableAdminUpgradeabilityProxy; opWETHearn: Vault; diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index d4fd2cb4..83c27517 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -3,7 +3,7 @@ import { deployments, ethers } from "hardhat"; import { solidity } from "ethereum-waffle"; import BN from "bignumber.js"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; -import { BigNumber } from "ethers"; +import { BigNumber, Contract } from "ethers"; import { formatEther, formatUnits, getAddress } from "ethers/lib/utils"; import { Signers, to_10powNumber_BN } from "../../helpers/utils"; import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; @@ -11,27 +11,29 @@ import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config" import { Registry, RiskManager, - StrategyProvider, Vault, ERC20, - IAdapterFull, RiskManager__factory, Registry__factory, - StrategyProvider__factory, Vault__factory, - IAdapterFull__factory, ERC20__factory, CurveSwapPoolAdapter, CurveSwapPoolAdapter__factory, CurveSwapETHGateway, CurveSwapETHGateway__factory, IComptroller__factory, + CompoundAdapter__factory, + CompoundAdapter, + StrategyRegistry__factory, } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; -import { setTokenBalanceInStorage, getLastStrategyStepBalanceLP } from "./utils"; +import { setTokenBalanceInStorage } from "./utils"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; import { TypedTokens } from "../../helpers/data"; +import { StrategyManager } from "../../helpers/strategy-manager"; +import { VaultHelper__factory } from "../../typechain/factories/VaultHelper__factory"; +import { VaultHelper } from "../../typechain/VaultHelper"; chai.use(solidity); @@ -41,34 +43,41 @@ const IGNORE_VAULTS = process.env.IGNORE_VAULTS; describe(`${fork}-Vault-rev4`, () => { before(async function () { + ethers.provider; await deployments.fixture(); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - this.signers = {} as Signers; - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.deployer = signers[0]; - this.signers.admin = signers[1]; - this.signers.alice = signers[3]; - this.signers.bob = signers[4]; - this.signers.eve = signers[10]; - this.signers.operator = signers[8]; - this.signers.financeOperator = signers[5]; - this.signers.strategyOperator = signers[7]; this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); const registryProxy = await deployments.get("RegistryProxy"); const riskManagerAddress = (await deployments.get("RiskManager")).address; - const strategyProvider = await deployments.get("StrategyProvider"); + // const strategyProvider = await deployments.get("StrategyProvider"); + this.strategyRegistry = await ethers.getContractAt( + StrategyRegistry__factory.abi, + ( + await deployments.get("StrategyRegistry") + ).address, + ); + this.compoundAdapter = ( + await ethers.getContractAt(CompoundAdapter__factory.abi, (await deployments.get("CompoundAdapter")).address) + ); + this.vaultHelper = ( + await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + ); + this.strategyManager = new StrategyManager(this.vaultHelper, this.compoundAdapter); this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxy.address); this.riskManager = await ethers.getContractAt(RiskManager__factory.abi, riskManagerAddress); - this.strategyProvider = ( - await ethers.getContractAt(StrategyProvider__factory.abi, strategyProvider.address) - ); this.vaults = {}; this.tokens = {}; - const governanceAddress = await this.registry.getGovernance(); - this.signers.governance = await ethers.getSigner(governanceAddress); - const governance = await ethers.getSigner(governanceAddress); - const financeOperatorAddress = await this.registry.getFinanceOperator(); - const financeOperator = await ethers.getSigner(financeOperatorAddress); + this.signers = {} as Signers; + const signers: SignerWithAddress[] = await ethers.getSigners(); + this.signers.deployer = signers[0]; + this.signers.admin = signers[1]; + this.signers.alice = signers[3]; + this.signers.bob = signers[4]; + this.signers.eve = signers[10]; + this.signers.operator = await ethers.getSigner(await this.registry.getOperator()); + this.signers.financeOperator = await ethers.getSigner(await this.registry.getFinanceOperator()); + this.signers.strategyOperator = await ethers.getSigner(await this.registry.getStrategyOperator()); + this.signers.governance = await ethers.getSigner(await this.registry.getGovernance()); for (const riskProfile of Object.keys(MultiChainVaults[fork])) { this.vaults[riskProfile] = {}; for (const token of Object.keys(MultiChainVaults[fork][riskProfile])) { @@ -92,11 +101,11 @@ describe(`${fork}-Vault-rev4`, () => { MultiChainVaults[fork][riskProfile][token].underlyingTokensHash, ); let tx = await this.vaults[riskProfile][token] - .connect(governance) + .connect(this.signers.governance) .setVaultConfiguration(MultiChainVaults[fork][riskProfile][token].vaultConfig); await tx.wait(1); tx = await this.vaults[riskProfile][token] - .connect(financeOperator) + .connect(this.signers.financeOperator) .setValueControlParams( MultiChainVaults[fork][riskProfile][token].userDepositCapUT, MultiChainVaults[fork][riskProfile][token].minimumDepositValueUT, @@ -130,7 +139,7 @@ describe(`${fork}-Vault-rev4`, () => { this.tokens["WETH"] = await ethers.getContractAt(ERC20__factory.abi, TypedTokens["WETH"]); } }); - describe(`${fork}-Vault-rev4 strategies`, () => { + describe(`${fork}-Vault-rev7 strategies`, () => { for (const riskProfile of Object.keys(StrategiesByTokenByChain[fork])) { for (const token of Object.keys(StrategiesByTokenByChain[fork][riskProfile])) { if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { @@ -149,18 +158,51 @@ describe(`${fork}-Vault-rev4`, () => { describe(`${fork}-${riskProfile}-${token}-${strategy}`, () => { before(async function () { - const strategyOperatorAddress = await this.registry.getStrategyOperator(); - const strategyOperator = await ethers.getSigner(strategyOperatorAddress); - const tx = await this.strategyProvider - .connect(strategyOperator) - .setBestStrategy(strategyDetail.riskProfileCode, tokenHash, steps); - await tx.wait(1); - this.adapter = ( - await ethers.getContractAt( - IAdapterFull__factory.abi, - await this.registry.getLiquidityPoolToAdapter(lastPool), - ) - ); + const addStrategyTx = await this.strategyRegistry + .connect(this.signers.operator) + .addStrategy(strategyHash, steps); + await addStrategyTx.wait(1); + const addStrategyPlantx = await this.strategyRegistry + .connect(this.signers.operator) + .addStrategyPlan(this.vaults[riskProfile][token].address, strategyHash, { + oraValueUTPlan: this.strategyManager.getOraValueUTPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + oraValueLPPlan: this.strategyManager.getOraSomeValueLPPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + lastStepBalanceLPPlan: this.strategyManager.getLastStrategyStepBalancePlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + depositSomeToStrategyPlan: this.strategyManager.getDepositPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + withdrawSomeFromStrategyPlan: this.strategyManager.getWithdrawPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + claimRewardsPlan: this.strategyManager.getClaimRewardsPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + harvestRewardsPlan: this.strategyManager.getHarvestRewardsPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + }); + await addStrategyPlantx.wait(1); + if (fork == eEVMNetwork.mainnet) { if ( (await this.registry.getLiquidityPoolToAdapter(lastPool)) == @@ -198,7 +240,9 @@ describe(`${fork}-Vault-rev4`, () => { const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); if (!isConvertToStEth) { DEBUG && console.log("\nStrategyOperator setting convertToStEth"); - const tx = await this.curveSwapEthGateway.connect(strategyOperator).setConvertToStEth(true); + const tx = await this.curveSwapEthGateway + .connect(this.signers.strategyOperator) + .setConvertToStEth(true); await tx.wait(1); } else { DEBUG && console.log("\nStrategyOperator already set convertToStEth"); @@ -211,7 +255,9 @@ describe(`${fork}-Vault-rev4`, () => { const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); if (isConvertToStEth) { DEBUG && console.log("\nStrategyOperator un-setting convertToStEth"); - const tx = await this.curveSwapEthGateway.connect(strategyOperator).setConvertToStEth(false); + const tx = await this.curveSwapEthGateway + .connect(this.signers.strategyOperator) + .setConvertToStEth(false); await tx.wait(1); } else { DEBUG && console.log("\nStrategyOperator already un-set convertToStEth"); @@ -280,12 +326,23 @@ describe(`${fork}-Vault-rev4`, () => { console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); } - const tx = await this.vaults[riskProfile][token].rebalance(); - await tx.wait(1); - expect(await this.vaults[riskProfile][token].getInvestStrategySteps()).to.deep.eq( - steps.map(item => Object.values(item)), - ); - expect(await this.vaults[riskProfile][token].investStrategyHash()).to.eq(strategyHash); + const vaultAddStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.strategyOperator) + .addStrategy(strategyHash); + await vaultAddStrategyTx.wait(1); + + const vaultDepositTx = await this.vaults[riskProfile][token] + .connect(this.signers.strategyOperator) + .vaultDepositSomeToStrategy( + strategyHash, + await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address), + ); + await vaultDepositTx.wait(1); + + // expect(await this.vaults[riskProfile][token].getInvestStrategySteps()).to.deep.eq( + // steps.map(item => Object.values(item)), + // ); + // expect(await this.vaults[riskProfile][token].investStrategyHash()).to.eq(strategyHash); if (DEBUG == true) { console.log("\n"); console.log("After rebalance "); @@ -346,6 +403,7 @@ describe(`${fork}-Vault-rev4`, () => { steps as StrategyStepType[], this.registry, this.tokens["WETH"], + this.strategyManager, ); } else { await withdraw( @@ -355,6 +413,7 @@ describe(`${fork}-Vault-rev4`, () => { steps as StrategyStepType[], this.registry, undefined, + this.strategyManager, ); } }); @@ -370,11 +429,15 @@ describe(`${fork}-Vault-rev4`, () => { const underlyingTokenSymbol = await this.tokens[token].symbol(); const vaultTokenSymbol = await this.vaults[riskProfile][token].symbol(); const vaultBalanceBefore = await this.vaults[riskProfile][token].balanceUT(); - const poolBalanceBefore = await getLastStrategyStepBalanceLP( - steps as StrategyStepType[], - this.registry, + const poolBalanceBefore = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getLPTokenBalance( this.vaults[riskProfile][token], - this.tokens[token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, ); if (DEBUG == true) { console.log("\n"); @@ -426,16 +489,20 @@ describe(`${fork}-Vault-rev4`, () => { ); console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); } - const tx = await this.vaults[riskProfile][token] - .connect(this.signers.financeOperator) - .vaultDepositAllToStrategy(); - await tx.wait(1); + // const tx = await this.vaults[riskProfile][token] + // .connect(this.signers.financeOperator) + // .vaultDepositAllToStrategy(); + // await tx.wait(1); const vaultBalanceAfter = await this.vaults[riskProfile][token].balanceUT(); - const poolBalanceAfter = await getLastStrategyStepBalanceLP( - steps as StrategyStepType[], - this.registry, + const poolBalanceAfter = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getLPTokenBalance( this.vaults[riskProfile][token], - this.tokens[token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, ); expect(vaultBalanceBefore).gt(vaultBalanceAfter); expect(poolBalanceBefore).lt(poolBalanceAfter); @@ -500,6 +567,7 @@ describe(`${fork}-Vault-rev4`, () => { steps as StrategyStepType[], this.registry, this.tokens["WETH"], + this.strategyManager, ); } else { await withdraw( @@ -509,6 +577,7 @@ describe(`${fork}-Vault-rev4`, () => { steps as StrategyStepType[], this.registry, undefined, + this.strategyManager, ); } }); @@ -638,17 +707,22 @@ async function withdraw( steps: StrategyStepType[], registryInstance: Registry, wethTokenInstance: ERC20 | undefined, + strategyManager: StrategyManager, ) { const underlyingTokenSymbol = await underlyingTokenInstance.symbol(); const vaultTokenSymbol = await vaultInstance.symbol(); for (let i = 0; i < signers.length; i++) { const userWithdrawBalance = await vaultInstance.balanceOf(signers[i].address); const userBalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); - const poolBalanceBefore = await getLastStrategyStepBalanceLP( - steps, - registryInstance, + const poolBalanceBefore = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getLPTokenBalance( vaultInstance, - underlyingTokenInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, ); if (DEBUG == true) { console.log("\n"); @@ -698,22 +772,26 @@ async function withdraw( .userWithdrawVault(signers[i].address, userWithdrawBalance.mul(3).div(4), []); await tx.wait(); const userBalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); - const poolBalanceAfter = await getLastStrategyStepBalanceLP( - steps as StrategyStepType[], - registryInstance, + const poolBalanceAfter = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getLPTokenBalance( vaultInstance, - underlyingTokenInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, ); expect(userBalanceBefore).lt(userBalanceAfter); - if ( - ![ - "0xdf3f8ef63f05db6e4b04c3b5a8198d128b61faee8075aed893d76832a0deed6f", //wbtc-DEPOSIT-dAMM-cWBTC has 0% supply APY at fork block - "0x44216c2a6ff5f35d0b24f54cfddb2f39f8ab9a7a998bfa4683aa6083dceb9a9a", - ] //wbtc-DEPOSIT-AaveV2-aWBTC-DEPOSIT-dAMM-dAWBTC has 0% supply APY at fork block - .includes(await vaultInstance.investStrategyHash()) - ) { - expect(poolBalanceBefore).gt(poolBalanceAfter); - } + // if ( + // ![ + // "0xdf3f8ef63f05db6e4b04c3b5a8198d128b61faee8075aed893d76832a0deed6f", //wbtc-DEPOSIT-dAMM-cWBTC has 0% supply APY at fork block + // "0x44216c2a6ff5f35d0b24f54cfddb2f39f8ab9a7a998bfa4683aa6083dceb9a9a", + // ] //wbtc-DEPOSIT-AaveV2-aWBTC-DEPOSIT-dAMM-dAWBTC has 0% supply APY at fork block + // .includes(await vaultInstance.investStrategyHash()) + // ) { + // expect(poolBalanceBefore).gt(poolBalanceAfter); + // } if (DEBUG == true) { console.log("\n"); console.log("After user withdraw "); From 95d4203bc96fc56431f0ab8573dd2006cc9df015 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Fri, 16 Dec 2022 23:02:40 -0500 Subject: [PATCH 30/44] feat(test): save strategy testing --- contracts/protocol/tokenization/Vault.sol | 28 +- contracts/utils/Errors.sol | 54 +-- deploy/009_deploy_vaulthelper.ts | 63 +++ deploy_mainnet/015_deploy_opWETH-Save.ts | 42 +- deploy_mainnet/030_deploy_opDAI-Save.ts | 51 ++- deploy_mainnet/031_deploy_opUSDT-Save.ts | 52 ++- deploy_mainnet/032_deploy_opWBTC-Save.ts | 52 ++- helper-hardhat-config.ts | 2 +- helpers/adapters/AdapterInterface.ts | 22 +- helpers/adapters/ethereum/AaveV1Adapter.ts | 26 +- helpers/adapters/ethereum/AaveV2Adapter.ts | 40 +- helpers/adapters/ethereum/CompoundAdapter.ts | 42 +- helpers/strategy-manager.ts | 116 ++++- helpers/utils.ts | 68 +++ package.json | 7 +- test/test-opty/3_vault_strategies.spec.ts | 422 ++++++++++++++----- test/test-opty/utils.ts | 47 ++- yarn.lock | 8 +- 18 files changed, 951 insertions(+), 191 deletions(-) create mode 100644 deploy/009_deploy_vaulthelper.ts diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 4639e515..5e376103 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -321,9 +321,11 @@ contract Vault is */ function harvestRewards(bytes32 _strategyHash) external payable override { _onlyStrategyOperator(); - // TODO : get the harvest rewards code from StrategyRegistry DataTypes.StrategyPlanInput memory _strategyPlanInput = - IStrategyRegistry(registryContract.getStrategyRegistry()).getClaimRewardsPlan(address(this), _strategyHash); + IStrategyRegistry(registryContract.getStrategyRegistry()).getHarvestRewardsPlan( + address(this), + _strategyHash + ); _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } @@ -365,7 +367,7 @@ contract Vault is _strategyHash, (vaultConfiguration >> 240) & 0xFF ); - strategies.add(_strategyHash); + require(strategies.add(_strategyHash), Errors.ADD_STRATEGY); emit AddStrategy(_strategyHash); } @@ -374,7 +376,8 @@ contract Vault is */ function removeStrategy(bytes32 _strategyHash) external override { _onlyStrategyOperator(); - strategies.remove(_strategyHash); + _vaultWithdrawSomeFromStrategy(_strategyHash, getLastStrategyStepBalanceLP(_strategyHash)); + require(strategies.remove(_strategyHash), Errors.REMOVE_STRATEGY); emit RemoveStrategy(_strategyHash); } @@ -532,6 +535,14 @@ contract Vault is return _cacheAmountLP; } + function getStrategies() external view returns (bytes32[] memory _strategies) { + uint256 _len = strategies.length(); + _strategies = new bytes32[](_len); + for (uint256 _i; _i < _len; _i++) { + _strategies[_i] = strategies.at(_i); + } + } + //===Internal functions===// /* solhint-disable avoid-low-level-calls*/ @@ -826,15 +837,14 @@ contract Vault is /** * @dev Computes the amount in underlying token for the investment made in strategy - * @return amount in underlying token + * @return _sum amount in underlying token */ - function _oraStratValueUT() internal view returns (uint256) { + function _oraStratValueUT() internal view returns (uint256 _sum) { IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); - uint256 _sum; - for (uint256 _i; _i < strategies.length(); _i++) { + uint256 _len = strategies.length(); + for (uint256 _i; _i < _len; _i++) { _sum += _getUint256(_strategyRegistry.getOraValueUTPlan(address(this), strategies.at(_i))); } - return _sum; } function _oraStratValueUTByStrategy(IStrategyRegistry _strategyRegistry, bytes32 _strategyHash) diff --git a/contracts/utils/Errors.sol b/contracts/utils/Errors.sol index eb61202f..47adbe59 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -4,36 +4,26 @@ pragma solidity ^0.6.12; library Errors { string public constant USER_WITHDRAW_INSUFFICIENT_VT = "1"; - string public constant VAULT_DEPOSIT = "2"; - string public constant VAULT_WITHDRAW = "3"; - string public constant EMPTY_STRING = "4"; - string public constant RISK_PROFILE_EXISTS = "5"; - string public constant NOT_A_CONTRACT = "6"; - string public constant TOKEN_NOT_APPROVED = "7"; - string public constant EOA_NOT_WHITELISTED = "8"; - string public constant INSUFFICIENT_OUTPUT_AMOUNT = "9"; - string public constant MINIMUM_USER_DEPOSIT_VALUE_UT = "10"; - string public constant TOTAL_VALUE_LOCKED_LIMIT_UT = "11"; - string public constant USER_DEPOSIT_CAP_UT = "12"; - string public constant VAULT_EMERGENCY_SHUTDOWN = "13"; - string public constant VAULT_PAUSED = "14"; - string public constant ADMIN_CALL = "15"; - string public constant EMERGENCY_BRAKE = "16"; - string public constant UNDERLYING_TOKENS_HASH_EXISTS = "17"; - string public constant TRANSFER_TO_THIS_CONTRACT = "18"; - string public constant UNDERLYING_TOKEN_APPROVED = "19"; - string public constant CLAIM_REWARD_FAILED = "20"; - string public constant NOTHING_TO_CLAIM = "21"; - string public constant PERMIT_FAILED = "22"; - string public constant PERMIT_LEGACY_FAILED = "23"; - string public constant AMOUNT_EXCEEDS_ALLOWANCE = "24"; - string public constant ZERO_ADDRESS_NOT_VALID = "25"; - string public constant INVALID_EXPIRATION = "26"; - string public constant INVALID_SIGNATURE = "27"; - string public constant LENGTH_MISMATCH = "28"; - string public constant BUFFER_EXCEEDED = "29"; - string public constant STRATEGY_NOT_SET = "30"; - string public constant WITHDRAWAL_TOO_LARGE = "31"; - string public constant RETURNED_UT_TOO_FEW = "32"; - string public constant RETURNED_LP_TOO_FEW = "33"; + string public constant EMPTY_STRING = "2"; + string public constant RISK_PROFILE_EXISTS = "3"; + string public constant EOA_NOT_WHITELISTED = "4"; + string public constant MINIMUM_USER_DEPOSIT_VALUE_UT = "4"; + string public constant TOTAL_VALUE_LOCKED_LIMIT_UT = "5"; + string public constant USER_DEPOSIT_CAP_UT = "6"; + string public constant VAULT_EMERGENCY_SHUTDOWN = "7"; + string public constant VAULT_PAUSED = "8"; + string public constant EMERGENCY_BRAKE = "9"; + string public constant UNDERLYING_TOKENS_HASH_EXISTS = "10"; + string public constant TRANSFER_TO_THIS_CONTRACT = "11"; + string public constant UNDERLYING_TOKEN_APPROVED = "12"; + string public constant NOTHING_TO_CLAIM = "13"; + string public constant PERMIT_FAILED = "14"; + string public constant PERMIT_LEGACY_FAILED = "15"; + string public constant ZERO_ADDRESS_NOT_VALID = "16"; + string public constant INVALID_EXPIRATION = "17"; + string public constant INVALID_SIGNATURE = "18"; + string public constant LENGTH_MISMATCH = "19"; + string public constant STRATEGY_NOT_SET = "20"; + string public constant ADD_STRATEGY = "21"; + string public constant REMOVE_STRATEGY = "22"; } diff --git a/deploy/009_deploy_vaulthelper.ts b/deploy/009_deploy_vaulthelper.ts new file mode 100644 index 00000000..e145130a --- /dev/null +++ b/deploy/009_deploy_vaulthelper.ts @@ -0,0 +1,63 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { waitforme } from "../helpers/utils"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact("VaultHelper"); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + const result = await deploy("VaultHelper", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const vaultHelper = await deployments.get("VaultHelper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "VaultHelper", + address: vaultHelper.address, + constructorArguments: [], + contract: "VaultHelper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "VaultHelper", + address: vaultHelper.address, + constructorArguments: [], + contract: "VaultHelper", + }); + } + } + } +}; +export default func; +func.tags = ["VaultHelper"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/015_deploy_opWETH-Save.ts b/deploy_mainnet/015_deploy_opWETH-Save.ts index 2fe6f225..5cb4f921 100644 --- a/deploy_mainnet/015_deploy_opWETH-Save.ts +++ b/deploy_mainnet/015_deploy_opWETH-Save.ts @@ -2,10 +2,13 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; +import { getAddress, parseEther } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -153,7 +156,42 @@ const func: DeployFunction = async ({ } } } + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) + ); + const approvalTokens = []; + const approvalSpender = []; + + const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const awethInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.weth.lpToken); + + const wethAaveV2Allowance = await wethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const awethAaveV2Allowance = await awethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + if (!wethAaveV2Allowance.gt(parseEther("1000000"))) { + approvalTokens.push(wethInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!awethAaveV2Allowance.gt(parseEther("1000000"))) { + approvalTokens.push(awethInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const governanceSigner = await hre.ethers.getSigner(await registryInstance.getGovernance()); + if (getAddress(governanceSigner.address) === getAddress(deployer)) { + const tx = await vaultInstance.connect(governanceSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["opWETH-Save"]; -func.dependencies = ["Registry"]; +func.dependencies = ["Registry", "CommandBuilder"]; diff --git a/deploy_mainnet/030_deploy_opDAI-Save.ts b/deploy_mainnet/030_deploy_opDAI-Save.ts index 64317a50..3d42ba14 100644 --- a/deploy_mainnet/030_deploy_opDAI-Save.ts +++ b/deploy_mainnet/030_deploy_opDAI-Save.ts @@ -2,10 +2,14 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; +import { getAddress, parseEther } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -149,7 +153,50 @@ const func: DeployFunction = async ({ } } } + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opDAI-Save_Proxy")).address) + ); + const approvalTokens = []; + const approvalSpender = []; + + const daiInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.DAI); + const adaiInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.dai.lpToken); + const cdaiInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.dai.pool); + + const daiAaveV2Allowance = await daiInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const adaiAaveV2Allowance = await adaiInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + const daiCompoundAllowance = await daiInstance.allowance(vaultInstance.address, cdaiInstance.address); + + if (!daiAaveV2Allowance.gt(parseEther("1000000"))) { + approvalTokens.push(daiInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!adaiAaveV2Allowance.gt(parseEther("1000000"))) { + approvalTokens.push(adaiInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!daiCompoundAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(daiInstance.address); + approvalSpender.push(Compound.pools.dai.pool); + } + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const governanceSigner = await hre.ethers.getSigner(await registryInstance.getGovernance()); + if (getAddress(governanceSigner.address) === getAddress(deployer)) { + const tx = await vaultInstance.connect(governanceSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["opDAI-Save"]; -func.dependencies = ["Registry"]; +func.dependencies = ["Registry", "CommandBuilder"]; diff --git a/deploy_mainnet/031_deploy_opUSDT-Save.ts b/deploy_mainnet/031_deploy_opUSDT-Save.ts index 51ccd157..68487e45 100644 --- a/deploy_mainnet/031_deploy_opUSDT-Save.ts +++ b/deploy_mainnet/031_deploy_opUSDT-Save.ts @@ -2,10 +2,14 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; +import { getAddress, parseUnits } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -149,7 +153,51 @@ const func: DeployFunction = async ({ } } } + + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDT-Save_Proxy")).address) + ); + const approvalTokens = []; + const approvalSpender = []; + + const usdtInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDT); + const ausdtInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.usdt.lpToken); + const cusdtInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.usdt.pool); + + const usdtAaveV2Allowance = await usdtInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const ausdtAaveV2Allowance = await ausdtInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + const usdtCompoundAllowance = await usdtInstance.allowance(vaultInstance.address, cusdtInstance.address); + + if (!usdtAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdtInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!ausdtAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(ausdtInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!usdtCompoundAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdtInstance.address); + approvalSpender.push(Compound.pools.usdt.pool); + } + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const governanceSigner = await hre.ethers.getSigner(await registryInstance.getGovernance()); + if (getAddress(governanceSigner.address) === getAddress(deployer)) { + const tx = await vaultInstance.connect(governanceSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["opUSDT-Save"]; -func.dependencies = ["Registry"]; +func.dependencies = ["Registry", "CommandBuilder"]; diff --git a/deploy_mainnet/032_deploy_opWBTC-Save.ts b/deploy_mainnet/032_deploy_opWBTC-Save.ts index 1202ecf5..401679ef 100644 --- a/deploy_mainnet/032_deploy_opWBTC-Save.ts +++ b/deploy_mainnet/032_deploy_opWBTC-Save.ts @@ -2,10 +2,14 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; +import { getAddress, parseUnits } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -149,7 +153,51 @@ const func: DeployFunction = async ({ } } } + + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWBTC-Save_Proxy")).address) + ); + const approvalTokens = []; + const approvalSpender = []; + + const wbtcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.BTC_TOKENS.WBTC); + const awbtcInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.wbtc.lpToken); + const cwbtcInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.wbtc.pool); + + const wbtcAaveV2Allowance = await wbtcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const awbtcAaveV2Allowance = await awbtcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + const wbtcCompoundAllowance = await wbtcInstance.allowance(vaultInstance.address, cwbtcInstance.address); + + if (!wbtcAaveV2Allowance.gt(parseUnits("1000000", "8"))) { + approvalTokens.push(wbtcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!awbtcAaveV2Allowance.gt(parseUnits("1000000", "8"))) { + approvalTokens.push(awbtcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!wbtcCompoundAllowance.gt(parseUnits("1000000", "8"))) { + approvalTokens.push(wbtcInstance.address); + approvalSpender.push(Compound.pools.wbtc.pool); + } + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const governanceSigner = await hre.ethers.getSigner(await registryInstance.getGovernance()); + if (getAddress(governanceSigner.address) === getAddress(deployer)) { + const tx = await vaultInstance.connect(governanceSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["opWBTC-Save"]; -func.dependencies = ["Registry"]; +func.dependencies = ["Registry", "CommandBuilder"]; diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index 2576be35..834d5396 100644 --- a/helper-hardhat-config.ts +++ b/helper-hardhat-config.ts @@ -96,7 +96,7 @@ export const NETWORKS_DEFAULT_GAS: iEVMParamsPerNetwork = { }; export const BLOCK_TO_FORK: iEVMParamsPerNetwork = { - [eEVMNetwork.mainnet]: 16087193, + [eEVMNetwork.mainnet]: 16195489, [eEVMNetwork.kovan]: 29962003, [eEVMNetwork.ropsten]: undefined, [eEVMNetwork.hardhat]: undefined, diff --git a/helpers/adapters/AdapterInterface.ts b/helpers/adapters/AdapterInterface.ts index b2555219..aaca0f88 100644 --- a/helpers/adapters/AdapterInterface.ts +++ b/helpers/adapters/AdapterInterface.ts @@ -63,7 +63,7 @@ export interface AdapterInterface { getHarvestRewardsPlan(planner: weirollPlanner, vaultInstance: Contract, vaultUnderlyingToken: string): weirollPlanner; - getLPTokenBalance( + getOutputTokenBalance( vaultInstance: Contract, inputToken: string, pool: string, @@ -71,4 +71,24 @@ export interface AdapterInterface { _isSwap: boolean, _provider: JsonRpcProvider, ): Promise; + + getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + isSwap: boolean, + provider: JsonRpcProvider, + ): Promise; + + getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + isSwap: boolean, + provider: JsonRpcProvider, + ): Promise; } diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index ff2e19bd..fed561cd 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -106,7 +106,7 @@ export class AaveV1Adapter implements AdapterInterface { throw new Error("not implemented"); } - async getLPTokenBalance( + async getOutputTokenBalance( vaultInstance: Contract, inputToken: string, pool: string, @@ -121,4 +121,28 @@ export class AaveV1Adapter implements AdapterInterface { ); return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return outputTokenAmount; + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return inputTokenAmount; + } } diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index 00c22943..ee83a148 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -6,7 +6,7 @@ import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { getAddress } from "ethers/lib/utils"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; -import { ERC20__factory, ISwapRouter__factory } from "../../../typechain"; +import { ERC20__factory, ISwapRouter, ISwapRouter__factory } from "../../../typechain"; import { JsonRpcProvider } from "@ethersproject/providers"; const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; @@ -20,10 +20,7 @@ export class Aavev2Adapter implements AdapterInterface { isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { - const lendingPoolContract = new ethers.Contract( - "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", - AaveV2.LendingPool.abi, - ); + const lendingPoolContract = new ethers.Contract(AaveV2.LendingPool.address, AaveV2.LendingPool.abi); const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); planner.add( lendingPoolInstance["deposit(address,uint256,address,uint16)"]( @@ -45,10 +42,7 @@ export class Aavev2Adapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { - const lendingPoolContract = new ethers.Contract( - "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9", - AaveV2.LendingPool.abi, - ); + const lendingPoolContract = new ethers.Contract(AaveV2.LendingPool.address, AaveV2.LendingPool.abi); const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); planner.add( lendingPoolInstance["withdraw(address,uint256,address)"](inputToken, outputTokenAmount, vaultInstance.address), @@ -205,14 +199,14 @@ export class Aavev2Adapter implements AdapterInterface { univ3Path, vaultInstance.address, ethers.constants.MaxUint256, - rewardAmount, + 0, // TODO add reward amount 0, ]), ); return planner; } - async getLPTokenBalance( + async getOutputTokenBalance( vaultInstance: Contract, inputToken: string, pool: string, @@ -227,6 +221,30 @@ export class Aavev2Adapter implements AdapterInterface { ); return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return outputTokenAmount; + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return inputTokenAmount; + } } const aaveIncentiveControllerAbi = [ diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index 6475f7fd..b8180f40 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -5,8 +5,8 @@ import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; import { ReturnValue } from "../../type"; -import { CompoundAdapter__factory, ERC20__factory, ICompound__factory, IWETH9__factory } from "../../../typechain"; -import { getAddress } from "ethers/lib/utils"; +import { ERC20__factory, ICompound__factory, IWETH9__factory } from "../../../typechain"; +import { getAddress, parseEther } from "ethers/lib/utils"; import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; @@ -210,7 +210,7 @@ export class CompoundAdapter implements AdapterInterface { return planner; } - async getLPTokenBalance( + async getOutputTokenBalance( vaultInstance: Contract, inputToken: string, pool: string, @@ -225,6 +225,42 @@ export class CompoundAdapter implements AdapterInterface { ); return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + const cTokenInstance = new ethers.Contract( + pool, + ICompound__factory.abi, + _provider, + ); + const exchangeRateStored = await cTokenInstance.exchangeRateStored(); + return outputTokenAmount.mul(exchangeRateStored).div(parseEther("1")); + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + const cTokenInstance = new ethers.Contract( + pool, + ICompound__factory.abi, + _provider, + ); + const exchangeRateStored = await cTokenInstance.exchangeRateStored(); + return inputTokenAmount.mul(parseEther("1")).div(exchangeRateStored); + } } const comptrollerAbi = [ diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index c65eb6d9..eaa104a7 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -1,6 +1,7 @@ import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; -import { Contract } from "ethers"; +import { BigNumber, Contract } from "ethers"; +import { JsonRpcProvider } from "@ethersproject/providers"; import { ERC20__factory } from "../typechain"; import { AdapterInterface } from "./adapters/AdapterInterface"; import { AaveV1Adapter } from "./adapters/ethereum/AaveV1Adapter"; @@ -59,7 +60,7 @@ export class StrategyManager { inputTokenAmount, ); } else { - const amountUT = planner.add(vaultContract["getCacheValueUT()"].staticcall()); + const amountUT = planner.add(vaultContract["getCacheValueUT()"]().staticcall()); adapterObj.getDepositPlan( planner, vaultInstance, @@ -85,7 +86,7 @@ export class StrategyManager { const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].pool]; if (iteratorIndex === strategySteps.length - 1) { // last step - const amountLP = planner.add(vaultContract.getCacheOraAmountLP().staticcall()); + const amountLP = planner.add(vaultContract.getCacheAmountLP().staticcall()); adapterObj.getWithdrawPlan( planner, vaultInstance, @@ -122,7 +123,9 @@ export class StrategyManager { const outputTokenContract = weirollContract.createContract( new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), ); - const amountLP = planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address)); + const amountLP = ( + planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address).staticcall()) + ); let amountUT; for (const [index, _strategyStep] of strategySteps.entries()) { const iteratorIndex = strategySteps.length - index - 1; @@ -171,7 +174,7 @@ export class StrategyManager { const vaultContract = weirollContract.createContract( new ethers.Contract(vaultInstance.address, vaultInstance.interface), ); - const valueUT = planner.add(vaultContract.getCacheExpectedStratWithdrawUT().staticcall()); + const valueUT = planner.add(vaultContract.getCacheValueUT().staticcall()); let amountLP; for (const [index, strategyStep] of strategySteps.entries()) { const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; @@ -187,7 +190,7 @@ export class StrategyManager { strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, - amountLP as ReturnValue, + valueUT, ); } else { amountLP = adapterObj.getAmountInOutputTokenPlan( @@ -197,7 +200,7 @@ export class StrategyManager { strategyStep.pool, strategyStep.outputToken, strategyStep.isSwap, - valueUT, + amountLP as ReturnValue, ); } } @@ -255,14 +258,97 @@ export class StrategyManager { strategySteps: StrategyStepType[], vaultInstance: Contract, ): WeirollPlan { - let planner = new weirollPlanner(); - const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].pool]; - let inputToken = underlyingToken; - if (strategySteps.length > 1) { - inputToken = strategySteps[strategySteps.length - 2].outputToken; + const planner = new weirollPlanner(); + try { + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].pool]; + adapterObj.getHarvestRewardsPlan(planner, vaultInstance, underlyingToken); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } catch (error: any) { + if (error.message === `not implemented for ${underlyingToken}`) { + return { commands: [], state: [], outputIndex: 0 }; + } + return error; } - planner = adapterObj.getHarvestRewardsPlan(planner, vaultInstance, inputToken); - const { commands, state } = planner.plan(); - return { commands, state, outputIndex: 0 }; + } + + async getValueInInputToken( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + outputTokenAmount: BigNumber, + provider: JsonRpcProvider, + ): Promise { + let amountUT: BigNumber = BigNumber.from("0"); + for (const [index, _strategyStep] of strategySteps.entries()) { + const iteratorIndex = strategySteps.length - 1 - index; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].pool]; + let inputToken = underlyingToken; + const outputToken = strategySteps[iteratorIndex].outputToken; + if (iteratorIndex !== 0) { + inputToken = strategySteps[iteratorIndex - 1].outputToken; + } + if (iteratorIndex === strategySteps.length - 1) { + amountUT = await adapterObj.getValueInInputToken( + vaultInstance, + inputToken, + strategySteps[iteratorIndex].pool, + outputToken, + outputTokenAmount, + strategySteps[iteratorIndex].isSwap, + provider, + ); + } else { + amountUT = await adapterObj.getValueInInputToken( + vaultInstance, + inputToken, + strategySteps[iteratorIndex].pool, + outputToken, + amountUT, + strategySteps[iteratorIndex].isSwap, + provider, + ); + } + } + return amountUT; + } + + async getValueInOutputToken( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + inputTokenAmount: BigNumber, + provider: JsonRpcProvider, + ): Promise { + let amountLP: BigNumber = BigNumber.from("0"); + for (const [index, strategyStep] of strategySteps.entries()) { + const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; + let inputToken = underlyingToken; + if (index !== 0) { + inputToken = strategySteps[index - 1].outputToken; + } + if (index === 0) { + amountLP = await adapterObj.getValueInOutputToken( + vaultInstance, + inputToken, + strategyStep.pool, + strategyStep.outputToken, + inputTokenAmount, + strategyStep.isSwap, + provider, + ); + } else { + amountLP = await adapterObj.getValueInOutputToken( + vaultInstance, + inputToken, + strategyStep.pool, + strategyStep.outputToken, + amountLP, + strategyStep.isSwap, + provider, + ); + } + } + return amountLP; } } diff --git a/helpers/utils.ts b/helpers/utils.ts index 2ada7297..a225862f 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -43,6 +43,9 @@ import { RegistryV2 } from "./types/registryV2/RegistryV2"; import { RiskManagerV2 } from "./types/riskManagerv2"; import { StrategyManager } from "./strategy-manager"; import { VaultHelper } from "../typechain/VaultHelper"; +import { StrategyStepType } from "./type"; +import { JsonRpcProvider } from "@ethersproject/providers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; // function to get the equivalent hash (as generated by the solidity) of data passed in args export function getSoliditySHA3Hash(argTypes: string[], args: any[]): string { @@ -344,6 +347,71 @@ export function assertVaultConfiguration( expect(getAllowWhitelistState(vaultConfigurationV2)).to.eq(expectedAllowWhitelistedState); } +export async function assertPostUserDepositState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], + provider: JsonRpcProvider, + userBalanceBeforeUT: BigNumber, + _userDepositInDecimals: BigNumber, + vaultTotalSupplyBeforeVT: BigNumber, + vaultValueBeforeUT: BigNumber, + userBalanceBeforeVT: BigNumber, +): Promise { + const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signer.address); + const userBalanceAfterVT = await vaultInstance.balanceOf(signer.address); + const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + provider, + ); + const ppsAfter = await vaultInstance.getPricePerFullShare(); + const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); + const expectedUserBalanceUT = userBalanceBeforeUT.sub(_userDepositInDecimals); + let expectedUserVT: BigNumber = BigNumber.from(0); + if (vaultTotalSupplyBeforeVT.eq(0) || vaultValueBeforeUT.eq(0)) { + expectedUserVT = _userDepositInDecimals; + } else { + expectedUserVT = _userDepositInDecimals.mul(vaultTotalSupplyBeforeVT).div(vaultValueBeforeUT); + } + const actualUserMintedVT = userBalanceAfterVT.sub(userBalanceBeforeVT); + const expectedTotalSupplyVT = vaultTotalSupplyBeforeVT.add(expectedUserVT); + const expectedPPS = expectedTotalSupplyVT.eq("0") + ? BigNumber.from("0") + : ( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + vaultBalanceAfterLP, + provider, + ) + ) + .add(vaultBalanceAfterUT) + .mul(parseEther("1")) + .div(expectedTotalSupplyVT); + + expect(userBalanceBeforeUT).gt(userBalanceAfterUT); + expect(userBalanceAfterUT).to.eq(expectedUserBalanceUT); + expect(actualUserMintedVT).to.closeTo( + expectedUserVT, + parseUnits("9", (await vaultInstance.decimals()) / 2).toNumber(), + ); + expect(vaultTotalSupplyAfterVT).to.closeTo( + expectedTotalSupplyVT, + parseUnits("9", (await vaultInstance.decimals()) / 2).toNumber(), + ); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); +} + export function getDepositFeeUT(vaultConfigurationV2: BigNumber | string): BigNumber { return BigNumber.from(BigInt(vaultConfigurationV2.toString()) & BigInt(65535)); } diff --git a/package.json b/package.json index ad1e7ce3..ffce43cc 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "@types/node": "^16.10.2", "@typescript-eslint/eslint-plugin": "^4.32.0", "@typescript-eslint/parser": "^4.32.0", - "@weiroll/weiroll.js": "^0.3.0", + "@weiroll/weiroll.js": "github:weiroll/weiroll.js#main", "bignumber.js": "^9.0.2", "chai": "^4.3.4", "commitizen": "^4.2.4", @@ -114,7 +114,7 @@ "test-fast:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/2_vault.spec.ts --deploy-fixture", "test-fast:emergency-brake": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/emergency-brake.spec.ts --deploy-fixture", "test-fast:vault-strategy:polygon": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=polygon NETWORK_NAME=polygon hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", - "test-fast:vault-strategy:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/vaultweiroll.spec.ts", + "test-fast:vault-strategy:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", "test-fast:vault-strategy:avalanche": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=avalanche NETWORK_NAME=avalanche FORK_BLOCK_NUMBER=13936470 hardhat --max-memory 4096 test test/test-opty/3_vault_strategies.spec.ts --deploy-fixture", "test-fast:integration:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/integration.spec.ts", "test-fast:strategy-manager:mainnet": "env SKIP_LOAD=true TS_NODE_TRANSPILE_ONLY=1 FORK=mainnet NETWORK_NAME=mainnet hardhat --max-memory 4096 test test/test-opty/strategy-manager.spec.ts", @@ -149,7 +149,6 @@ "@uniswap/v2-core": "^1.0.1", "@uniswap/v2-periphery": "^1.1.0-beta.0", "@uniswap/v3-core": "^1.0.1", - "@uniswap/v3-periphery": "^1.4.2", - "hardhat-contract-sizer": "^2.6.1" + "@uniswap/v3-periphery": "^1.4.2" } } diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index 83c27517..4a3993f9 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -4,8 +4,9 @@ import { solidity } from "ethereum-waffle"; import BN from "bignumber.js"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { BigNumber, Contract } from "ethers"; -import { formatEther, formatUnits, getAddress } from "ethers/lib/utils"; -import { Signers, to_10powNumber_BN } from "../../helpers/utils"; +import { formatEther, formatUnits, getAddress, parseEther, parseUnits } from "ethers/lib/utils"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; +import { assertPostDepositState, assertPostUserDepositState, Signers, to_10powNumber_BN } from "../../helpers/utils"; import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; import { @@ -28,7 +29,7 @@ import { } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; -import { setTokenBalanceInStorage } from "./utils"; +import { getPreDepositState, getPreUserDepositState, setTokenBalanceInStorage } from "./utils"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; import { TypedTokens } from "../../helpers/data"; import { StrategyManager } from "../../helpers/strategy-manager"; @@ -41,15 +42,13 @@ const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; const IGNORE_VAULTS = process.env.IGNORE_VAULTS; -describe(`${fork}-Vault-rev4`, () => { +const strategyHashLPoutputIndex: { [key: string]: number } = { + "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": 0, + "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": 2, +}; + +describe(`${fork}-Vault-rev7`, () => { before(async function () { - ethers.provider; - await deployments.fixture(); - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const registryProxy = await deployments.get("RegistryProxy"); - const riskManagerAddress = (await deployments.get("RiskManager")).address; - // const strategyProvider = await deployments.get("StrategyProvider"); this.strategyRegistry = await ethers.getContractAt( StrategyRegistry__factory.abi, ( @@ -63,8 +62,12 @@ describe(`${fork}-Vault-rev4`, () => { await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) ); this.strategyManager = new StrategyManager(this.vaultHelper, this.compoundAdapter); - this.registry = await ethers.getContractAt(Registry__factory.abi, registryProxy.address); - this.riskManager = await ethers.getContractAt(RiskManager__factory.abi, riskManagerAddress); + this.registry = ( + await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) + ); + this.riskManager = ( + await ethers.getContractAt(RiskManager__factory.abi, (await deployments.get("RiskManager")).address) + ); this.vaults = {}; this.tokens = {}; this.signers = {} as Signers; @@ -118,21 +121,15 @@ describe(`${fork}-Vault-rev4`, () => { let _userDepositInDecimals = await this.vaults[riskProfile][token].minimumDepositValueUT(); const decimals = await this.tokens[token].decimals(); if (_userDepositInDecimals.eq(BigNumber.from("0"))) { - _userDepositInDecimals = BigNumber.from("10").pow(decimals); + _userDepositInDecimals = BigNumber.from("3").mul(parseUnits("1", decimals)); } const _userDeposit = new BN(_userDepositInDecimals.toString()).div( new BN(to_10powNumber_BN(await this.vaults[riskProfile][token].decimals()).toString()), ); - await setTokenBalanceInStorage( - this.tokens[token], - this.signers.alice.address, - _userDeposit.multipliedBy("3").toString(), - ); - await setTokenBalanceInStorage( - this.tokens[token], - this.signers.bob.address, - _userDeposit.multipliedBy("3").toString(), - ); + await setTokenBalanceInStorage(this.tokens[token], this.signers.alice.address, _userDeposit.toString()); + await setTokenBalanceInStorage(this.tokens[token], this.signers.bob.address, _userDeposit.toString()); + expect(await this.tokens[token].balanceOf(this.signers.alice.address)).to.eq(_userDepositInDecimals); + expect(await this.tokens[token].balanceOf(this.signers.bob.address)).to.eq(_userDepositInDecimals); } } if (fork == eEVMNetwork.mainnet) { @@ -149,6 +146,7 @@ describe(`${fork}-Vault-rev4`, () => { const strategyDetail = StrategiesByTokenByChain[fork][riskProfile][token][strategy]; const tokenHash = generateTokenHashV2([strategyDetail.token], NETWORKS_CHAIN_ID_HEX[fork]); const strategyHash = generateStrategyHashV2(strategyDetail.strategy, tokenHash); + console.log("strategyHash ", strategyHash); const lastPool = strategyDetail.strategy[strategyDetail.strategy.length - 1].contract; const steps = strategyDetail.strategy.map(item => ({ pool: item.contract, @@ -158,10 +156,6 @@ describe(`${fork}-Vault-rev4`, () => { describe(`${fork}-${riskProfile}-${token}-${strategy}`, () => { before(async function () { - const addStrategyTx = await this.strategyRegistry - .connect(this.signers.operator) - .addStrategy(strategyHash, steps); - await addStrategyTx.wait(1); const addStrategyPlantx = await this.strategyRegistry .connect(this.signers.operator) .addStrategyPlan(this.vaults[riskProfile][token].address, strategyHash, { @@ -170,11 +164,14 @@ describe(`${fork}-Vault-rev4`, () => { steps, this.vaults[riskProfile][token], ), - oraValueLPPlan: this.strategyManager.getOraSomeValueLPPlan( - StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, - steps, - this.vaults[riskProfile][token], - ), + oraValueLPPlan: { + ...this.strategyManager.getOraSomeValueLPPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + outputIndex: strategyHashLPoutputIndex[strategyHash], + }, lastStepBalanceLPPlan: this.strategyManager.getLastStrategyStepBalancePlan( StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, steps, @@ -205,8 +202,9 @@ describe(`${fork}-Vault-rev4`, () => { if (fork == eEVMNetwork.mainnet) { if ( - (await this.registry.getLiquidityPoolToAdapter(lastPool)) == - (await deployments.get("CompoundAdapter")).address + Object.keys(Compound.pools) + .map(x => getAddress(Compound.pools[x as string as keyof typeof Compound.pools].pool)) + .includes(getAddress(lastPool)) ) { const comptrollerInstance = await ethers.getContractAt( IComptroller__factory.abi, @@ -218,64 +216,86 @@ describe(`${fork}-Vault-rev4`, () => { this.skip(); } } - this.curveSwapPoolAdapter = ( - await ethers.getContractAt( - CurveSwapPoolAdapter__factory.abi, - await ( - await deployments.get("CurveSwapPoolAdapter") - ).address, - ) - ); - this.curveSwapEthGateway = ( - await ethers.getContractAt( - CurveSwapETHGateway__factory.abi, - await this.curveSwapPoolAdapter.curveSwapETHGatewayContract(), - ) - ); - if ( - strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV" || - strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" - ) { - DEBUG && console.log("\nLIDO strategy"); - const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); - if (!isConvertToStEth) { - DEBUG && console.log("\nStrategyOperator setting convertToStEth"); - const tx = await this.curveSwapEthGateway - .connect(this.signers.strategyOperator) - .setConvertToStEth(true); - await tx.wait(1); - } else { - DEBUG && console.log("\nStrategyOperator already set convertToStEth"); - } - } else if ( - strategy === "weth-DEPOSIT-CurveSwapPool-steCRV" || - strategy === "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" - ) { - DEBUG && console.log("\n non-LIDO strategy"); - const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); - if (isConvertToStEth) { - DEBUG && console.log("\nStrategyOperator un-setting convertToStEth"); - const tx = await this.curveSwapEthGateway - .connect(this.signers.strategyOperator) - .setConvertToStEth(false); - await tx.wait(1); - } else { - DEBUG && console.log("\nStrategyOperator already un-set convertToStEth"); - } - } + // this.curveSwapPoolAdapter = ( + // await ethers.getContractAt( + // CurveSwapPoolAdapter__factory.abi, + // await ( + // await deployments.get("CurveSwapPoolAdapter") + // ).address, + // ) + // ); + // this.curveSwapEthGateway = ( + // await ethers.getContractAt( + // CurveSwapETHGateway__factory.abi, + // await this.curveSwapPoolAdapter.curveSwapETHGatewayContract(), + // ) + // ); + // if ( + // strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV" || + // strategy === "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" + // ) { + // DEBUG && console.log("\nLIDO strategy"); + // const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); + // if (!isConvertToStEth) { + // DEBUG && console.log("\nStrategyOperator setting convertToStEth"); + // const tx = await this.curveSwapEthGateway + // .connect(this.signers.strategyOperator) + // .setConvertToStEth(true); + // await tx.wait(1); + // } else { + // DEBUG && console.log("\nStrategyOperator already set convertToStEth"); + // } + // } else if ( + // strategy === "weth-DEPOSIT-CurveSwapPool-steCRV" || + // strategy === "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV" + // ) { + // DEBUG && console.log("\n non-LIDO strategy"); + // const isConvertToStEth = await this.curveSwapEthGateway.convertToStEth(); + // if (isConvertToStEth) { + // DEBUG && console.log("\nStrategyOperator un-setting convertToStEth"); + // const tx = await this.curveSwapEthGateway + // .connect(this.signers.strategyOperator) + // .setConvertToStEth(false); + // await tx.wait(1); + // } else { + // DEBUG && console.log("\nStrategyOperator already un-set convertToStEth"); + // } + // } } }); it(`${riskProfile}-${token}-${strategy}-(first) alice and bob should deposit into Vault successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], this.tokens["WETH"]); + await deposit( + signers, + this.vaults[riskProfile][token], + this.tokens[token], + this.tokens["WETH"], + steps as StrategyStepType[], + this.strategyManager, + ); } else { - await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], undefined); + await deposit( + signers, + this.vaults[riskProfile][token], + this.tokens[token], + undefined, + steps as StrategyStepType[], + this.strategyManager, + ); } }); it(`${riskProfile}-${token}-${strategy}-should receive new strategy after rebalancing`, async function () { const underlyingTokenSymbol = await this.tokens[token].symbol(); const vaultTokenSymbol = await this.vaults[riskProfile][token].symbol(); + // ToDo + // 1. assert alice UT balance before rebalance + // 2. assert bob UT balance before rebalance + // 3. assert alice VT balance before rebalance + // 4. assert bob VT balance before rebalance + // 5. assert vault LP token balance before rebalance + // 6. assert vault PPS before rebalance + // 7. assert vault total supply before rebalance if (DEBUG == true) { console.log("\n"); console.log("Before rebalance "); @@ -324,6 +344,14 @@ describe(`${fork}-Vault-rev4`, () => { ), ); console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); + console.log( + "Total supply ", + formatUnits( + await this.vaults[riskProfile][token].totalSupply(), + await this.vaults[riskProfile][token].decimals(), + ), + ); + console.log("strategies ", await this.vaults[riskProfile][token].getStrategies()); } const vaultAddStrategyTx = await this.vaults[riskProfile][token] @@ -331,6 +359,8 @@ describe(`${fork}-Vault-rev4`, () => { .addStrategy(strategyHash); await vaultAddStrategyTx.wait(1); + expect(await this.vaults[riskProfile][token].getStrategies()).to.deep.eq([strategyHash]); + const vaultDepositTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) .vaultDepositSomeToStrategy( @@ -338,11 +368,14 @@ describe(`${fork}-Vault-rev4`, () => { await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address), ); await vaultDepositTx.wait(1); - - // expect(await this.vaults[riskProfile][token].getInvestStrategySteps()).to.deep.eq( - // steps.map(item => Object.values(item)), - // ); - // expect(await this.vaults[riskProfile][token].investStrategyHash()).to.eq(strategyHash); + // ToDo + // 1. assert alice UT balance after rebalance + // 2. assert bob UT balance after rebalance + // 3. assert alice VT balance after rebalance + // 4. assert bob VT balance after rebalance + // 5. assert vault LP token balance after rebalance + // 6. assert vault PPS after rebalance + // 7. assert vault total supply after rebalance if (DEBUG == true) { console.log("\n"); console.log("After rebalance "); @@ -391,6 +424,14 @@ describe(`${fork}-Vault-rev4`, () => { ), ); console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); + console.log( + "Total supply ", + formatUnits( + await this.vaults[riskProfile][token].totalSupply(), + await this.vaults[riskProfile][token].decimals(), + ), + ); + console.log("strategies ", await this.vaults[riskProfile][token].getStrategies()); } }); it(`${riskProfile}-${token}-${strategy}-(first) alice and bob should be able to withdraw successfully, vault should withdraw from the current strategy successfully`, async function () { @@ -420,9 +461,23 @@ describe(`${fork}-Vault-rev4`, () => { it(`${riskProfile}-${token}-${strategy}-(second) alice and bob should deposit into Vault successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], this.tokens["WETH"]); + await deposit( + signers, + this.vaults[riskProfile][token], + this.tokens[token], + this.tokens["WETH"], + steps as StrategyStepType[], + this.strategyManager, + ); } else { - await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], undefined); + await deposit( + signers, + this.vaults[riskProfile][token], + this.tokens[token], + undefined, + steps as StrategyStepType[], + this.strategyManager, + ); } }); it(`${riskProfile}-${token}-${strategy}-vault should deposit successfully to strategy after vaultDepositAllToStrategy()`, async function () { @@ -431,7 +486,7 @@ describe(`${fork}-Vault-rev4`, () => { const vaultBalanceBefore = await this.vaults[riskProfile][token].balanceUT(); const poolBalanceBefore = await this.strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool - ].getLPTokenBalance( + ].getOutputTokenBalance( this.vaults[riskProfile][token], steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, steps[steps.length - 1].pool, @@ -439,6 +494,14 @@ describe(`${fork}-Vault-rev4`, () => { steps[steps.length - 1].isSwap, ethers.provider, ); + // ToDo + // 1. assert alice UT balance before vaultDeposit + // 2. assert bob UT balance before vaultDeposit + // 3. assert alice VT balance before vaultDeposit + // 4. assert bob VT balance before vaultDeposit + // 5. assert vault LP token balance before vaultDeposit + // 6. assert vault PPS before vaultDeposit + // 7. assert vault total supply before vaultDeposit if (DEBUG == true) { console.log("\n"); console.log("Before vault deposit "); @@ -488,15 +551,26 @@ describe(`${fork}-Vault-rev4`, () => { ), ); console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); + console.log( + "Total supply ", + formatUnits( + await this.vaults[riskProfile][token].totalSupply(), + await this.vaults[riskProfile][token].decimals(), + ), + ); + console.log("strategies ", await this.vaults[riskProfile][token].getStrategies()); } - // const tx = await this.vaults[riskProfile][token] - // .connect(this.signers.financeOperator) - // .vaultDepositAllToStrategy(); - // await tx.wait(1); + const vaultDepositSomeTx = await this.vaults[riskProfile][token] + .connect(this.signers.operator) + .vaultDepositSomeToStrategy( + strategyHash, + await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address), + ); + await vaultDepositSomeTx.wait(1); const vaultBalanceAfter = await this.vaults[riskProfile][token].balanceUT(); const poolBalanceAfter = await this.strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool - ].getLPTokenBalance( + ].getOutputTokenBalance( this.vaults[riskProfile][token], steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, steps[steps.length - 1].pool, @@ -506,6 +580,14 @@ describe(`${fork}-Vault-rev4`, () => { ); expect(vaultBalanceBefore).gt(vaultBalanceAfter); expect(poolBalanceBefore).lt(poolBalanceAfter); + // ToDo + // 1. assert alice UT balance after vaultDeposit + // 2. assert bob UT balance after vaultDeposit + // 3. assert alice VT balance after vaultDeposit + // 4. assert bob VT balance after vaultDeposit + // 5. assert vault LP token balance after vaultDeposit + // 6. assert vault PPS after vaultDeposit + // 7. assert vault total supply after vaultDeposit if (DEBUG == true) { console.log("\n"); console.log("After vault deposit "); @@ -555,10 +637,19 @@ describe(`${fork}-Vault-rev4`, () => { ), ); console.log("PPS ", formatUnits(await this.vaults[riskProfile][token].getPricePerFullShare(), 18)); + console.log( + "Total supply ", + formatUnits( + await this.vaults[riskProfile][token].totalSupply(), + await this.vaults[riskProfile][token].decimals(), + ), + ); + console.log("strategies ", await this.vaults[riskProfile][token].getStrategies()); } }); it(`${riskProfile}-${token}-${strategy}-(second) alice and bob should be able to withdraw successfully, vault should withdraw from the current strategy successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; + console.log("\nStrategy ", strategy); if (fork == eEVMNetwork.mainnet) { await withdraw( signers, @@ -580,6 +671,24 @@ describe(`${fork}-Vault-rev4`, () => { this.strategyManager, ); } + const lpTokenBalance = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + this.vaults[riskProfile][token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); + const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.strategyOperator) + .vaultWithdrawSomeFromStrategy(strategyHash, lpTokenBalance); + await vaultWithdrawSomeFromStrategyTx.wait(1); + const removeStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.operator) + .removeStrategy(strategyHash); + await removeStrategyTx.wait(1); }); }); } @@ -593,6 +702,8 @@ async function deposit( vaultInstance: Vault, underlyingTokenInstance: ERC20, wethTokenInstance: ERC20 | undefined, + steps: StrategyStepType[], + strategyManager: StrategyManager, ) { let _userDepositInDecimals = await vaultInstance.minimumDepositValueUT(); const underlyingTokenSymbol = await underlyingTokenInstance.symbol(); @@ -601,6 +712,7 @@ async function deposit( _userDepositInDecimals = BigNumber.from("10").pow(decimals); } const vaultTokenSymbol = await vaultInstance.symbol(); + if (DEBUG == true) { console.log("\n"); console.log("Before user deposit "); @@ -642,19 +754,105 @@ async function deposit( formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), ); console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + console.log("Total Supply ", formatUnits(await vaultInstance.totalSupply(), await vaultInstance.decimals())); + console.log("strategies ", await vaultInstance.getStrategies()); } for (let i = 0; i < signers.length; i++) { const tx1 = await underlyingTokenInstance .connect(signers[i]) .approve(vaultInstance.address, _userDepositInDecimals); await tx1.wait(1); - const _BalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); + + // const userBalanceBeforeUT = await underlyingTokenInstance.balanceOf(signers[i].address); + // const userBalanceBeforeVT = await vaultInstance.balanceOf(signers[i].address); + // const vaultBalanceBeforeUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + // const vaultBalanceBeforeLP = await strategyManager.liquidityPoolToAdapter[ + // steps[steps.length - 1].pool + // ].getOutputTokenBalance( + // vaultInstance, + // steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + // steps[steps.length - 1].pool, + // steps[steps.length - 1].outputToken, + // steps[steps.length - 1].isSwap, + // ethers.provider, + // ); + // const vaultTotalSupplyBeforeVT = await vaultInstance.totalSupply(); + // const vaultValueBeforeUT = ( + // await strategyManager.getValueInInputToken( + // underlyingTokenInstance.address, + // steps, + // vaultInstance, + // vaultBalanceBeforeLP, + // ethers.provider, + // ) + // ).add(vaultBalanceBeforeUT); + + const { userBalanceBeforeUT, userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT } = + await getPreUserDepositState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); + const tx2 = await vaultInstance .connect(signers[i]) .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", []); await tx2.wait(1); - const _BalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); - expect(_BalanceBefore).gt(_BalanceAfter); + + await assertPostUserDepositState( + signers[i], + underlyingTokenInstance, + vaultInstance, + strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + _userDepositInDecimals, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + userBalanceBeforeVT, + ); + + // const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signers[i].address); + // const userBalanceAfterVT = await vaultInstance.balanceOf(signers[i].address); + // const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + // const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ + // steps[steps.length - 1].pool + // ].getOutputTokenBalance( + // vaultInstance, + // steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + // steps[steps.length - 1].pool, + // steps[steps.length - 1].outputToken, + // steps[steps.length - 1].isSwap, + // ethers.provider, + // ); + // const ppsAfter = await vaultInstance.getPricePerFullShare(); + // const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); + // const expectedUserBalanceUT = userBalanceBeforeUT.sub(_userDepositInDecimals); + // let expectedUserVT: BigNumber = BigNumber.from(0); + // if (vaultTotalSupplyBeforeVT.eq(0) || vaultValueBeforeUT.eq(0)) { + // expectedUserVT = _userDepositInDecimals; + // } else { + // expectedUserVT = _userDepositInDecimals.mul(vaultTotalSupplyBeforeVT).div(vaultValueBeforeUT); + // } + // const actualUserMintedVT = userBalanceAfterVT.sub(userBalanceBeforeVT); + // const expectedTotalSupplyVT = vaultTotalSupplyBeforeVT.add(expectedUserVT); + // const expectedPPS = expectedTotalSupplyVT.eq("0") + // ? BigNumber.from("0") + // : ( + // await strategyManager.getValueInInputToken( + // underlyingTokenInstance.address, + // steps, + // vaultInstance, + // vaultBalanceAfterLP, + // ethers.provider, + // ) + // ) + // .add(vaultBalanceAfterUT) + // .mul(parseEther("1")) + // .div(expectedTotalSupplyVT); + + // expect(userBalanceBeforeUT).gt(userBalanceAfterUT); + // expect(userBalanceAfterUT).to.eq(expectedUserBalanceUT); + // expect(actualUserMintedVT).to.gte(expectedUserVT); + // expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); + // expect(ppsAfter).to.eq(expectedPPS); if (DEBUG == true) { console.log("\n"); console.log("After user deposit "); @@ -696,6 +894,8 @@ async function deposit( formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), ); console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + console.log("Total Supply ", formatUnits(await vaultInstance.totalSupply(), await vaultInstance.decimals())); + console.log("strategies ", await vaultInstance.getStrategies()); } } } @@ -716,7 +916,7 @@ async function withdraw( const userBalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); const poolBalanceBefore = await strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool - ].getLPTokenBalance( + ].getOutputTokenBalance( vaultInstance, steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, steps[steps.length - 1].pool, @@ -724,6 +924,14 @@ async function withdraw( steps[steps.length - 1].isSwap, ethers.provider, ); + // ToDo + // 1. assert alice UT balance before withdraw + // 2. assert bob UT balance before withdraw + // 3. assert alice VT balance before withdraw + // 4. assert bob VT balance before withdraw + // 5. assert vault LP token balance before withdraw + // 6. assert vault PPS before withdraw + // 7. assert vault total supply before withdraw if (DEBUG == true) { console.log("\n"); console.log("Before user withdraw "); @@ -766,6 +974,8 @@ async function withdraw( formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), ); console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + console.log("Total Supply ", formatUnits(await vaultInstance.totalSupply(), await vaultInstance.decimals())); + console.log("strategies ", await vaultInstance.getStrategies()); } const tx = await vaultInstance .connect(signers[i]) @@ -774,7 +984,7 @@ async function withdraw( const userBalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); const poolBalanceAfter = await strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool - ].getLPTokenBalance( + ].getOutputTokenBalance( vaultInstance, steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, steps[steps.length - 1].pool, @@ -790,8 +1000,16 @@ async function withdraw( // ] //wbtc-DEPOSIT-AaveV2-aWBTC-DEPOSIT-dAMM-dAWBTC has 0% supply APY at fork block // .includes(await vaultInstance.investStrategyHash()) // ) { - // expect(poolBalanceBefore).gt(poolBalanceAfter); + expect(poolBalanceBefore).gt(poolBalanceAfter); // } + // ToDo + // 1. assert alice UT balance after withdraw + // 2. assert bob UT balance after withdraw + // 3. assert alice VT balance after withdraw + // 4. assert bob VT balance after withdraw + // 5. assert vault LP token balance after withdraw + // 6. assert vault PPS after withdraw + // 7. assert vault total supply after withdraw if (DEBUG == true) { console.log("\n"); console.log("After user withdraw "); @@ -834,6 +1052,8 @@ async function withdraw( formatUnits(await vaultInstance.balanceOf(signers[1].address), await vaultInstance.decimals()), ); console.log("PPS ", formatUnits(await vaultInstance.getPricePerFullShare(), 18)); + console.log("Total Supply ", formatUnits(await vaultInstance.totalSupply(), await vaultInstance.decimals())); + console.log("strategies ", await vaultInstance.getStrategies()); } } } diff --git a/test/test-opty/utils.ts b/test/test-opty/utils.ts index 1e5bd8aa..d0ecf7b0 100644 --- a/test/test-opty/utils.ts +++ b/test/test-opty/utils.ts @@ -8,9 +8,10 @@ import polygonTokens from "@optyfi/defi-legos/polygon/tokens"; import avaxTokens from "@optyfi/defi-legos/avalanche/tokens"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { StrategyStepType } from "../../helpers/type"; -import { ERC20, IAdapterFull, IWETH, Registry, ERC20Permit } from "../../typechain"; +import { ERC20, IAdapterFull, IWETH, Registry, ERC20Permit, Vault } from "../../typechain"; import { fundWalletToken, getBlockTimestamp } from "../../helpers/contracts-actions"; import { RegistryV1 } from "../../helpers/types/registryV1"; +import { StrategyManager } from "../../helpers/strategy-manager"; const setStorageAt = (address: string, slot: string, val: string): Promise => hre.network.provider.send("hardhat_setStorageAt", [address, slot, val]); @@ -510,3 +511,47 @@ export async function getPermitLegacySignature( ), ); } + +export async function getPreUserDepositState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], +): Promise<{ + userBalanceBeforeUT: BigNumber; + userBalanceBeforeVT: BigNumber; + vaultTotalSupplyBeforeVT: BigNumber; + vaultValueBeforeUT: BigNumber; +}> { + const userBalanceBeforeUT = await underlyingTokenInstance.balanceOf(signer.address); + const userBalanceBeforeVT = await vaultInstance.balanceOf(signer.address); + const vaultBalanceBeforeUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + const vaultBalanceBeforeLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); + const vaultTotalSupplyBeforeVT = await vaultInstance.totalSupply(); + const vaultValueBeforeUT = ( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + vaultBalanceBeforeLP, + ethers.provider, + ) + ).add(vaultBalanceBeforeUT); + + return { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + }; +} diff --git a/yarn.lock b/yarn.lock index edc23a15..4ad89118 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3407,12 +3407,12 @@ __metadata: languageName: node linkType: hard -"@weiroll/weiroll.js@npm:^0.3.0": +"@weiroll/weiroll.js@github:weiroll/weiroll.js#main": version: 0.3.0 - resolution: "@weiroll/weiroll.js@npm:0.3.0" + resolution: "@weiroll/weiroll.js@https://github.com/weiroll/weiroll.js.git#commit=d1bdb851c42183e27b341e39e58eb3956d3237fb" dependencies: ethers: ^5.3.1 - checksum: 571d94ed27edb3433d4b4dcae20ee0489130adaf523f74aa54b2681bc601f6557bb5e9d15db626fef8524b43eee5119f46cac6118290fa936c63237d9bbdace5 + checksum: fd828fb42769a5da0243caa1709e072fde45d82446d84084bf9291187ad280aeda7f08ed62cdb77c15acf949248c7a5b0c0564ea7eda00bfa779c2a8523444a3 languageName: node linkType: hard @@ -7052,7 +7052,7 @@ __metadata: "@uniswap/v2-periphery": ^1.1.0-beta.0 "@uniswap/v3-core": ^1.0.1 "@uniswap/v3-periphery": ^1.4.2 - "@weiroll/weiroll.js": ^0.3.0 + "@weiroll/weiroll.js": "github:weiroll/weiroll.js#main" bignumber.js: ^9.0.2 chai: ^4.3.4 commitizen: ^4.2.4 From 4ddff06d7b84d76b801145a3467a987b7fbe8bfd Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Fri, 16 Dec 2022 23:04:12 -0500 Subject: [PATCH 31/44] refactor(opusdc-save): add approvals --- deploy_mainnet/036_deploy_opUSDC-Save.ts | 52 +++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/deploy_mainnet/036_deploy_opUSDC-Save.ts b/deploy_mainnet/036_deploy_opUSDC-Save.ts index e0e3783a..49567a27 100644 --- a/deploy_mainnet/036_deploy_opUSDC-Save.ts +++ b/deploy_mainnet/036_deploy_opUSDC-Save.ts @@ -1,11 +1,15 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { getAddress } from "ethers/lib/utils"; +import { getAddress, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; +import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -150,7 +154,51 @@ const func: DeployFunction = async ({ } } } + + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Save_Proxy")).address) + ); + const approvalTokens = []; + const approvalSpender = []; + + const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const ausdcInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.usdc.lpToken); + const cusdcInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.usdc.pool); + + const usdcAaveV2Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const ausdcAaveV2Allowance = await ausdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + const usdcCompoundAllowance = await usdcInstance.allowance(vaultInstance.address, cusdcInstance.address); + + if (!usdcAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!ausdcAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(ausdcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!usdcCompoundAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(Compound.pools.usdc.pool); + } + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const governanceSigner = await hre.ethers.getSigner(await registryInstance.getGovernance()); + if (getAddress(governanceSigner.address) === getAddress(deployer)) { + const tx = await vaultInstance.connect(governanceSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["opUSDC-Save"]; -func.dependencies = ["Registry"]; +func.dependencies = ["Registry", "CommandBuilder"]; From d0fba4b34ea68b43e2cabd8b72c32a307c15735f Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 19 Dec 2022 17:13:40 -0500 Subject: [PATCH 32/44] feat(test): add vault withdraw and deposit assertions --- contracts/protocol/tokenization/Vault.sol | 15 +- helpers/utils.ts | 229 ++++++++++++++ test/test-opty/3_vault_strategies.spec.ts | 356 +++++++++++----------- test/test-opty/utils.ts | 6 +- 4 files changed, 409 insertions(+), 197 deletions(-) diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 5e376103..1313732f 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -237,7 +237,7 @@ contract Vault is _onlyRiskOperator(); uint256 _tokensLen = _tokens.length; require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); - for (uint256 _i; _i < _tokens.length; _i++) { + for (uint256 _i; _i < _tokensLen; _i++) { _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); } } @@ -249,7 +249,7 @@ contract Vault is _onlyRiskOperator(); uint256 _tokensLen = _tokens.length; require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); - for (uint256 _i; _i < _tokens.length; _i++) { + for (uint256 _i; _i < _tokensLen; _i++) { _tokens[_i].safeApprove(_spenders[_i], 0); } } @@ -666,18 +666,12 @@ contract Vault is _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } - // Identify Slippage - // UT requested from strategy withdraw = _expectedStratWithdrawUT // UT actually received from strategy withdraw - // = _receivedStratWithdrawUT - // = _vaultValuePostStratWithdrawUT.sub(_vaultValuePreStratWithdrawUT) - // slippage = _expectedStratWithdrawUT - _receivedStratWithdrawUT - uint256 _vaultValuePostStratWithdrawUT = balanceUT(); - uint256 _receivedStratWithdrawUT = _vaultValuePostStratWithdrawUT.sub(_vaultValuePreStratWithdrawUT); + uint256 _receivedStratWithdrawUT = balanceUT().sub(_vaultValuePreStratWithdrawUT); // If slippage occurs, reduce _oraUserWithdrawUT by slippage amount if (_receivedStratWithdrawUT < _expectedStratWithdrawUT) { - _oraUserWithdrawUT = _oraUserWithdrawUT.sub(_expectedStratWithdrawUT.sub(_receivedStratWithdrawUT)); + _oraUserWithdrawUT = _oraUserWithdrawUT.add(_receivedStratWithdrawUT); } } uint256 _withdrawFeeUT = calcWithdrawalFeeUT(_oraUserWithdrawUT); @@ -722,7 +716,6 @@ contract Vault is _onlyStrategyOperator(); _checkVaultDeposit(); require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_SET); - // TODO : isValidStrategy should be read only function IRiskManager(registryContract.getRiskManager()).isValidStrategy( _strategyHash, (vaultConfiguration >> 240) & 0xFF diff --git a/helpers/utils.ts b/helpers/utils.ts index a225862f..35185e0d 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -31,6 +31,7 @@ import { UniswapV2ExchangeAdapter, CurveExchangeAdapter, StrategyRegistry, + ERC20__factory, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -412,6 +413,234 @@ export async function assertPostUserDepositState( expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); } +export async function assertPostVaultDepositState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], + provider: JsonRpcProvider, + userBalanceBeforeUT: BigNumber, + vaultDepositUT: BigNumber, + vaultTotalSupplyBeforeVT: BigNumber, + userBalanceBeforeVT: BigNumber, + vaultBalanceBeforeLP: BigNumber, + vaultBalanceBeforeUT: BigNumber, +): Promise { + const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signer.address); + const userBalanceAfterVT = await vaultInstance.balanceOf(signer.address); + const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + provider, + ); + const ppsAfter = await vaultInstance.getPricePerFullShare(); + const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); + + const expectedVaultBalanceAfterLP = ( + await strategyManager.liquidityPoolToAdapter[steps[steps.length - 1].pool].getValueInOutputToken( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + vaultDepositUT, + steps[steps.length - 1].isSwap, + provider, + ) + ).add(vaultBalanceBeforeLP); + + const expectedVaultBalanceAfterUT = vaultBalanceBeforeUT.sub(vaultDepositUT); + + const expectedPPS = vaultTotalSupplyBeforeVT.eq("0") + ? BigNumber.from("0") + : ( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + expectedVaultBalanceAfterLP, + provider, + ) + ) + .add(expectedVaultBalanceAfterUT) + .mul(parseEther("1")) + .div(vaultTotalSupplyBeforeVT); + + expect(vaultBalanceAfterUT).to.eq(expectedVaultBalanceAfterUT); + const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + expect(vaultBalanceAfterLP).to.closeTo( + expectedVaultBalanceAfterLP, + parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + ); + expect(userBalanceBeforeVT).to.eq(userBalanceAfterVT); + expect(userBalanceAfterUT).to.eq(userBalanceBeforeUT); + expect(vaultTotalSupplyAfterVT).to.eq(vaultTotalSupplyBeforeVT); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); +} + +export async function assertPostUserWithdrawState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], + provider: JsonRpcProvider, + userBalanceBeforeUT: BigNumber, + userWithdrawAmountVT: BigNumber, + vaultTotalSupplyBeforeVT: BigNumber, + vaultValueBeforeUT: BigNumber, + userBalanceBeforeVT: BigNumber, + vaultBalanceBeforeLP: BigNumber, + vaultBalanceBeforeUT: BigNumber, +): Promise { + const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signer.address); + const userBalanceAfterVT = await vaultInstance.balanceOf(signer.address); + const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + provider, + ); + const ppsAfter = await vaultInstance.getPricePerFullShare(); + const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); + const expectedWithdrawAmountUT = userWithdrawAmountVT.mul(vaultValueBeforeUT).div(vaultTotalSupplyBeforeVT); + const expectedUserBalanceUT = userBalanceBeforeUT.add(expectedWithdrawAmountUT); + const expectedUserBurntVT = userWithdrawAmountVT; + const actualUserBurntVT = userBalanceBeforeVT.sub(userBalanceAfterVT); + const expectedUserBalanceVT = userBalanceBeforeVT.sub(userWithdrawAmountVT); + const expectedTotalSupplyVT = vaultTotalSupplyBeforeVT.sub(expectedUserBurntVT); + const expectedPPS = expectedTotalSupplyVT.eq("0") + ? BigNumber.from("0") + : ( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + vaultBalanceAfterLP, + provider, + ) + ) + .add(vaultBalanceAfterUT) + .mul(parseEther("1")) + .div(expectedTotalSupplyVT); + if (!vaultBalanceBeforeUT.gte(expectedUserBalanceUT)) { + const expectedStratWithdrawUT = expectedWithdrawAmountUT.sub(vaultBalanceBeforeUT); + const expectedAmountWithdrawLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getValueInOutputToken( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + expectedStratWithdrawUT, + steps[steps.length - 1].isSwap, + provider, + ); + const expectedVaultBalanceLP = vaultBalanceBeforeLP.sub(expectedAmountWithdrawLP); + const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + expect(vaultBalanceAfterLP).to.closeTo( + expectedVaultBalanceLP, + parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + ); + expect(vaultBalanceBeforeLP).gt(vaultBalanceAfterLP); + } + expect(userBalanceAfterUT).gt(userBalanceBeforeUT); + expect(actualUserBurntVT).to.eq(expectedUserBurntVT); + expect(userBalanceAfterVT).to.eq(expectedUserBalanceVT); + expect(userBalanceAfterUT).to.closeTo( + expectedUserBalanceUT, + parseUnits("9", (await underlyingTokenInstance.decimals()) / 2).toNumber(), + ); + expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); +} + +export async function assertPostVaultWithdrawState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], + provider: JsonRpcProvider, + userBalanceBeforeUT: BigNumber, + vaultWithdrawLP: BigNumber, + vaultTotalSupplyBeforeVT: BigNumber, + vaultValueBeforeUT: BigNumber, + userBalanceBeforeVT: BigNumber, + vaultBalanceBeforeLP: BigNumber, + vaultBalanceBeforeUT: BigNumber, +): Promise { + const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signer.address); + const userBalanceAfterVT = await vaultInstance.balanceOf(signer.address); + const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); + const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + provider, + ); + const ppsAfter = await vaultInstance.getPricePerFullShare(); + const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); + const expectedVaultBalanceAfterUT = ( + await strategyManager.liquidityPoolToAdapter[steps[steps.length - 1].pool].getValueInInputToken( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + vaultWithdrawLP, + steps[steps.length - 1].isSwap, + provider, + ) + ).add(vaultBalanceBeforeUT); + const expectedUserBalanceUT = userBalanceBeforeUT; + const expectedUserBalanceVT = userBalanceBeforeVT; + const expectedTotalSupplyVT = vaultTotalSupplyBeforeVT; + const expectedVaultBalanceAfterLP = vaultBalanceBeforeLP.sub(vaultWithdrawLP); + const expectedPPS = expectedTotalSupplyVT.eq("0") + ? BigNumber.from("0") + : ( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + expectedVaultBalanceAfterLP, + provider, + ) + ) + .add(expectedVaultBalanceAfterUT) + .mul(parseEther("1")) + .div(expectedTotalSupplyVT); + expect(userBalanceAfterUT).eq(userBalanceBeforeUT); + expect(userBalanceAfterVT).to.eq(expectedUserBalanceVT); + expect(userBalanceAfterUT).to.eq(expectedUserBalanceUT); + expect(vaultBalanceAfterUT).to.closeTo( + expectedVaultBalanceAfterUT, + parseUnits("9", (await underlyingTokenInstance.decimals()) / 2).toNumber(), + ); + const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + expect(vaultBalanceAfterLP).to.closeTo( + expectedVaultBalanceAfterLP, + parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + ); + expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); +} + export function getDepositFeeUT(vaultConfigurationV2: BigNumber | string): BigNumber { return BigNumber.from(BigInt(vaultConfigurationV2.toString()) & BigInt(65535)); } diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index 4a3993f9..841b987e 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -4,9 +4,16 @@ import { solidity } from "ethereum-waffle"; import BN from "bignumber.js"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { BigNumber, Contract } from "ethers"; -import { formatEther, formatUnits, getAddress, parseEther, parseUnits } from "ethers/lib/utils"; +import { formatEther, formatUnits, getAddress, parseUnits } from "ethers/lib/utils"; import Compound from "@optyfi/defi-legos/ethereum/compound/index"; -import { assertPostDepositState, assertPostUserDepositState, Signers, to_10powNumber_BN } from "../../helpers/utils"; +import { + assertPostUserDepositState, + assertPostUserWithdrawState, + assertPostVaultDepositState, + assertPostVaultWithdrawState, + Signers, + to_10powNumber_BN, +} from "../../helpers/utils"; import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; import { @@ -29,7 +36,7 @@ import { } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; -import { getPreDepositState, getPreUserDepositState, setTokenBalanceInStorage } from "./utils"; +import { getPreActionState, setTokenBalanceInStorage } from "./utils"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; import { TypedTokens } from "../../helpers/data"; import { StrategyManager } from "../../helpers/strategy-manager"; @@ -42,9 +49,11 @@ const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; const IGNORE_VAULTS = process.env.IGNORE_VAULTS; -const strategyHashLPoutputIndex: { [key: string]: number } = { - "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": 0, - "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": 2, +const strategyHashReadIndexes: { [key: string]: { valueLPIndex: number } } = { + "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { + valueLPIndex: 0, + }, + "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { valueLPIndex: 2 }, }; describe(`${fork}-Vault-rev7`, () => { @@ -170,7 +179,7 @@ describe(`${fork}-Vault-rev7`, () => { steps, this.vaults[riskProfile][token], ), - outputIndex: strategyHashLPoutputIndex[strategyHash], + outputIndex: strategyHashReadIndexes[strategyHash].valueLPIndex, }, lastStepBalanceLPPlan: this.strategyManager.getLastStrategyStepBalancePlan( StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, @@ -266,7 +275,7 @@ describe(`${fork}-Vault-rev7`, () => { it(`${riskProfile}-${token}-${strategy}-(first) alice and bob should deposit into Vault successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await deposit( + await userDeposit( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -275,7 +284,7 @@ describe(`${fork}-Vault-rev7`, () => { this.strategyManager, ); } else { - await deposit( + await userDeposit( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -288,14 +297,6 @@ describe(`${fork}-Vault-rev7`, () => { it(`${riskProfile}-${token}-${strategy}-should receive new strategy after rebalancing`, async function () { const underlyingTokenSymbol = await this.tokens[token].symbol(); const vaultTokenSymbol = await this.vaults[riskProfile][token].symbol(); - // ToDo - // 1. assert alice UT balance before rebalance - // 2. assert bob UT balance before rebalance - // 3. assert alice VT balance before rebalance - // 4. assert bob VT balance before rebalance - // 5. assert vault LP token balance before rebalance - // 6. assert vault PPS before rebalance - // 7. assert vault total supply before rebalance if (DEBUG == true) { console.log("\n"); console.log("Before rebalance "); @@ -361,21 +362,39 @@ describe(`${fork}-Vault-rev7`, () => { expect(await this.vaults[riskProfile][token].getStrategies()).to.deep.eq([strategyHash]); + const { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + } = await getPreActionState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ); + const vaultDepositUT = await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address); const vaultDepositTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) - .vaultDepositSomeToStrategy( - strategyHash, - await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address), - ); + .vaultDepositSomeToStrategy(strategyHash, vaultDepositUT); await vaultDepositTx.wait(1); - // ToDo - // 1. assert alice UT balance after rebalance - // 2. assert bob UT balance after rebalance - // 3. assert alice VT balance after rebalance - // 4. assert bob VT balance after rebalance - // 5. assert vault LP token balance after rebalance - // 6. assert vault PPS after rebalance - // 7. assert vault total supply after rebalance + + await assertPostVaultDepositState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + vaultDepositUT, + vaultTotalSupplyBeforeVT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + ); if (DEBUG == true) { console.log("\n"); console.log("After rebalance "); @@ -437,7 +456,7 @@ describe(`${fork}-Vault-rev7`, () => { it(`${riskProfile}-${token}-${strategy}-(first) alice and bob should be able to withdraw successfully, vault should withdraw from the current strategy successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -447,7 +466,7 @@ describe(`${fork}-Vault-rev7`, () => { this.strategyManager, ); } else { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -461,7 +480,7 @@ describe(`${fork}-Vault-rev7`, () => { it(`${riskProfile}-${token}-${strategy}-(second) alice and bob should deposit into Vault successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await deposit( + await userDeposit( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -470,7 +489,7 @@ describe(`${fork}-Vault-rev7`, () => { this.strategyManager, ); } else { - await deposit( + await userDeposit( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -483,26 +502,17 @@ describe(`${fork}-Vault-rev7`, () => { it(`${riskProfile}-${token}-${strategy}-vault should deposit successfully to strategy after vaultDepositAllToStrategy()`, async function () { const underlyingTokenSymbol = await this.tokens[token].symbol(); const vaultTokenSymbol = await this.vaults[riskProfile][token].symbol(); - const vaultBalanceBefore = await this.vaults[riskProfile][token].balanceUT(); - const poolBalanceBefore = await this.strategyManager.liquidityPoolToAdapter[ - steps[steps.length - 1].pool - ].getOutputTokenBalance( - this.vaults[riskProfile][token], - steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, - steps[steps.length - 1].isSwap, - ethers.provider, - ); - // ToDo - // 1. assert alice UT balance before vaultDeposit - // 2. assert bob UT balance before vaultDeposit - // 3. assert alice VT balance before vaultDeposit - // 4. assert bob VT balance before vaultDeposit - // 5. assert vault LP token balance before vaultDeposit - // 6. assert vault PPS before vaultDeposit - // 7. assert vault total supply before vaultDeposit if (DEBUG == true) { + const poolBalanceBefore = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + this.vaults[riskProfile][token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); console.log("\n"); console.log("Before vault deposit "); console.log("poolBalanceBefore ", formatEther(poolBalanceBefore)); @@ -560,37 +570,51 @@ describe(`${fork}-Vault-rev7`, () => { ); console.log("strategies ", await this.vaults[riskProfile][token].getStrategies()); } + const { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + } = await getPreActionState( + this.signers.bob, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ); + const vaultDepositUT = await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address); const vaultDepositSomeTx = await this.vaults[riskProfile][token] .connect(this.signers.operator) - .vaultDepositSomeToStrategy( - strategyHash, - await this.tokens[token].balanceOf(this.vaults[riskProfile][token].address), - ); + .vaultDepositSomeToStrategy(strategyHash, vaultDepositUT); await vaultDepositSomeTx.wait(1); - const vaultBalanceAfter = await this.vaults[riskProfile][token].balanceUT(); - const poolBalanceAfter = await this.strategyManager.liquidityPoolToAdapter[ - steps[steps.length - 1].pool - ].getOutputTokenBalance( + await assertPostVaultDepositState( + this.signers.bob, + this.tokens[token], this.vaults[riskProfile][token], - steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, - steps[steps.length - 1].isSwap, + this.strategyManager, + steps, ethers.provider, + userBalanceBeforeUT, + vaultDepositUT, + vaultTotalSupplyBeforeVT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, ); - expect(vaultBalanceBefore).gt(vaultBalanceAfter); - expect(poolBalanceBefore).lt(poolBalanceAfter); - // ToDo - // 1. assert alice UT balance after vaultDeposit - // 2. assert bob UT balance after vaultDeposit - // 3. assert alice VT balance after vaultDeposit - // 4. assert bob VT balance after vaultDeposit - // 5. assert vault LP token balance after vaultDeposit - // 6. assert vault PPS after vaultDeposit - // 7. assert vault total supply after vaultDeposit if (DEBUG == true) { console.log("\n"); console.log("After vault deposit "); + const poolBalanceAfter = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + this.vaults[riskProfile][token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); console.log("poolBalanceAfter ", formatEther(poolBalanceAfter)); console.log( `${underlyingTokenSymbol} balance `, @@ -651,7 +675,7 @@ describe(`${fork}-Vault-rev7`, () => { const signers = [this.signers.alice, this.signers.bob]; console.log("\nStrategy ", strategy); if (fork == eEVMNetwork.mainnet) { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -661,7 +685,7 @@ describe(`${fork}-Vault-rev7`, () => { this.strategyManager, ); } else { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], @@ -671,7 +695,9 @@ describe(`${fork}-Vault-rev7`, () => { this.strategyManager, ); } - const lpTokenBalance = await this.strategyManager.liquidityPoolToAdapter[ + }); + after(async function () { + const vaultWithdrawLP = await this.strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool ].getOutputTokenBalance( this.vaults[riskProfile][token], @@ -681,10 +707,43 @@ describe(`${fork}-Vault-rev7`, () => { steps[steps.length - 1].isSwap, ethers.provider, ); + + const { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + } = await getPreActionState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ); + const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) - .vaultWithdrawSomeFromStrategy(strategyHash, lpTokenBalance); + .vaultWithdrawSomeFromStrategy(strategyHash, vaultWithdrawLP); await vaultWithdrawSomeFromStrategyTx.wait(1); + + await assertPostVaultWithdrawState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + vaultWithdrawLP, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + ); + const removeStrategyTx = await this.vaults[riskProfile][token] .connect(this.signers.operator) .removeStrategy(strategyHash); @@ -697,7 +756,7 @@ describe(`${fork}-Vault-rev7`, () => { }); }); -async function deposit( +async function userDeposit( signers: SignerWithAddress[], vaultInstance: Vault, underlyingTokenInstance: ERC20, @@ -763,32 +822,8 @@ async function deposit( .approve(vaultInstance.address, _userDepositInDecimals); await tx1.wait(1); - // const userBalanceBeforeUT = await underlyingTokenInstance.balanceOf(signers[i].address); - // const userBalanceBeforeVT = await vaultInstance.balanceOf(signers[i].address); - // const vaultBalanceBeforeUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); - // const vaultBalanceBeforeLP = await strategyManager.liquidityPoolToAdapter[ - // steps[steps.length - 1].pool - // ].getOutputTokenBalance( - // vaultInstance, - // steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - // steps[steps.length - 1].pool, - // steps[steps.length - 1].outputToken, - // steps[steps.length - 1].isSwap, - // ethers.provider, - // ); - // const vaultTotalSupplyBeforeVT = await vaultInstance.totalSupply(); - // const vaultValueBeforeUT = ( - // await strategyManager.getValueInInputToken( - // underlyingTokenInstance.address, - // steps, - // vaultInstance, - // vaultBalanceBeforeLP, - // ethers.provider, - // ) - // ).add(vaultBalanceBeforeUT); - const { userBalanceBeforeUT, userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT } = - await getPreUserDepositState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); + await getPreActionState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); const tx2 = await vaultInstance .connect(signers[i]) @@ -808,51 +843,6 @@ async function deposit( vaultValueBeforeUT, userBalanceBeforeVT, ); - - // const userBalanceAfterUT = await underlyingTokenInstance.balanceOf(signers[i].address); - // const userBalanceAfterVT = await vaultInstance.balanceOf(signers[i].address); - // const vaultBalanceAfterUT = await underlyingTokenInstance.balanceOf(vaultInstance.address); - // const vaultBalanceAfterLP = await strategyManager.liquidityPoolToAdapter[ - // steps[steps.length - 1].pool - // ].getOutputTokenBalance( - // vaultInstance, - // steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - // steps[steps.length - 1].pool, - // steps[steps.length - 1].outputToken, - // steps[steps.length - 1].isSwap, - // ethers.provider, - // ); - // const ppsAfter = await vaultInstance.getPricePerFullShare(); - // const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); - // const expectedUserBalanceUT = userBalanceBeforeUT.sub(_userDepositInDecimals); - // let expectedUserVT: BigNumber = BigNumber.from(0); - // if (vaultTotalSupplyBeforeVT.eq(0) || vaultValueBeforeUT.eq(0)) { - // expectedUserVT = _userDepositInDecimals; - // } else { - // expectedUserVT = _userDepositInDecimals.mul(vaultTotalSupplyBeforeVT).div(vaultValueBeforeUT); - // } - // const actualUserMintedVT = userBalanceAfterVT.sub(userBalanceBeforeVT); - // const expectedTotalSupplyVT = vaultTotalSupplyBeforeVT.add(expectedUserVT); - // const expectedPPS = expectedTotalSupplyVT.eq("0") - // ? BigNumber.from("0") - // : ( - // await strategyManager.getValueInInputToken( - // underlyingTokenInstance.address, - // steps, - // vaultInstance, - // vaultBalanceAfterLP, - // ethers.provider, - // ) - // ) - // .add(vaultBalanceAfterUT) - // .mul(parseEther("1")) - // .div(expectedTotalSupplyVT); - - // expect(userBalanceBeforeUT).gt(userBalanceAfterUT); - // expect(userBalanceAfterUT).to.eq(expectedUserBalanceUT); - // expect(actualUserMintedVT).to.gte(expectedUserVT); - // expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); - // expect(ppsAfter).to.eq(expectedPPS); if (DEBUG == true) { console.log("\n"); console.log("After user deposit "); @@ -900,7 +890,7 @@ async function deposit( } } -async function withdraw( +async function userWithdraw( signers: SignerWithAddress[], vaultInstance: Vault, underlyingTokenInstance: ERC20, @@ -913,7 +903,6 @@ async function withdraw( const vaultTokenSymbol = await vaultInstance.symbol(); for (let i = 0; i < signers.length; i++) { const userWithdrawBalance = await vaultInstance.balanceOf(signers[i].address); - const userBalanceBefore = await underlyingTokenInstance.balanceOf(signers[i].address); const poolBalanceBefore = await strategyManager.liquidityPoolToAdapter[ steps[steps.length - 1].pool ].getOutputTokenBalance( @@ -924,14 +913,14 @@ async function withdraw( steps[steps.length - 1].isSwap, ethers.provider, ); - // ToDo - // 1. assert alice UT balance before withdraw - // 2. assert bob UT balance before withdraw - // 3. assert alice VT balance before withdraw - // 4. assert bob VT balance before withdraw - // 5. assert vault LP token balance before withdraw - // 6. assert vault PPS before withdraw - // 7. assert vault total supply before withdraw + const { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + } = await getPreActionState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); if (DEBUG == true) { console.log("\n"); console.log("Before user withdraw "); @@ -981,38 +970,35 @@ async function withdraw( .connect(signers[i]) .userWithdrawVault(signers[i].address, userWithdrawBalance.mul(3).div(4), []); await tx.wait(); - const userBalanceAfter = await underlyingTokenInstance.balanceOf(signers[i].address); - const poolBalanceAfter = await strategyManager.liquidityPoolToAdapter[ - steps[steps.length - 1].pool - ].getOutputTokenBalance( + + await assertPostUserWithdrawState( + signers[i], + underlyingTokenInstance, vaultInstance, - steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, - steps[steps.length - 1].isSwap, + strategyManager, + steps, ethers.provider, + userBalanceBeforeUT, + userWithdrawBalance.mul(3).div(4), + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, ); - expect(userBalanceBefore).lt(userBalanceAfter); - // if ( - // ![ - // "0xdf3f8ef63f05db6e4b04c3b5a8198d128b61faee8075aed893d76832a0deed6f", //wbtc-DEPOSIT-dAMM-cWBTC has 0% supply APY at fork block - // "0x44216c2a6ff5f35d0b24f54cfddb2f39f8ab9a7a998bfa4683aa6083dceb9a9a", - // ] //wbtc-DEPOSIT-AaveV2-aWBTC-DEPOSIT-dAMM-dAWBTC has 0% supply APY at fork block - // .includes(await vaultInstance.investStrategyHash()) - // ) { - expect(poolBalanceBefore).gt(poolBalanceAfter); - // } - // ToDo - // 1. assert alice UT balance after withdraw - // 2. assert bob UT balance after withdraw - // 3. assert alice VT balance after withdraw - // 4. assert bob VT balance after withdraw - // 5. assert vault LP token balance after withdraw - // 6. assert vault PPS after withdraw - // 7. assert vault total supply after withdraw if (DEBUG == true) { console.log("\n"); console.log("After user withdraw "); + const poolBalanceAfter = await strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + vaultInstance, + steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); console.log("poolBalanceAfter ", formatEther(poolBalanceAfter)); console.log( `${underlyingTokenSymbol} balance `, diff --git a/test/test-opty/utils.ts b/test/test-opty/utils.ts index d0ecf7b0..744c6b39 100644 --- a/test/test-opty/utils.ts +++ b/test/test-opty/utils.ts @@ -512,7 +512,7 @@ export async function getPermitLegacySignature( ); } -export async function getPreUserDepositState( +export async function getPreActionState( signer: SignerWithAddress, underlyingTokenInstance: ERC20, vaultInstance: Vault, @@ -523,6 +523,8 @@ export async function getPreUserDepositState( userBalanceBeforeVT: BigNumber; vaultTotalSupplyBeforeVT: BigNumber; vaultValueBeforeUT: BigNumber; + vaultBalanceBeforeLP: BigNumber; + vaultBalanceBeforeUT: BigNumber; }> { const userBalanceBeforeUT = await underlyingTokenInstance.balanceOf(signer.address); const userBalanceBeforeVT = await vaultInstance.balanceOf(signer.address); @@ -553,5 +555,7 @@ export async function getPreUserDepositState( userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, }; } From ff0a2f32a5376c0fd98b96a8c3c82ddc2645842a Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 20 Dec 2022 15:48:16 -0500 Subject: [PATCH 33/44] refactor(harvest): split by multi sig role --- ..._usdc-mim-sushi_weth-ldo-sushi_harvest.ts} | 197 ++-------- scripts/harvest/010_harvest_bDAMM_wbtc.ts | 194 ---------- .../harvest/010_harvest_wbtc-damm_3crv-mim.ts | 336 ++++++++++++++++++ 3 files changed, 369 insertions(+), 358 deletions(-) rename scripts/harvest/{009_mim_harvest.ts => 009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts} (58%) delete mode 100644 scripts/harvest/010_harvest_bDAMM_wbtc.ts create mode 100644 scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts diff --git a/scripts/harvest/009_mim_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts similarity index 58% rename from scripts/harvest/009_mim_harvest.ts rename to scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts index 7ffdefd0..011fd6aa 100644 --- a/scripts/harvest/009_mim_harvest.ts +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -6,16 +6,8 @@ import { deployments, ethers } from "hardhat"; import EthersAdapter from "@gnosis.pm/safe-ethers-lib"; import Safe from "@gnosis.pm/safe-core-sdk"; import { MetaTransactionData, EthAdapter } from "@gnosis.pm/safe-core-sdk-types"; -import { - ERC20, - ERC20__factory, - IConvexStake, - ICurveSwap, - ICurveSwap__factory, - IUniswapV2Router02, - IUniswapV2Router02__factory, -} from "../../typechain"; -import { formatUnits, parseEther, parseUnits } from "ethers/lib/utils"; +import { ERC20, ERC20__factory, IUniswapV2Router02, IUniswapV2Router02__factory } from "../../typechain"; +import { parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import { getCVXUnclaimed } from "./003_claim_and_swap_CRV_CVX_to_USDC"; import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; @@ -26,9 +18,6 @@ async function main() { const opUSDCEarn = ( await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); - const opUSD3Earn = ( - await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) - ); const opWETHEarn = ( await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) ); @@ -92,17 +81,12 @@ async function main() { const cvxStEthGaugeInstance = await ethers.getContractAt(cvxABI, Convex.pools.steth.stakingPool); - const threeCrvPool = ( - await ethers.getContractAt(ICurveSwap__factory.abi, "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7") - ); - const cvxInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.CVX); const crvInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.CRV); const ldoInstance = ( await ethers.getContractAt(ERC20__factory.abi, "0x5A98FcBEA516Cf06857215779Fd812CA3beF1B32") ); const sushiInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.SUSHI); - const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); const crvUnclaimedForopUSDCEarn = await cvxMIM3lp3GaugeInstance.earned(opUSDCEarn.address); const crvBalanceForopUSDCEarn = await crvInstance.balanceOf(opUSDCEarn.address); @@ -116,30 +100,17 @@ async function main() { ); const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - const ldoUnclaimedForopWETHEarn = parseEther("15.510000000000000000"); + const ldoUnclaimedForopWETHEarn = parseEther("13.310000000000000000"); const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); - const crvUnclaimedForopUSD3Earn = await cvxMIM3lp3GaugeInstance.earned(opUSD3Earn.address); - const crvBalanceForOPUSD3Earn = await crvInstance.balanceOf(opUSD3Earn.address); - const usdcBalanceForopUSD3Earn = await usdcInstance.balanceOf(opUSD3Earn.address); - - const cvxUnclaimedForopUSD3Earn = getCVXUnclaimed( - crvUnclaimedForopUSD3Earn, - await cvxInstance.totalSupply(), - BigNumber.from("100000000000000000000000"), - BigNumber.from("1000"), - BigNumber.from("100000000000000000000000000"), - ); - const cvxBalanceForopUSD3Earn = await cvxInstance.balanceOf(opUSD3Earn.address); - const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); const sushiBalanceForopWETHInvst = await sushiInstance.balanceOf(opWETHInvst.address); - const oneCRVToUSDC = parseUnits("0.59", "6"); // crv - usdc - const oneCVXToUSDC = parseUnits("3.7", "6"); - const oneLDOToWETH = parseEther("0.0008"); - const oneSUSHIToUSDC = parseUnits("1.33", "6"); - const oneSUSHIToWETH = parseEther("0.001"); // sushi-dai-weth + const oneCRVToUSDC = parseUnits("0.5", "6"); //0.53 crv - usdc + const oneCVXToUSDC = parseUnits("3.2", "6"); // 3.3 + const oneLDOToWETH = parseEther("0.0007"); //0.0008 + const oneSUSHIToUSDC = parseUnits("0.96", "6"); + const oneSUSHIToWETH = parseEther("0.0007"); // sushi-dai-weth const expectedMinUSDCFromCRVForopUSDCEarn = BigNumber.from(crvBalanceForopUSDCEarn.add(crvUnclaimedForopUSDCEarn)) .mul(oneCRVToUSDC) @@ -153,12 +124,6 @@ async function main() { .mul(oneCVXToUSDC) .div(parseEther("1")); - const expectedMinUSDCFromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) - .mul(oneCRVToUSDC) - .div(parseEther("1")); - const expectedMinUSDCFromCVXForopUSD3Earn = BigNumber.from(cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn)) - .mul(oneCVXToUSDC) - .div(parseEther("1")); const expectedMinUSDCFromSUSHIForopUSDCInvst = sushiBalanceForopUSDCInvst.mul(oneSUSHIToUSDC).div(parseEther("1")); const expectMinWETHFromSUSHIForopWETHInvst = sushiBalanceForopWETHInvst.mul(oneSUSHIToWETH).div(parseEther("1")); @@ -181,14 +146,6 @@ async function main() { value: "0", data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSDCEarn.address, true]), }, - // // ---- - // claim CRV, CVX for opUSD3Earn - { - to: cvxMIM3lp3GaugeInstance.address, - value: "0", - data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSD3Earn.address, true]), - }, - // // ---- // claim LDO,CRV,CVX for opWETHEarn { to: cvxStEthGaugeInstance.address, @@ -211,35 +168,6 @@ async function main() { uniV3SwapPathForCRVtoUSDC, ]), }, - // // ---- - // // swap CRV to USDC - // { - // to: opUSD3Earn.address, - // value: "0", - // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ - // [ - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // sushiswapRouter.address, - // sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ - // crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn), - // expectedMinUSDCFromCRVForopUSD3Earn, - // [ - // EthereumTokens.REWARD_TOKENS.CRV, - // EthereumTokens.PLAIN_TOKENS.DAI, - // EthereumTokens.PLAIN_TOKENS.USDT, - // EthereumTokens.PLAIN_TOKENS.USDC, - // ], - // opUSD3Earn.address, - // BigNumber.from(timestamp).add(600), - // ]), - // ], - // ), - // ], - // ]), - // }, - // // ---- // harvest CVX for opUSDCEarn { to: opUSDCEarn.address, @@ -256,61 +184,6 @@ async function main() { uniV3SwapPathForCVXtoUSDC, ]), }, - // // ---- - // // harvest CVX for opUSD3Earn - // // swap CVX to USDC - // { - // to: opUSD3Earn.address, - // value: "0", - // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ - // [ - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // sushiswapRouter.address, - // sushiswapRouter.interface.encodeFunctionData("swapExactTokensForTokens", [ - // cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn), - // expectedMinUSDCFromCVXForopUSD3Earn, - // [ - // EthereumTokens.REWARD_TOKENS.CVX, - // EthereumTokens.WRAPPED_TOKENS.WETH, - // EthereumTokens.REWARD_TOKENS.SUSHI, - // EthereumTokens.PLAIN_TOKENS.USDC, - // ], - // opUSD3Earn.address, - // BigNumber.from(timestamp).add(600), - // ]), - // ], - // ), - // ], - // ]), - // }, - // // swap USDC to 3crv - // { - // to: opUSD3Earn.address, - // value: "0", - // data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ - // [ - // ethers.utils.defaultAbiCoder.encode( - // ["address", "bytes"], - // [ - // threeCrvPool.address, - // threeCrvPool.interface.encodeFunctionData("add_liquidity(uint256[3],uint256)", [ - // [ - // 0, - // expectedMinUSDCFromCRVForopUSD3Earn - // .add(expectedMinUSDCFromCVXForopUSD3Earn) - // .add(usdcBalanceForopUSD3Earn), - // 0, - // ], - // "18000000000000000000", - // ]), - // ], - // ), - // ], - // ]), - // }, - // // ---- // harvest CVX for opWETHEarn // harvest LDO for opWETHEarn { @@ -345,34 +218,31 @@ async function main() { "0x", ]), }, - // // harvest SUSHI for opUSDCInvst - // { - // to: opUSDCInvst.address, - // value: "0", - // data: opUSDCInvst.interface.encodeFunctionData("harvest", [ - // sushiInstance.address, - // sushiswapRouter.address, - // false, - // expectedMinUSDCFromSUSHIForopUSDCInvst, - // BigNumber.from(timestamp).add("600"), - // [sushiInstance.address, EthereumTokens.PLAIN_TOKENS.USDT,EthereumTokens.PLAIN_TOKENS.DAI, EthereumTokens.PLAIN_TOKENS.USDC], - // "0x", - // ]), - // }, + // harvest SUSHI for opUSDCInvst + { + to: opUSDCInvst.address, + value: "0", + data: opUSDCInvst.interface.encodeFunctionData("harvest", [ + sushiInstance.address, + sushiswapRouter.address, + false, + expectedMinUSDCFromSUSHIForopUSDCInvst, + BigNumber.from(timestamp).add("600"), + [ + sushiInstance.address, + EthereumTokens.PLAIN_TOKENS.USDT, + EthereumTokens.PLAIN_TOKENS.DAI, + EthereumTokens.PLAIN_TOKENS.USDC, + ], + "0x", + ]), + }, // vaultDepositAllToStrategy for opUSDCEarn { to: opUSDCEarn.address, value: "0", data: opUSDCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), }, - // // ---- - // // vaultDepositAllToStrategy for opUSD3Earn - // { - // to: opUSD3Earn.address, - // value: "0", - // data: opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy"), - // }, - // // ---- // vaultDepositAllToStrategy for opWETHEarn { to: opWETHEarn.address, @@ -385,17 +255,16 @@ async function main() { value: "0", data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), }, - // // vaultDepositAllToStrategy for opUSDCInvst - // { - // to: opUSDCInvst.address, - // value: "0", - // data: opUSDCInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), - // }, + // vaultDepositAllToStrategy for opUSDCInvst + { + to: opUSDCInvst.address, + value: "0", + data: opUSDCInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, ]; const safeOwner = ethers.provider.getSigner(0); const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; - // const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; const ethAdapter = new EthersAdapter({ ethers, signer: safeOwner, diff --git a/scripts/harvest/010_harvest_bDAMM_wbtc.ts b/scripts/harvest/010_harvest_bDAMM_wbtc.ts deleted file mode 100644 index 54e7c627..00000000 --- a/scripts/harvest/010_harvest_bDAMM_wbtc.ts +++ /dev/null @@ -1,194 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import { deployments, ethers } from "hardhat"; -import EthersAdapter from "@gnosis.pm/safe-ethers-lib"; -import Safe from "@gnosis.pm/safe-core-sdk"; -import { MetaTransactionData, EthAdapter } from "@gnosis.pm/safe-core-sdk-types"; -import { - ERC20, - ERC20__factory, - ISwapRouter, - ISwapRouter__factory, - IUniswapV2Router02, - IUniswapV2Router02__factory, -} from "../../typechain"; -import { parseEther, parseUnits } from "ethers/lib/utils"; -import { BigNumber } from "ethers"; -import { comptrollerABI } from "./007_BDAMM"; -import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; - -const redemptionABI = [ - { - inputs: [ - { - internalType: "uint256", - name: "amount", - type: "uint256", - }, - { - internalType: "uint256", - name: "redemptionFee", - type: "uint256", - }, - ], - name: "redeem", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, -]; - -const uniswapV3Router = ethers.utils.getAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"); -async function main() { - const uniswapV3RouterInstance = await ethers.getContractAt(ISwapRouter__factory.abi, uniswapV3Router); - const compoundComptrollerInstance = await ethers.getContractAt( - comptrollerABI, - "0x4F96AB61520a6636331a48A11eaFBA8FB51f74e4", - ); - const bdammRedemption = await ethers.getContractAt(redemptionABI, "0x46b825C8Ff734D534e6E40b2fE2C21643eCBF493"); - const sushiswapRouter = ( - await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) - ); - const opWBTCEarn = ( - await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) - ); - const usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); - - const safeOwner = ethers.provider.getSigner(0); - // const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; - const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; - const ethAdapter = new EthersAdapter({ - ethers, - signer: safeOwner, - }); - const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress }); - const timestamp = await (await ethers.provider.getBlock("latest")).timestamp; - - const uniV3SwapPathForDAMMtoWBTC = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [ - "0xb3207935FF56120f3499e8aD08461Dd403bF16b8", - 10000, - EthereumTokens.PLAIN_TOKENS.USDC, - 500, - EthereumTokens.BTC_TOKENS.WBTC, - ], - ); - const transactions: MetaTransactionData[] = [ - { - to: compoundComptrollerInstance.address, - value: "0", - data: compoundComptrollerInstance.interface.encodeFunctionData("claimComp", [ - [opWBTCEarn.address], - [ - "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80", // dAMM finance - dWBTC - ], - false, - true, - ]), - }, - // { - // to: opWBTCEarn.address, - // value: "0", - // data: opWBTCEarn.interface.encodeFunctionData("giveAllowances", [ - // [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.BTC_TOKENS.WBTC], - // [ - // "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", - // "0xd9e1ce17f2641f24ae83637ab66a2cca9c378b9f", - // "0xE592427A0AEce92De3Edee1F18E0157C05861564", - // ], - // ]), - // }, - { - to: opWBTCEarn.address, - value: "0", - data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ - [ - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - sushiswapRouter.address, - sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ - parseUnits("28.05", "6"), - parseUnits("0.0016", "8"), - [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], - opWBTCEarn.address, - BigNumber.from(timestamp).add("600"), - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - EthereumTokens.PLAIN_TOKENS.USDC, - usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("28.05", "6")]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", - usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("255")]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - bdammRedemption.address, - bdammRedemption.interface.encodeFunctionData("redeem", [ - parseEther("255"), // 50.107374749199942111 - parseUnits("28.05", "6"), - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - uniswapV3Router, - uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ - { - path: uniV3SwapPathForDAMMtoWBTC, - recipient: opWBTCEarn.address, - deadline: BigNumber.from(timestamp).add("600"), - amountIn: parseEther("255"), - amountOutMinimum: parseUnits("0", "6"), - }, - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [opWBTCEarn.address, opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy")], - ), - ], - ]), - }, - // { - // to: opWBTCEarn.address, - // value: "0", - // data: opWBTCEarn.interface.encodeFunctionData("harvest", [ - // "0xb3207935FF56120f3499e8aD08461Dd403bF16b8", - // "0xE592427A0AEce92De3Edee1F18E0157C05861564", - // true, - // parseUnits("0.0004", "8"), - // BigNumber.from(timestamp).add("600"), - // [], - // uniV3SwapPathForDAMMtoWBTC, - // ]), - // }, - // { - // to: opWBTCEarn.address, - // value: "0", - // data: opWBTCEarn.interface.encodeFunctionData("rebalance"), - // }, - ]; - - const safeTransaction = await safeSdk.createTransaction(transactions); - - const tx = await safeSdk.executeTransaction(safeTransaction); - const txR = await tx.transactionResponse?.wait(1); - console.log(txR); -} - -main().then(console.log).catch(console.error); diff --git a/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts new file mode 100644 index 00000000..76b103b5 --- /dev/null +++ b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts @@ -0,0 +1,336 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { deployments, ethers } from "hardhat"; +import EthersAdapter from "@gnosis.pm/safe-ethers-lib"; +import Safe from "@gnosis.pm/safe-core-sdk"; +import Convex from "@optyfi/defi-legos/ethereum/convex"; +import { MetaTransactionData, EthAdapter } from "@gnosis.pm/safe-core-sdk-types"; +import { + ERC20, + ERC20__factory, + ICompound, + ICompound__factory, + ICurveSwap, + ICurveSwap__factory, + ISwapRouter, + ISwapRouter__factory, + IUniswapV2Router02, + IUniswapV2Router02__factory, +} from "../../typechain"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { BigNumber } from "ethers"; +import { comptrollerABI } from "./007_BDAMM"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; +import { getCVXUnclaimed } from "./003_claim_and_swap_CRV_CVX_to_USDC"; + +const redemptionABI = [ + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "uint256", + name: "redemptionFee", + type: "uint256", + }, + ], + name: "redeem", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, +]; + +const uniswapV3Router = ethers.utils.getAddress("0xE592427A0AEce92De3Edee1F18E0157C05861564"); +const dWBTCAddress = "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80"; +async function main() { + const uniswapV3RouterInstance = await ethers.getContractAt(ISwapRouter__factory.abi, uniswapV3Router); + const compoundComptrollerInstance = await ethers.getContractAt( + comptrollerABI, + "0x4F96AB61520a6636331a48A11eaFBA8FB51f74e4", + ); + const bdammRedemption = await ethers.getContractAt(redemptionABI, "0x46b825C8Ff734D534e6E40b2fE2C21643eCBF493"); + const sushiswapRouter = ( + await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) + ); + const opWBTCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) + ); + const opUSD3Earn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) + ); + const usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + + const usdcBalanceForopUSD3Earn = await usdc.balanceOf(opUSD3Earn.address); + + const dWBTCInstance = await ethers.getContractAt(ICompound__factory.abi, dWBTCAddress); + const dWBTCERC20Instance = await ethers.getContractAt(ERC20__factory.abi, dWBTCInstance.address); + + const dwbtcBalance = await dWBTCERC20Instance.balanceOf(opWBTCEarn.address); + + const cvxMIM3lp3GaugeInstance = await ethers.getContractAt(cvxABI, Convex.pools.mim.stakingPool); + + const cvxInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.CVX); + const crvInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.CRV); + + const cvxBalanceForopUSD3Earn = await cvxInstance.balanceOf(opUSD3Earn.address); + const crvUnclaimedForopUSD3Earn = await cvxMIM3lp3GaugeInstance.earned(opUSD3Earn.address); + const crvBalanceForOPUSD3Earn = await crvInstance.balanceOf(opUSD3Earn.address); + + const threeCrvPool = ( + await ethers.getContractAt(ICurveSwap__factory.abi, "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7") + ); + + const cvxUnclaimedForopUSD3Earn = getCVXUnclaimed( + crvUnclaimedForopUSD3Earn, + await cvxInstance.totalSupply(), + BigNumber.from("100000000000000000000000"), + BigNumber.from("1000"), + BigNumber.from("100000000000000000000000000"), + ); + + const oneCRVToUSDC = parseUnits("0.5", "6"); //0.53 crv - usdc + const oneCVXToUSDC = parseUnits("3.3", "6"); // 3.3 + + const expectedMinUSDCFromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) + .mul(oneCRVToUSDC) + .div(parseEther("1")); + const expectedMinUSDCFromCVXForopUSD3Earn = BigNumber.from(cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn)) + .mul(oneCVXToUSDC) + .div(parseEther("1")); + + const safeOwner = ethers.provider.getSigner(0); + const safeAddress = "0xb95dff9A2D1d0003e74A64A1f36eE6767c8fb9Ed"; + const ethAdapter = new EthersAdapter({ + ethers, + signer: safeOwner, + }); + const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress }); + const timestamp = await (await ethers.provider.getBlock("latest")).timestamp; + + const uniV3SwapPathForDAMMtoWBTC = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + "0xb3207935FF56120f3499e8aD08461Dd403bF16b8", + 10000, + EthereumTokens.PLAIN_TOKENS.USDC, + 500, + EthereumTokens.BTC_TOKENS.WBTC, + ], + ); + const uniV3SwapPathForCRVtoUSDC = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 10000, EthereumTokens.PLAIN_TOKENS.USDC], + ); + const uniV3SwapPathForCVXtoUSDC = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.PLAIN_TOKENS.USDC], + ); + const transactions: MetaTransactionData[] = [ + { + to: compoundComptrollerInstance.address, + value: "0", + data: compoundComptrollerInstance.interface.encodeFunctionData("claimComp", [ + [opWBTCEarn.address], + [ + "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80", // dAMM finance - dWBTC + ], + false, + true, + ]), + }, + { + to: opWBTCEarn.address, + value: "0", + data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ + [ + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [dWBTCInstance.address, dWBTCInstance.interface.encodeFunctionData("redeem", [dwbtcBalance])], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + sushiswapRouter.address, + sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ + parseUnits("57.886070", "6"), + parseUnits("0.0035", "8"), + [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], + opWBTCEarn.address, + BigNumber.from(timestamp).add("600"), + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + EthereumTokens.PLAIN_TOKENS.USDC, + usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("57.886070", "6")]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", + usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("526.237")]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + bdammRedemption.address, + bdammRedemption.interface.encodeFunctionData("redeem", [ + parseEther("526.237"), + parseUnits("57.886070", "6"), + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + uniswapV3Router, + uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ + { + path: uniV3SwapPathForDAMMtoWBTC, + recipient: opWBTCEarn.address, + deadline: BigNumber.from(timestamp).add("600"), + amountIn: parseEther("526.237"), + amountOutMinimum: parseUnits("0", "6"), + }, + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [opWBTCEarn.address, opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy")], + ), + ], + ]), + }, + // claim CRV, CVX for opUSD3Earn + { + to: cvxMIM3lp3GaugeInstance.address, + value: "0", + data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSD3Earn.address, true]), + }, + { + to: opUSD3Earn.address, + value: "0", + data: opUSD3Earn.interface.encodeFunctionData("adminCall", [ + [ + // CRV to USDC + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + uniswapV3RouterInstance.address, + uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ + { + path: uniV3SwapPathForCRVtoUSDC, + deadline: BigNumber.from(timestamp).add("600"), + amountIn: crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn), + amountOutMinimum: expectedMinUSDCFromCRVForopUSD3Earn, + recipient: opUSD3Earn.address, + }, + ]), + ], + ), + // CVX to USDC + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + uniswapV3RouterInstance.address, + uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ + { + path: uniV3SwapPathForCVXtoUSDC, + deadline: BigNumber.from(timestamp).add("600"), + amountIn: cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn), + amountOutMinimum: expectedMinUSDCFromCVXForopUSD3Earn, + recipient: opUSD3Earn.address, + }, + ]), + ], + ), + // USDC to 3Crv + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [ + threeCrvPool.address, + threeCrvPool.interface.encodeFunctionData("add_liquidity(uint256[3],uint256)", [ + [ + 0, + expectedMinUSDCFromCRVForopUSD3Earn + .add(expectedMinUSDCFromCVXForopUSD3Earn) + .add(usdcBalanceForopUSD3Earn), + 0, + ], + "32000000000000000000", + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [opUSD3Earn.address, opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy")], + ), + ], + ]), + }, + ]; + + const safeTransaction = await safeSdk.createTransaction(transactions); + + const tx = await safeSdk.executeTransaction(safeTransaction); + const txR = await tx.transactionResponse?.wait(1); + console.log(txR); +} + +main().then(console.log).catch(console.error); + +const cvxABI = [ + { + inputs: [ + { + internalType: "address", + name: "_account", + type: "address", + }, + { + internalType: "bool", + name: "_claimExtras", + type: "bool", + }, + ], + name: "getReward", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "earned", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, +]; From 72a160c7ad6294a820c91e4ed9a28d8bcbf5b0ce Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 21 Dec 2022 15:11:02 -0500 Subject: [PATCH 34/44] test(strategies): deposit / withdraw eth --- .gitmodules | 3 + .prettierignore | 1 + .solhintignore | 3 +- .../protocol/tokenization/VaultHelper.sol | 9 -- .../tokenization/VaultHelperMainnet.sol | 127 ++++++++++++++++++ contracts/utils/BytesLib.sol | 101 ++++++++++++++ contracts/utils/optyfi-oracle | 1 + deploy_mainnet/015_deploy_opWETH-Save.ts | 33 ++++- deploy_mainnet/040_set_price_oracle_feed.ts | 12 ++ helpers/adapters/ethereum/AaveV1Adapter.ts | 26 +++- helpers/adapters/ethereum/AaveV2Adapter.ts | 32 +++-- helpers/adapters/ethereum/CompoundAdapter.ts | 41 +++--- helpers/strategy-manager.ts | 20 +-- helpers/utils.ts | 6 +- test/test-opty/3_vault_strategies.spec.ts | 34 +++-- 15 files changed, 370 insertions(+), 79 deletions(-) delete mode 100644 contracts/protocol/tokenization/VaultHelper.sol create mode 100644 contracts/protocol/tokenization/VaultHelperMainnet.sol create mode 100644 contracts/utils/BytesLib.sol create mode 160000 contracts/utils/optyfi-oracle diff --git a/.gitmodules b/.gitmodules index 7b37dffb..f522f8a4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -44,3 +44,6 @@ path = contracts/protocol/earn-protocol-configuration url = git@github.com:Opty-Fi/earn-protocol-configuration.git branch = feat/multi-strat +[submodule "contracts/utils/optyfi-oracle"] + path = contracts/utils/optyfi-oracle + url = git@github.com:Opty-Fi/optyfi-oracle.git diff --git a/.prettierignore b/.prettierignore index 17b2e77c..0bc354d5 100644 --- a/.prettierignore +++ b/.prettierignore @@ -16,6 +16,7 @@ contracts/protocol/adapters **out/ helpers/types/ deployments/ +contracts/utils/optyfi-oracle # files *.env diff --git a/.solhintignore b/.solhintignore index 9e4b82f9..17b4ffda 100644 --- a/.solhintignore +++ b/.solhintignore @@ -7,4 +7,5 @@ contracts/utils/ contracts/dependencies/ contracts/mocks/ contracts/protocol/earn-protocol-configuration -contracts/protocol/adapters \ No newline at end of file +contracts/protocol/adapters +contracts/utils/optyfi-oracle \ No newline at end of file diff --git a/contracts/protocol/tokenization/VaultHelper.sol b/contracts/protocol/tokenization/VaultHelper.sol deleted file mode 100644 index 3fcc97fe..00000000 --- a/contracts/protocol/tokenization/VaultHelper.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier:MIT - -pragma solidity =0.8.11; - -contract VaultHelper { - function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { - return _arg; - } -} diff --git a/contracts/protocol/tokenization/VaultHelperMainnet.sol b/contracts/protocol/tokenization/VaultHelperMainnet.sol new file mode 100644 index 00000000..485de5a6 --- /dev/null +++ b/contracts/protocol/tokenization/VaultHelperMainnet.sol @@ -0,0 +1,127 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { ICompound } from "@optyfi/defi-legos/ethereum/compound/contracts/ICompound.sol"; +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; +import { ISwapRouter } from "../../interfaces/uniswap/ISwapRouter.sol"; +import { IOptyFiOracle } from "../../utils/optyfi-oracle/contracts/interfaces/IOptyFiOracle.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts-0.8.x/token/ERC20/extensions/IERC20Metadata.sol"; +import { ILendingPool } from "@optyfi/defi-legos/ethereum/aave/contracts/ILendingPool.sol"; +import { IAaveV1Token } from "@optyfi/defi-legos/ethereum/aave/contracts/IAaveV1Token.sol"; + +// libraries +import { BytesLib } from "../../utils/BytesLib.sol"; + +contract VaultHelperMainnet { + using BytesLib for bytes; + + IWETH public immutable WETH; + ISwapRouter public immutable SwapRouter; + + // solhint-disable-next-line var-name-mixedcase + address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); + + constructor(IWETH _WETH, ISwapRouter _SwapRouter) { + WETH = _WETH; + SwapRouter = _SwapRouter; + } + + function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { + return _arg; + } + + function calculateAmountInToken_Compound(address _liquidityPool, uint256 _liquidityPoolTokenAmount) + external + view + returns (uint256) + { + return (_liquidityPoolTokenAmount * ICompound(_liquidityPool).exchangeRateStored()) / 1e18; + } + + function calculateAmountInLPToken_Compound(address _liquidityPool, uint256 _depositAmount) + external + view + returns (uint256) + { + return (_depositAmount * 1e18) / ICompound(_liquidityPool).exchangeRateStored(); + } + + function depositETH_Compound(address _liquidityPool, uint256 _amount) external { + IERC20(address(WETH)).transferFrom(msg.sender, address(this), _amount); + WETH.withdraw(_amount); + uint256 _balanceBeforeLP = IERC20(_liquidityPool).balanceOf(address(this)); + ICompound(_liquidityPool).mint{ value: _amount }(); + uint256 _balanceAfterLP = IERC20(_liquidityPool).balanceOf(address(this)); + IERC20(_liquidityPool).transfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function withdrawETH_Compound(address _liquidityPool, uint256 _amount) external { + uint256 _balanceBeforeETH = address(this).balance; + IERC20(_liquidityPool).transferFrom(msg.sender, address(this), _amount); + ICompound(_liquidityPool).redeem(_amount); + uint256 _balanceAfterETH = address(this).balance; + WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); + IERC20(address(WETH)).transfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); + } + + function depositETH_AaveV1( + address _lendingPool, + address _lpToken, + uint256 _amount + ) external { + IERC20(address(WETH)).transferFrom(msg.sender, address(this), _amount); + WETH.withdraw(_amount); + uint256 _balanceBeforeLP = IERC20(_lpToken).balanceOf(address(this)); + ILendingPool(_lendingPool).deposit{ value: _amount }(ETH, _amount, uint16(0)); + uint256 _balanceAfterLP = IERC20(_lpToken).balanceOf(address(this)); + IERC20(_lpToken).transfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function withdrawETH_AaveV1(address _lpToken, uint256 _amount) external { + uint256 _balanceBeforeETH = address(this).balance; + IERC20(_lpToken).transferFrom(msg.sender, address(this), _amount); + IAaveV1Token(_lpToken).redeem(_amount); + uint256 _balanceAfterETH = address(this).balance; + WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); + IERC20(address(WETH)).transfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); + } + + function exactInput_UniswapV3( + bytes memory _path, + uint256 _deadline, + uint256 _amountIn, + uint256 _amountOutMinimum + ) external payable { + address _inputToken = _path.toAddress(0); + IERC20(_inputToken).transferFrom(msg.sender, address(this), _amountIn); + SwapRouter.exactInput( + ISwapRouter.ExactInputParams({ + path: _path, + recipient: msg.sender, + deadline: _deadline, + amountIn: _amountIn, + amountOutMinimum: _amountOutMinimum + }) + ); + } + + function getMinimumExpectedTokenOutPrice_OptyFiOracle( + IOptyFiOracle _optyFiOracle, + address _tokenIn, + address _tokenOut, + uint256 _amountIn, + uint256 _slippage + ) external view returns (uint256) { + uint256 _price = _optyFiOracle.getTokenPrice(_tokenIn, _tokenOut); + require(_price > uint256(0), "!price"); + uint256 decimalsIn = uint256(IERC20Metadata(_tokenIn).decimals()); + uint256 decimalsOut = uint256(IERC20Metadata(_tokenOut).decimals()); + uint256 _amountOut = (_amountIn * _price * 10**decimalsOut) / 10**(18 + decimalsIn); + return (_amountOut * (10000 - _slippage)) / 10000; + } + + receive() external payable {} +} diff --git a/contracts/utils/BytesLib.sol b/contracts/utils/BytesLib.sol new file mode 100644 index 00000000..31e5f8fb --- /dev/null +++ b/contracts/utils/BytesLib.sol @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * @title Solidity Bytes Arrays Utils + * @author Gonçalo Sá + * + * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. + * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. + */ +pragma solidity >=0.5.0; + +library BytesLib { + function slice( + bytes memory _bytes, + uint256 _start, + uint256 _length + ) internal pure returns (bytes memory) { + require(_length + 31 >= _length, "slice_overflow"); + require(_start + _length >= _start, "slice_overflow"); + require(_bytes.length >= _start + _length, "slice_outOfBounds"); + + bytes memory tempBytes; + + assembly { + switch iszero(_length) + case 0 { + // Get a location of some free memory and store it in tempBytes as + // Solidity does for memory variables. + tempBytes := mload(0x40) + + // The first word of the slice result is potentially a partial + // word read from the original array. To read it, we calculate + // the length of that partial word and start copying that many + // bytes into the array. The first word we copy will start with + // data we don't care about, but the last `lengthmod` bytes will + // land at the beginning of the contents of the new array. When + // we're done copying, we overwrite the full first word with + // the actual length of the slice. + let lengthmod := and(_length, 31) + + // The multiplication in the next line is necessary + // because when slicing multiples of 32 bytes (lengthmod == 0) + // the following copy loop was copying the origin's length + // and then ending prematurely not copying everything it should. + let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) + let end := add(mc, _length) + + for { + // The multiplication in the next line has the same exact purpose + // as the one above. + let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) + } lt(mc, end) { + mc := add(mc, 0x20) + cc := add(cc, 0x20) + } { + mstore(mc, mload(cc)) + } + + mstore(tempBytes, _length) + + //update free-memory pointer + //allocating the array padded to 32 bytes like the compiler does now + mstore(0x40, and(add(mc, 31), not(31))) + } + //if we want a zero-length slice let's just return a zero-length array + default { + tempBytes := mload(0x40) + //zero out the 32 bytes slice we are about to return + //we need to do it because Solidity does not garbage collect + mstore(tempBytes, 0) + + mstore(0x40, add(tempBytes, 0x20)) + } + } + + return tempBytes; + } + + function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { + require(_start + 20 >= _start, "toAddress_overflow"); + require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); + address tempAddress; + + assembly { + tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) + } + + return tempAddress; + } + + function toUint24(bytes memory _bytes, uint256 _start) internal pure returns (uint24) { + require(_start + 3 >= _start, "toUint24_overflow"); + require(_bytes.length >= _start + 3, "toUint24_outOfBounds"); + uint24 tempUint; + + assembly { + tempUint := mload(add(add(_bytes, 0x3), _start)) + } + + return tempUint; + } +} diff --git a/contracts/utils/optyfi-oracle b/contracts/utils/optyfi-oracle new file mode 160000 index 00000000..6eff5713 --- /dev/null +++ b/contracts/utils/optyfi-oracle @@ -0,0 +1 @@ +Subproject commit 6eff57139927efa3f6e749267dfe952b02f1a8d8 diff --git a/deploy_mainnet/015_deploy_opWETH-Save.ts b/deploy_mainnet/015_deploy_opWETH-Save.ts index 5cb4f921..983495f9 100644 --- a/deploy_mainnet/015_deploy_opWETH-Save.ts +++ b/deploy_mainnet/015_deploy_opWETH-Save.ts @@ -5,10 +5,11 @@ import { BigNumber } from "ethers"; import { getAddress, parseEther } from "ethers/lib/utils"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; +import { ERC20, ERC20__factory, Vault, VaultHelperMainnet__factory, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -159,14 +160,31 @@ const func: DeployFunction = async ({ const vaultInstance = ( await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) ); + + const vaultHelperMainnetInstance = await ethers.getContractAt( + VaultHelperMainnet__factory.abi, + ( + await deployments.get("VaultHelperMainnet") + ).address, + ); + const approvalTokens = []; const approvalSpender = []; - const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.WETH); const awethInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.weth.lpToken); + const cethInstance = await ethers.getContractAt(ERC20__factory.abi, Compound.pools.eth.lpToken); const wethAaveV2Allowance = await wethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); const awethAaveV2Allowance = await awethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const wethVaultHelperMainnetAllowance = await wethInstance.allowance( + vaultInstance.address, + vaultHelperMainnetInstance.address, + ); + const cethVaultHelperMainnetAllowance = await cethInstance.allowance( + vaultInstance.address, + vaultHelperMainnetInstance.address, + ); if (!wethAaveV2Allowance.gt(parseEther("1000000"))) { approvalTokens.push(wethInstance.address); @@ -177,6 +195,17 @@ const func: DeployFunction = async ({ approvalTokens.push(awethInstance.address); approvalSpender.push(AaveV2.LendingPool.address); } + + if (!wethVaultHelperMainnetAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(wethInstance.address); + approvalSpender.push(vaultHelperMainnetInstance.address); + } + + if (!cethVaultHelperMainnetAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(cethInstance.address); + approvalSpender.push(vaultHelperMainnetInstance.address); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/deploy_mainnet/040_set_price_oracle_feed.ts b/deploy_mainnet/040_set_price_oracle_feed.ts index eaf160fb..8524953b 100644 --- a/deploy_mainnet/040_set_price_oracle_feed.ts +++ b/deploy_mainnet/040_set_price_oracle_feed.ts @@ -14,6 +14,8 @@ const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvir const USDC_WETH_FEED = "0x986b5E1e1755e3C2440e960477f25201B0a8bbD4"; const WBTC_WETH_FEED = "0xdeb288F737066589598e9214E782fa5A8eD689e8"; const WETH_WBTC_FEED = "0xAc559F25B1619171CbC396a50854A3240b6A4e99"; + const COMP_ETH_FEED = "0x1B39Ee86Ec5979ba5C322b826B3ECb8C79991699"; + const COMP_USD_FEED = "0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5"; const owner = await optyfiOracleInstance.owner(); const feedToTokens = [ @@ -32,6 +34,16 @@ const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvir tokenB: ethereumTokens.BTC_TOKENS.WBTC, priceFeed: WETH_WBTC_FEED, }, + { + tokenA: ethereumTokens.REWARD_TOKENS.COMP, + tokenB: ethereumTokens.WRAPPED_TOKENS.WETH, + priceFeed: COMP_ETH_FEED, + }, + { + tokenA: ethereumTokens.REWARD_TOKENS.COMP, + tokenB: ethereumTokens.PLAIN_TOKENS.USD, + priceFeed: COMP_USD_FEED, + }, ]; const pendingFeedToTokens = []; for (const feedToToken of feedToTokens) { diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index fed561cd..76d6f982 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -9,6 +9,14 @@ import { AdapterInterface } from "../AdapterInterface"; import { ERC20__factory, IWETH9__factory } from "../../../typechain"; import { JsonRpcProvider } from "@ethersproject/providers"; export class AaveV1Adapter implements AdapterInterface { + vaultHelperMainnetInstance; + + constructor(vaultHelperMainnetContract: Contract) { + this.vaultHelperMainnetInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), + ); + } + getDepositPlan( planner: weirollPlanner, vaultInstance: Contract, @@ -18,14 +26,13 @@ export class AaveV1Adapter implements AdapterInterface { isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { - // TODO handle ETH deposit - const lendingPoolContract = new ethers.Contract("0x398eC7346DcD622eDc5ae82352F02bE94C62d119", Aave.LendingPool.abi); + const lendingPoolContract = new ethers.Contract(Aave.LendingPool.address, Aave.LendingPool.abi); const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); - planner.add(wethContract["withdraw(uint256)"](inputTokenAmount)); planner.add( - lendingPoolInstance["deposit(address,uint256,uint16)"](inputToken, inputTokenAmount, "0").withValue( + this.vaultHelperMainnetInstance["depositETH_AaveV1(address,address,uint256)"]( + lendingPoolContract.address, + outputToken, inputTokenAmount, ), ); @@ -44,10 +51,15 @@ export class AaveV1Adapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { - // TODO handle ETH withdraw const lpTokenContract = new ethers.Contract(outputToken, Aave.ATokenAbi); const lpTokenInstance = weirollContract.createContract(lpTokenContract); - planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add( + this.vaultHelperMainnetInstance["withdrawETH_AaveV1(address,uint256)"](outputToken, outputTokenAmount), + ); + } else { + planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); + } return planner; } diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index ee83a148..f8c0a903 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -6,11 +6,21 @@ import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { getAddress } from "ethers/lib/utils"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; -import { ERC20__factory, ISwapRouter, ISwapRouter__factory } from "../../../typechain"; +import { ERC20__factory } from "../../../typechain"; import { JsonRpcProvider } from "@ethersproject/providers"; const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; export class Aavev2Adapter implements AdapterInterface { + optyFiOracleAddress; + vaultHelperMainnetInstance; + + constructor(vaultHelperMainnetContract: Contract, _optyFiOracleAddress: string) { + this.optyFiOracleAddress = _optyFiOracleAddress; + this.vaultHelperMainnetInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), + ); + } + getDepositPlan( planner: weirollPlanner, vaultInstance: Contract, @@ -112,15 +122,14 @@ export class Aavev2Adapter implements AdapterInterface { vaultInstance: Contract, vaultUnderlyingToken: string, ): weirollPlanner { - // TODO consider adding minimum amount to receive - const uniswapV3RouterContract = new ethers.Contract( - "0xE592427A0AEce92De3Edee1F18E0157C05861564", - ISwapRouter__factory.abi, - ); - const uniswapV3RouterInstance = weirollContract.createContract(uniswapV3RouterContract); const rewardContract = new ethers.Contract(STK_AAVE, ERC20__factory.abi); const rewardInstance = weirollContract.createContract(rewardContract); const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const minumumOutputAmount = planner.add( + this.vaultHelperMainnetInstance[ + "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" + ](this.optyFiOracleAddress, rewardInstance.address, vaultUnderlyingToken, rewardAmount, 100).staticcall(), + ); let univ3Path; switch (getAddress(vaultUnderlyingToken)) { case getAddress(EthereumTokens.PLAIN_TOKENS.USDT): { @@ -195,13 +204,12 @@ export class Aavev2Adapter implements AdapterInterface { } } planner.add( - uniswapV3RouterInstance["exactInput((bytes,address,uint256,uint256,uint256))"]([ + this.vaultHelperMainnetInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( univ3Path, - vaultInstance.address, ethers.constants.MaxUint256, - 0, // TODO add reward amount - 0, - ]), + rewardAmount, + minumumOutputAmount, + ), ); return planner; } diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index b8180f40..b27f0934 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -11,12 +11,14 @@ import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; export class CompoundAdapter implements AdapterInterface { - compoundAdapterInstance; + vaultHelperMainnetInstance; + optyFiOracleAddress; - constructor(compoundAdapterInstance: Contract) { - this.compoundAdapterInstance = weirollContract.createContract( - new ethers.Contract(compoundAdapterInstance.address, compoundAdapterInstance.interface), + constructor(vaultHelperMainnetContract: Contract, _optyFiOracleAddress: string) { + this.vaultHelperMainnetInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), ); + this.optyFiOracleAddress = _optyFiOracleAddress; } getDepositPlan( @@ -30,9 +32,7 @@ export class CompoundAdapter implements AdapterInterface { ): weirollPlanner { const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); - planner.add(wethContract["withdraw(uint256)"](inputTokenAmount)); - planner.add(poolInstance["mint()"].withValue(inputTokenAmount)); + planner.add(this.vaultHelperMainnetInstance["depositETH_Compound(address,uint256)"](pool, inputTokenAmount)); } else { planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); } @@ -50,9 +50,7 @@ export class CompoundAdapter implements AdapterInterface { ): weirollPlanner { const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); - const wethContract = weirollContract.createContract(new ethers.Contract(inputToken, IWETH9__factory.abi)); - planner.add(wethContract["deposit(uint256)"].withValue(outputTokenAmount)); + planner.add(this.vaultHelperMainnetInstance["withdrawETH_Compound(address,uint256)"](pool, outputTokenAmount)); } else { planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); } @@ -69,8 +67,7 @@ export class CompoundAdapter implements AdapterInterface { outputTokenAmount: ReturnValue, ): ReturnValue { const amountUT = planner.add( - this.compoundAdapterInstance["getSomeAmountInToken(address,address,uint256)"]( - inputToken, + this.vaultHelperMainnetInstance["calculateAmountInToken_Compound(address,uint256)"]( pool, outputTokenAmount, ).staticcall(), @@ -88,8 +85,7 @@ export class CompoundAdapter implements AdapterInterface { inputTokenAmount: ReturnValue, ): ReturnValue { const amountLP = planner.add( - this.compoundAdapterInstance["calculateAmountInLPToken(address,address,uint256)"]( - inputToken, + this.vaultHelperMainnetInstance["calculateAmountInLPToken_Compound(address,uint256)"]( pool, inputTokenAmount, ).staticcall(), @@ -135,19 +131,22 @@ export class CompoundAdapter implements AdapterInterface { vaultInstance: Contract, vaultUnderlyingToken: string, ): weirollPlanner { - // TODO add read call to oracle for computing minimum expected const uniswapV2RouterContract = new ethers.Contract(UniswapV2.router02.address, UniswapV2.router02.abi); const uniswapV2RouterInstance = weirollContract.createContract(uniswapV2RouterContract); const rewardContract = new ethers.Contract(EthereumTokens.REWARD_TOKENS.COMP, ERC20__factory.abi); const rewardInstance = weirollContract.createContract(rewardContract); const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); - + const minumumOutputAmount = planner.add( + this.vaultHelperMainnetInstance[ + "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" + ](this.optyFiOracleAddress, rewardInstance.address, vaultUnderlyingToken, rewardAmount, 100).staticcall(), + ); switch (getAddress(vaultUnderlyingToken)) { case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - 0, + minumumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], vaultInstance.address, ethers.constants.MaxUint256, @@ -159,7 +158,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - 0, + minumumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH], vaultInstance.address, ethers.constants.MaxUint256, @@ -171,7 +170,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - 0, + minumumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.DAI], vaultInstance.address, ethers.constants.MaxUint256, @@ -183,7 +182,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - 0, + minumumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDT], vaultInstance.address, ethers.constants.MaxUint256, @@ -195,7 +194,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - 0, + minumumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.BTC_TOKENS.WBTC], vaultInstance.address, ethers.constants.MaxUint256, diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index eaa104a7..cde9e667 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -13,17 +13,17 @@ export class StrategyManager { public readonly compoundAdapterObj: AdapterInterface; public readonly aaveV1AdapterObj: AdapterInterface; public readonly aaveV2AdapterObj: AdapterInterface; - public readonly vaultHelperContract: weirollContract; + public readonly vaultHelperMainnetContract: weirollContract; public readonly liquidityPoolToAdapter: { [key: string]: AdapterInterface } = {}; - constructor(vaultHelperInstance: Contract, compoundAdapterInstance: Contract) { - this.vaultHelperContract = weirollContract.createContract( - new ethers.Contract(vaultHelperInstance.address, vaultHelperInstance.interface), + constructor(vaultHelperMainnetInstance: Contract, optyFiOracleAddress: string) { + this.vaultHelperMainnetContract = weirollContract.createContract( + new ethers.Contract(vaultHelperMainnetInstance.address, vaultHelperMainnetInstance.interface), ); - this.compoundAdapterObj = new CompoundAdapter(compoundAdapterInstance); - this.aaveV1AdapterObj = new AaveV1Adapter(); - this.aaveV2AdapterObj = new Aavev2Adapter(); + this.compoundAdapterObj = new CompoundAdapter(vaultHelperMainnetInstance, optyFiOracleAddress); + this.aaveV1AdapterObj = new AaveV1Adapter(vaultHelperMainnetInstance); + this.aaveV2AdapterObj = new Aavev2Adapter(vaultHelperMainnetInstance, optyFiOracleAddress); this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; @@ -160,7 +160,7 @@ export class StrategyManager { ); } } - planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); + planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } @@ -204,7 +204,7 @@ export class StrategyManager { ); } } - planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } @@ -219,7 +219,7 @@ export class StrategyManager { new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), ); const amountLP = planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address).staticcall()); - planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } diff --git a/helpers/utils.ts b/helpers/utils.ts index 35185e0d..568ec7b8 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -32,6 +32,7 @@ import { CurveExchangeAdapter, StrategyRegistry, ERC20__factory, + VaultHelperMainnet, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -43,7 +44,6 @@ import { RiskManagerProxyV1 } from "./types/riskManagerv1"; import { RegistryV2 } from "./types/registryV2/RegistryV2"; import { RiskManagerV2 } from "./types/riskManagerv2"; import { StrategyManager } from "./strategy-manager"; -import { VaultHelper } from "../typechain/VaultHelper"; import { StrategyStepType } from "./type"; import { JsonRpcProvider } from "@ethersproject/providers"; import { parseEther, parseUnits } from "ethers/lib/utils"; @@ -99,7 +99,7 @@ declare module "mocha" { curveDepositPoolAdapter: CurveDepositPoolAdapter; curveSwapPoolAdapter: CurveSwapPoolAdapter; curveSwapEthGateway: CurveSwapETHGateway; - // strategyProvider: StrategyProvider; + strategyProvider: StrategyProvider; strategyRegistry: StrategyRegistry; strategyManager: StrategyManager; strategyProviderV1: StrategyProviderV1; @@ -108,7 +108,7 @@ declare module "mocha" { riskManagerV2: RiskManagerV2; vault: Vault; vaultProxy: InitializableImmutableAdminUpgradeabilityProxy; - vaultHelper: VaultHelper; + vaultHelperMainnet: VaultHelperMainnet; opUSDCearn: Vault; opUSDCearnProxy: InitializableImmutableAdminUpgradeabilityProxy; opWETHearn: Vault; diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index 841b987e..460cd411 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -25,14 +25,10 @@ import { Registry__factory, Vault__factory, ERC20__factory, - CurveSwapPoolAdapter, - CurveSwapPoolAdapter__factory, - CurveSwapETHGateway, - CurveSwapETHGateway__factory, IComptroller__factory, - CompoundAdapter__factory, - CompoundAdapter, StrategyRegistry__factory, + VaultHelperMainnet, + VaultHelperMainnet__factory, } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; @@ -40,8 +36,6 @@ import { getPreActionState, setTokenBalanceInStorage } from "./utils"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; import { TypedTokens } from "../../helpers/data"; import { StrategyManager } from "../../helpers/strategy-manager"; -import { VaultHelper__factory } from "../../typechain/factories/VaultHelper__factory"; -import { VaultHelper } from "../../typechain/VaultHelper"; chai.use(solidity); @@ -51,9 +45,21 @@ const IGNORE_VAULTS = process.env.IGNORE_VAULTS; const strategyHashReadIndexes: { [key: string]: { valueLPIndex: number } } = { "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { + // dai-DEPOSIT-AaveV2-aDAI valueLPIndex: 0, }, - "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { valueLPIndex: 2 }, + "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { + // dai-DEPOSIT-Compound-cDAI + valueLPIndex: 2, + }, + "0x514e845e4f1401cfe30f214a1386dfd06a98e15316dde7f669889a82ddffdeb8": { + // weth-DEPOSIT-AaveV2-aWETH + valueLPIndex: 0, + }, + "0x87ed056054f13b934a9864226aa8c1f52ad63a7ad25bb2c74c1f920c9635c04c": { + // weth-DEPOSIT-Compound-cETH + valueLPIndex: 1, + }, }; describe(`${fork}-Vault-rev7`, () => { @@ -64,13 +70,13 @@ describe(`${fork}-Vault-rev7`, () => { await deployments.get("StrategyRegistry") ).address, ); - this.compoundAdapter = ( - await ethers.getContractAt(CompoundAdapter__factory.abi, (await deployments.get("CompoundAdapter")).address) + this.vaultHelperMainnet = ( + await ethers.getContractAt(VaultHelperMainnet__factory.abi, (await deployments.get("VaultHelperMainnet")).address) ); - this.vaultHelper = ( - await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + this.strategyManager = new StrategyManager( + this.vaultHelperMainnet, + (await deployments.get("OptyFiOracle")).address, ); - this.strategyManager = new StrategyManager(this.vaultHelper, this.compoundAdapter); this.registry = ( await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) ); From dea80a8cd56d578b1bab300105e3570dcb871673 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 21 Dec 2022 23:50:30 -0500 Subject: [PATCH 35/44] test(strategies): save except USDT --- contracts/protocol/tokenization/Vault.sol | 11 - deploy/003_deploy_riskmanager.ts | 96 -------- ...stry.ts => 003_deploy_strategyregistry.ts} | 0 ...uilder.ts => 004_deploy_commandbuilder.ts} | 0 deploy/004_deploy_strategyprovider.ts | 92 -------- deploy/005_deploy_strategymanager.ts | 64 ------ deploy/006_deploy_harvestcodeprovider.ts | 94 -------- deploy/009_deploy_vaulthelper.ts | 63 ------ .../001_deploy_curveswappooladapter.ts | 60 ----- ..._oracle.ts => 001_deploy_optyfi_oracle.ts} | 0 .../002_deploy_curvemetapoolswapadapter.ts | 62 ------ ...er.ts => 002_deploy_vaulthelpermainnet.ts} | 32 +-- deploy_mainnet/003_deploy_aavev1adapter.ts | 62 ------ ...WETH-Save.ts => 003_deploy_opWETH-Save.ts} | 0 deploy_mainnet/004_deploy_aavev2adapter.ts | 62 ------ ...WETH-Earn.ts => 004_deploy_opWETH-Earn.ts} | 0 ...WO-Invst.ts => 005_deploy_opNEWO-Invst.ts} | 0 deploy_mainnet/006_deploy_convexadapter.ts | 62 ------ ...VE-Invst.ts => 006_deploy_opAAVE-Invst.ts} | 0 .../007_deploy_newostakingadapter.ts | 62 ------ ...APE-Invst.ts => 007_deploy_opAPE-Invst.ts} | 0 ...NA-Invst.ts => 008_deploy_opMANA-Invst.ts} | 0 ...I-Invst.ts => 009_deploy_opSUSHI-Invst.ts} | 0 ...009_deploy_sushiswappooladapterethereum.ts | 158 ------------- ...NK-Invst.ts => 010_deploy_opLINK-Invst.ts} | 0 ...010_deploy_sushiswapmasterchefv1adapter.ts | 112 ---------- ...ENS-Invst.ts => 011_deploy_opENS-Invst.ts} | 0 ...011_deploy_sushiswapmasterchefv2adapter.ts | 122 ---------- ...MP-Invst.ts => 012_deploy_opCOMP-Invst.ts} | 0 .../012_deploy_sushi-bar-adapter.ts | 63 ------ .../013_deploy_curvecryptopooladapter.ts | 69 ------ ...IMX-Invst.ts => 013_deploy_opIMX-Invst.ts} | 0 .../014_deploy_metapoolgaugeadapter.ts | 60 ----- ...CX-Invst.ts => 014_deploy_opALCX-Invst.ts} | 0 ...CRV-Invst.ts => 015_deploy_opCRV-Invst.ts} | 0 ...CVX-Invst.ts => 016_deploy_opCVX-Invst.ts} | 0 ...YFI-Invst.ts => 017_deploy_opYFI-Invst.ts} | 0 ...opDAI-Save.ts => 018_deploy_opDAI-Save.ts} | 0 ...USDT-Save.ts => 019_deploy_opUSDT-Save.ts} | 0 ...WBTC-Save.ts => 020_deploy_opWBTC-Save.ts} | 0 ...USD3-Earn.ts => 021_deploy_opUSD3-Earn.ts} | 0 ...WBTC-Earn.ts => 022_deploy_opWBTC-Earn.ts} | 0 ...USDC-Earn.ts => 023_deploy_opUSDC-Earn.ts} | 0 ...USDC-Save.ts => 024_deploy_opUSDC-Save.ts} | 0 ...DC-Invst.ts => 025_deploy_opUSDC-Invst.ts} | 0 ...TH-Invst.ts => 026_deploy_opWETH-Invst.ts} | 0 ...e_feed.ts => 027_set_price_oracle_feed.ts} | 0 ...039_approveandmapliquiditypooltoadapter.ts | 210 ------------------ .../041_deploy_curveexchangeadapter.ts | 94 -------- ...deploy_uniswapv2exchangeadapterethereum.ts | 109 --------- ...deploy_sushiswapexchangeadapterethereum.ts | 110 --------- .../044_approveandmapswappooltoadapter.ts | 122 ---------- helpers/data/adapter-with-strategies.ts | 2 +- helpers/strategy-manager.ts | 14 +- helpers/utils.ts | 2 +- test/test-opty/3_vault_strategies.spec.ts | 130 ++++++----- 56 files changed, 102 insertions(+), 2097 deletions(-) delete mode 100644 deploy/003_deploy_riskmanager.ts rename deploy/{007_deploy_strategyregistry.ts => 003_deploy_strategyregistry.ts} (100%) rename deploy/{008_deploy_commandbuilder.ts => 004_deploy_commandbuilder.ts} (100%) delete mode 100644 deploy/004_deploy_strategyprovider.ts delete mode 100644 deploy/005_deploy_strategymanager.ts delete mode 100644 deploy/006_deploy_harvestcodeprovider.ts delete mode 100644 deploy/009_deploy_vaulthelper.ts delete mode 100644 deploy_mainnet/001_deploy_curveswappooladapter.ts rename deploy_mainnet/{008_deploy_optyfi_oracle.ts => 001_deploy_optyfi_oracle.ts} (100%) delete mode 100644 deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts rename deploy_mainnet/{005_deploy_compoundadapter.ts => 002_deploy_vaulthelpermainnet.ts} (61%) delete mode 100644 deploy_mainnet/003_deploy_aavev1adapter.ts rename deploy_mainnet/{015_deploy_opWETH-Save.ts => 003_deploy_opWETH-Save.ts} (100%) delete mode 100644 deploy_mainnet/004_deploy_aavev2adapter.ts rename deploy_mainnet/{016_deploy_opWETH-Earn.ts => 004_deploy_opWETH-Earn.ts} (100%) rename deploy_mainnet/{017_deploy_opNEWO-Invst.ts => 005_deploy_opNEWO-Invst.ts} (100%) delete mode 100644 deploy_mainnet/006_deploy_convexadapter.ts rename deploy_mainnet/{018_deploy_opAAVE-Invst.ts => 006_deploy_opAAVE-Invst.ts} (100%) delete mode 100644 deploy_mainnet/007_deploy_newostakingadapter.ts rename deploy_mainnet/{019_deploy_opAPE-Invst.ts => 007_deploy_opAPE-Invst.ts} (100%) rename deploy_mainnet/{020_deploy_opMANA-Invst.ts => 008_deploy_opMANA-Invst.ts} (100%) rename deploy_mainnet/{021_deploy_opSUSHI-Invst.ts => 009_deploy_opSUSHI-Invst.ts} (100%) delete mode 100644 deploy_mainnet/009_deploy_sushiswappooladapterethereum.ts rename deploy_mainnet/{022_deploy_opLINK-Invst.ts => 010_deploy_opLINK-Invst.ts} (100%) delete mode 100644 deploy_mainnet/010_deploy_sushiswapmasterchefv1adapter.ts rename deploy_mainnet/{023_deploy_opENS-Invst.ts => 011_deploy_opENS-Invst.ts} (100%) delete mode 100644 deploy_mainnet/011_deploy_sushiswapmasterchefv2adapter.ts rename deploy_mainnet/{024_deploy_opCOMP-Invst.ts => 012_deploy_opCOMP-Invst.ts} (100%) delete mode 100644 deploy_mainnet/012_deploy_sushi-bar-adapter.ts delete mode 100644 deploy_mainnet/013_deploy_curvecryptopooladapter.ts rename deploy_mainnet/{025_deploy_opIMX-Invst.ts => 013_deploy_opIMX-Invst.ts} (100%) delete mode 100644 deploy_mainnet/014_deploy_metapoolgaugeadapter.ts rename deploy_mainnet/{026_deploy_opALCX-Invst.ts => 014_deploy_opALCX-Invst.ts} (100%) rename deploy_mainnet/{027_deploy_opCRV-Invst.ts => 015_deploy_opCRV-Invst.ts} (100%) rename deploy_mainnet/{028_deploy_opCVX-Invst.ts => 016_deploy_opCVX-Invst.ts} (100%) rename deploy_mainnet/{029_deploy_opYFI-Invst.ts => 017_deploy_opYFI-Invst.ts} (100%) rename deploy_mainnet/{030_deploy_opDAI-Save.ts => 018_deploy_opDAI-Save.ts} (100%) rename deploy_mainnet/{031_deploy_opUSDT-Save.ts => 019_deploy_opUSDT-Save.ts} (100%) rename deploy_mainnet/{032_deploy_opWBTC-Save.ts => 020_deploy_opWBTC-Save.ts} (100%) rename deploy_mainnet/{033_deploy_opUSD3-Earn.ts => 021_deploy_opUSD3-Earn.ts} (100%) rename deploy_mainnet/{034_deploy_opWBTC-Earn.ts => 022_deploy_opWBTC-Earn.ts} (100%) rename deploy_mainnet/{035_deploy_opUSDC-Earn.ts => 023_deploy_opUSDC-Earn.ts} (100%) rename deploy_mainnet/{036_deploy_opUSDC-Save.ts => 024_deploy_opUSDC-Save.ts} (100%) rename deploy_mainnet/{037_deploy_opUSDC-Invst.ts => 025_deploy_opUSDC-Invst.ts} (100%) rename deploy_mainnet/{038_deploy_opWETH-Invst.ts => 026_deploy_opWETH-Invst.ts} (100%) rename deploy_mainnet/{040_set_price_oracle_feed.ts => 027_set_price_oracle_feed.ts} (100%) delete mode 100644 deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts delete mode 100644 deploy_mainnet/041_deploy_curveexchangeadapter.ts delete mode 100644 deploy_mainnet/042_deploy_uniswapv2exchangeadapterethereum.ts delete mode 100644 deploy_mainnet/043_deploy_sushiswapexchangeadapterethereum.ts delete mode 100644 deploy_mainnet/044_approveandmapswappooltoadapter.ts diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index 1313732f..ac08e3d9 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -3,8 +3,6 @@ pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; -//NOTE: Variable Shadowing - // helper contracts import { ReentrancyGuard } from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { VersionedInitializable } from "../../dependencies/openzeppelin/VersionedInitializable.sol"; @@ -28,7 +26,6 @@ import { IERC20Permit } from "@openzeppelin/contracts/drafts/IERC20Permit.sol"; import { IERC20PermitLegacy } from "../../interfaces/opty/IERC20PermitLegacy.sol"; import { IVault } from "../../interfaces/opty/IVault.sol"; import { IRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IRegistry.sol"; -import { IRiskManager } from "../earn-protocol-configuration/contracts/interfaces/opty/IRiskManager.sol"; import { IStrategyRegistry } from "../earn-protocol-configuration/contracts/interfaces/opty/IStrategyRegistry.sol"; /** @@ -363,10 +360,6 @@ contract Vault is */ function addStrategy(bytes32 _strategyHash) external override { _onlyStrategyOperator(); - IRiskManager(registryContract.getRiskManager()).isValidStrategy( - _strategyHash, - (vaultConfiguration >> 240) & 0xFF - ); require(strategies.add(_strategyHash), Errors.ADD_STRATEGY); emit AddStrategy(_strategyHash); } @@ -716,10 +709,6 @@ contract Vault is _onlyStrategyOperator(); _checkVaultDeposit(); require(strategies.contains(_strategyHash), Errors.STRATEGY_NOT_SET); - IRiskManager(registryContract.getRiskManager()).isValidStrategy( - _strategyHash, - (vaultConfiguration >> 240) & 0xFF - ); _cacheValueUT = _depositValueUT; DataTypes.StrategyPlanInput memory _strategyPlanInput = IStrategyRegistry(registryContract.getStrategyRegistry()).getDepositSomeToStrategyPlan( diff --git a/deploy/003_deploy_riskmanager.ts b/deploy/003_deploy_riskmanager.ts deleted file mode 100644 index f8cb57e4..00000000 --- a/deploy/003_deploy_riskmanager.ts +++ /dev/null @@ -1,96 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { Registry, Registry__factory, RiskManager, RiskManager__factory } from "../typechain"; -import { getAddress } from "ethers/lib/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.RISK_MANAGER); - const registryProxy = await deployments.get("RegistryProxy"); - const chainId = await getChainId(); - const networkName = network.name; - let feeData = await ethers.provider.getFeeData(); - const result = await deploy("RiskManager", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxy.address], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const riskManager = await deployments.get("RiskManager"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "RiskManager", - address: riskManager.address, - constructorArguments: [registryProxy.address], - contract: "contracts/protocol/earn-protocol-configuration/contracts/RiskManager.sol:RiskManager", - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "RiskManager", - address: riskManager.address, - constructorArguments: [registryProxy.address], - contract: "contracts/protocol/earn-protocol-configuration/contracts/RiskManager.sol:RiskManager", - }); - } - } - } - - const riskManagerInstance = ( - await ethers.getContractAt(RiskManager__factory.abi, (await deployments.get("RiskManager")).address) - ); - const registryInstance = ( - await ethers.getContractAt(Registry__factory.abi, await (await deployments.get("RegistryProxy")).address) - ); - const operatorAddress = await registryInstance.operator(); - const operatorSigner = await ethers.getSigner(operatorAddress); - const riskManagerRegisteredInRegistry = await registryInstance.riskManager(); - if (getAddress(riskManagerRegisteredInRegistry) != getAddress(riskManagerInstance.address)) { - console.log("operator registering RiskManager ..."); - console.log("\n"); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - feeData = await ethers.provider.getFeeData(); - const setRiskManagerTx = await registryInstance - .connect(operatorSigner) - .setRiskManager(riskManagerInstance.address, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await setRiskManagerTx.wait(); - } else { - console.log("Signer is not the operator"); - } - } else { - console.log("Risk manager is already registered."); - console.log("\n"); - } -}; -export default func; -func.tags = ["RiskManager"]; -func.dependencies = ["Registry"]; diff --git a/deploy/007_deploy_strategyregistry.ts b/deploy/003_deploy_strategyregistry.ts similarity index 100% rename from deploy/007_deploy_strategyregistry.ts rename to deploy/003_deploy_strategyregistry.ts diff --git a/deploy/008_deploy_commandbuilder.ts b/deploy/004_deploy_commandbuilder.ts similarity index 100% rename from deploy/008_deploy_commandbuilder.ts rename to deploy/004_deploy_commandbuilder.ts diff --git a/deploy/004_deploy_strategyprovider.ts b/deploy/004_deploy_strategyprovider.ts deleted file mode 100644 index 12956348..00000000 --- a/deploy/004_deploy_strategyprovider.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { Registry } from "../typechain"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.STRATEGY_PROVIDER); - const chainId = await getChainId(); - const networkName = network.name; - const { getAddress } = ethers.utils; - - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - let feeData = await ethers.provider.getFeeData(); - const result = await deploy("StrategyProvider", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - const registryV2Instance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); - const operatorAddress = await registryV2Instance.operator(); - const operatorSigner = await ethers.getSigner(operatorAddress); - - const oldStrategyProvider = await registryV2Instance.getStrategyProvider(); - const strategyProviderV2 = await deployments.get("StrategyProvider"); - - console.log("==StrategyProvider registration=="); - console.log("\n"); - if (getAddress(oldStrategyProvider) !== getAddress(strategyProviderV2.address)) { - console.log("operator registering StrategyProvider.."); - feeData = await ethers.provider.getFeeData(); - const setStrategyProviderTx = await registryV2Instance - .connect(operatorSigner) - .setStrategyProvider(strategyProviderV2.address, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await setStrategyProviderTx.wait(1); - } else { - console.log("StrategyProvider already registered"); - console.log("\n"); - } - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const strategyProviderV2 = await deployments.get("StrategyProvider"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "StrategyProvider", - address: strategyProviderV2.address, - constructorArguments: [registryProxyAddress], - contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyProvider.sol:StrategyProvider", - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "StrategyProvider", - address: strategyProviderV2.address, - constructorArguments: [registryProxyAddress], - contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyProvider.sol:StrategyProvider", - }); - } - } - } -}; -export default func; -func.tags = ["StrategyProvider"]; -func.dependencies = ["Registry"]; diff --git a/deploy/005_deploy_strategymanager.ts b/deploy/005_deploy_strategymanager.ts deleted file mode 100644 index 7b44c2d8..00000000 --- a/deploy/005_deploy_strategymanager.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.STRATEGY_MANAGER); - const chainId = await getChainId(); - const networkName = network.name; - - const feeData = await ethers.provider.getFeeData(); - - const result = await deploy("StrategyManager", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY === "true") { - if (result.newlyDeployed) { - const strategyManager = await deployments.get("StrategyManager"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "StrategyManager", - address: strategyManager.address, - constructorArguments: [], - contract: "contracts/protocol/lib/StrategyManager.sol:StrategyManager", - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "StrategyManager", - address: strategyManager.address, - constructorArguments: [], - contract: "contracts/protocol/lib/StrategyManager.sol:StrategyManager", - }); - } - } - } -}; -export default func; -func.tags = ["StrategyManager"]; diff --git a/deploy/006_deploy_harvestcodeprovider.ts b/deploy/006_deploy_harvestcodeprovider.ts deleted file mode 100644 index c6ec14f2..00000000 --- a/deploy/006_deploy_harvestcodeprovider.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { Registry } from "../typechain"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.HARVEST_CODE_PROVIDER); - const chainId = await getChainId(); - const networkName = network.name; - const { getAddress } = ethers.utils; - - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - let feeData = await ethers.provider.getFeeData(); - const result = await deploy("HarvestCodeProvider", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - const registryV2Instance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); - const operatorAddress = await registryV2Instance.operator(); - const operatorSigner = await ethers.getSigner(operatorAddress); - - const oldHarvestCodeProvider = await registryV2Instance.getHarvestCodeProvider(); - const harvestCodeProvider = await deployments.get("HarvestCodeProvider"); - - console.log("==HarvestCodeProvider registration=="); - console.log("\n"); - if (getAddress(oldHarvestCodeProvider) !== getAddress(harvestCodeProvider.address)) { - console.log("operator registering HarvestCodeProvider.."); - feeData = await ethers.provider.getFeeData(); - const setHarvestCodeProviderTx = await registryV2Instance - .connect(operatorSigner) - .setHarvestCodeProvider(harvestCodeProvider.address, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await setHarvestCodeProviderTx.wait(1); - } else { - console.log("HarvestCodeProvider already registered"); - console.log("\n"); - } - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const harvestCodeProvider = await deployments.get("HarvestCodeProvider"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "HarvestCodeProvider", - address: harvestCodeProvider.address, - constructorArguments: [registryProxyAddress], - contract: - "contracts/protocol/adapters/ethereum/team-defi-adapters/contracts/1_ethereum/HarvestCodeProvider.sol:HarvestCodeProvider", - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "HarvestCodeProvider", - address: harvestCodeProvider.address, - constructorArguments: [registryProxyAddress], - contract: - "contracts/protocol/adapters/ethereum/team-defi-adapters/contracts/1_ethereum/HarvestCodeProvider.sol:HarvestCodeProvider", - }); - } - } - } -}; -export default func; -func.tags = ["HarvestCodeProvider"]; -func.dependencies = ["Registry"]; diff --git a/deploy/009_deploy_vaulthelper.ts b/deploy/009_deploy_vaulthelper.ts deleted file mode 100644 index e145130a..00000000 --- a/deploy/009_deploy_vaulthelper.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("VaultHelper"); - const chainId = await getChainId(); - const networkName = network.name; - - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("VaultHelper", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const vaultHelper = await deployments.get("VaultHelper"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "VaultHelper", - address: vaultHelper.address, - constructorArguments: [], - contract: "VaultHelper", - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "VaultHelper", - address: vaultHelper.address, - constructorArguments: [], - contract: "VaultHelper", - }); - } - } - } -}; -export default func; -func.tags = ["VaultHelper"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/001_deploy_curveswappooladapter.ts b/deploy_mainnet/001_deploy_curveswappooladapter.ts deleted file mode 100644 index 75b3120e..00000000 --- a/deploy_mainnet/001_deploy_curveswappooladapter.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const artifact = await deployments.getArtifact("CurveSwapPoolAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const deployer = (await ethers.getSigners())[0].address; - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CurveSwapPoolAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const curveSwapPoolAdapter = await deployments.get("CurveSwapPoolAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "CurveSwapPoolAdapter", - address: curveSwapPoolAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "CurveSwapPoolAdapter", - address: curveSwapPoolAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["CurveSwapPoolAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/008_deploy_optyfi_oracle.ts b/deploy_mainnet/001_deploy_optyfi_oracle.ts similarity index 100% rename from deploy_mainnet/008_deploy_optyfi_oracle.ts rename to deploy_mainnet/001_deploy_optyfi_oracle.ts diff --git a/deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts b/deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts deleted file mode 100644 index caa6fc14..00000000 --- a/deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("CurveMetapoolSwapAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CurveMetapoolSwapAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const curveMetaPoolSwapAdapter = await deployments.get("CurveMetapoolSwapAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "CurveMetapoolSwapAdapter", - address: curveMetaPoolSwapAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "CurveMetaPoolSwapAdapter", - address: curveMetaPoolSwapAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["CurveMetapoolSwapAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/005_deploy_compoundadapter.ts b/deploy_mainnet/002_deploy_vaulthelpermainnet.ts similarity index 61% rename from deploy_mainnet/005_deploy_compoundadapter.ts rename to deploy_mainnet/002_deploy_vaulthelpermainnet.ts index 39f1c560..c330dcdc 100644 --- a/deploy_mainnet/005_deploy_compoundadapter.ts +++ b/deploy_mainnet/002_deploy_vaulthelpermainnet.ts @@ -1,35 +1,37 @@ import { BigNumber } from "ethers"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { waitforme } from "../helpers/utils"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; +const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; + const func: DeployFunction = async ({ deployments, getNamedAccounts, getChainId, + ethers, network, tenderly, run, - ethers, }: HardhatRuntimeEnvironment) => { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("CompoundAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - + const artifact = await deployments.getArtifact("VaultHelperMainnet"); const chainId = await getChainId(); const networkName = network.name; + const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CompoundAdapter", { + const result = await deploy("VaultHelperMainnet", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress], + args: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -38,25 +40,27 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const compoundAdapter = await deployments.get("CompoundAdapter"); + const vaultHelperMainnet = await deployments.get("VaultHelperMainnet"); if (networkName === "tenderly") { await tenderly.verify({ - name: "CompoundAdapter", - address: compoundAdapter.address, - constructorArguments: [registryProxyAddress], + name: "VaultHelperMainnet", + address: vaultHelperMainnet.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], + contract: "VaultHelperMainnet", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "CompoundAdapter", - address: compoundAdapter.address, - constructorArguments: [registryProxyAddress], + name: "VaultHelperMainnet", + address: vaultHelperMainnet.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], + contract: "VaultHelperMainnet", }); } } } }; export default func; -func.tags = ["CompoundAdapter"]; +func.tags = ["VaultHelperMainnet"]; func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/003_deploy_aavev1adapter.ts b/deploy_mainnet/003_deploy_aavev1adapter.ts deleted file mode 100644 index d8340e65..00000000 --- a/deploy_mainnet/003_deploy_aavev1adapter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("AaveV1Adapter"); - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("AaveV1Adapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const aavev1Adapter = await deployments.get("AaveV1Adapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "AaveV1Adapter", - address: aavev1Adapter.address, - constructorArguments: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "AaveV1Adapter", - address: aavev1Adapter.address, - constructorArguments: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], - }); - } - } - } -}; -export default func; -func.tags = ["AaveV1Adapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/015_deploy_opWETH-Save.ts b/deploy_mainnet/003_deploy_opWETH-Save.ts similarity index 100% rename from deploy_mainnet/015_deploy_opWETH-Save.ts rename to deploy_mainnet/003_deploy_opWETH-Save.ts diff --git a/deploy_mainnet/004_deploy_aavev2adapter.ts b/deploy_mainnet/004_deploy_aavev2adapter.ts deleted file mode 100644 index f26cb3b7..00000000 --- a/deploy_mainnet/004_deploy_aavev2adapter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("AaveV2Adapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("AaveV2Adapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const aavev2Adapter = await deployments.get("AaveV2Adapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "AaveV2Adapter", - address: aavev2Adapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "AaveV2Adapter", - address: aavev2Adapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["AaveV2Adapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/016_deploy_opWETH-Earn.ts b/deploy_mainnet/004_deploy_opWETH-Earn.ts similarity index 100% rename from deploy_mainnet/016_deploy_opWETH-Earn.ts rename to deploy_mainnet/004_deploy_opWETH-Earn.ts diff --git a/deploy_mainnet/017_deploy_opNEWO-Invst.ts b/deploy_mainnet/005_deploy_opNEWO-Invst.ts similarity index 100% rename from deploy_mainnet/017_deploy_opNEWO-Invst.ts rename to deploy_mainnet/005_deploy_opNEWO-Invst.ts diff --git a/deploy_mainnet/006_deploy_convexadapter.ts b/deploy_mainnet/006_deploy_convexadapter.ts deleted file mode 100644 index 261c5319..00000000 --- a/deploy_mainnet/006_deploy_convexadapter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("ConvexFinanceAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("ConvexFinanceAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const convexFinanceAdapter = await deployments.get("ConvexFinanceAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "ConvexFinanceAdapter", - address: convexFinanceAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "ConvexFinanceAdapter", - address: convexFinanceAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["ConvexFinanceAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/018_deploy_opAAVE-Invst.ts b/deploy_mainnet/006_deploy_opAAVE-Invst.ts similarity index 100% rename from deploy_mainnet/018_deploy_opAAVE-Invst.ts rename to deploy_mainnet/006_deploy_opAAVE-Invst.ts diff --git a/deploy_mainnet/007_deploy_newostakingadapter.ts b/deploy_mainnet/007_deploy_newostakingadapter.ts deleted file mode 100644 index 6580c455..00000000 --- a/deploy_mainnet/007_deploy_newostakingadapter.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("NewoStakingAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("NewoStakingAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const newoStakingAdapter = await deployments.get("NewoStakingAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "NewoStakingAdapter", - address: newoStakingAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "NewoStakingAdapter", - address: newoStakingAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["NewoStakingAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/019_deploy_opAPE-Invst.ts b/deploy_mainnet/007_deploy_opAPE-Invst.ts similarity index 100% rename from deploy_mainnet/019_deploy_opAPE-Invst.ts rename to deploy_mainnet/007_deploy_opAPE-Invst.ts diff --git a/deploy_mainnet/020_deploy_opMANA-Invst.ts b/deploy_mainnet/008_deploy_opMANA-Invst.ts similarity index 100% rename from deploy_mainnet/020_deploy_opMANA-Invst.ts rename to deploy_mainnet/008_deploy_opMANA-Invst.ts diff --git a/deploy_mainnet/021_deploy_opSUSHI-Invst.ts b/deploy_mainnet/009_deploy_opSUSHI-Invst.ts similarity index 100% rename from deploy_mainnet/021_deploy_opSUSHI-Invst.ts rename to deploy_mainnet/009_deploy_opSUSHI-Invst.ts diff --git a/deploy_mainnet/009_deploy_sushiswappooladapterethereum.ts b/deploy_mainnet/009_deploy_sushiswappooladapterethereum.ts deleted file mode 100644 index 834864ea..00000000 --- a/deploy_mainnet/009_deploy_sushiswappooladapterethereum.ts +++ /dev/null @@ -1,158 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import ethereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import sushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; -import { getAddress } from "ethers/lib/utils"; -import { waitforme } from "../helpers/utils"; -import { Registry, Registry__factory, UniswapV2PoolAdapter, UniswapV2PoolAdapter__factory } from "../typechain"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("UniswapV2PoolAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const optyfiOracleAddress = await (await deployments.get("OptyFiOracle")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiswapPoolAdapterEthereum", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [ - registryProxyAddress, - optyfiOracleAddress, - sushiswap.SushiswapRouter.address, - sushiswap.SushiswapFactory.address, - sushiswap.rootKFactor, - ], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const sushiswapPoolAdapterEthereum = await deployments.get("SushiswapPoolAdapterEthereum"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "SushiswapPoolAdapterEthereum", - address: sushiswapPoolAdapterEthereum.address, - constructorArguments: [ - registryProxyAddress, - optyfiOracleAddress, - sushiswap.SushiswapRouter.address, - sushiswap.SushiswapFactory.address, - sushiswap.rootKFactor, - ], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "SushiswapPoolAdapterEthereum", - address: sushiswapPoolAdapterEthereum.address, - constructorArguments: [ - registryProxyAddress, - optyfiOracleAddress, - sushiswap.SushiswapRouter.address, - sushiswap.SushiswapFactory.address, - sushiswap.rootKFactor, - ], - }); - } - } - } - const sushiswapPoolAdapterEthereumAddress = await (await deployments.get("SushiswapPoolAdapterEthereum")).address; - const sushiswapPoolAdapterEthereumInstance = ( - await ethers.getContractAt(UniswapV2PoolAdapter__factory.abi, sushiswapPoolAdapterEthereumAddress) - ); - const registryProxyInstance = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const riskOperator = await registryProxyInstance.riskOperator(); - const riskOperatorSigner = await ethers.getSigner(riskOperator); - - const USDC_WETH_LP = "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0"; - const WBTC_WETH_LP = "0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58"; - - const liquidityPoolToTolerances = [ - { liquidityPool: USDC_WETH_LP, tolerance: "100" }, - { liquidityPool: WBTC_WETH_LP, tolerance: "50" }, - ]; - const pendingLiquidityPoolToTolerances = []; - for (const liquidityPoolToTolerance of liquidityPoolToTolerances) { - const tolerance = await sushiswapPoolAdapterEthereumInstance.liquidityPoolToTolerance( - liquidityPoolToTolerance.liquidityPool, - ); - if (!BigNumber.from(tolerance).eq(BigNumber.from(liquidityPoolToTolerance.tolerance))) { - pendingLiquidityPoolToTolerances.push(liquidityPoolToTolerance); - } - } - - if (pendingLiquidityPoolToTolerances.length > 0) { - console.log(JSON.stringify(pendingLiquidityPoolToTolerances, null, 4)); - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log("updating pending LiquidityPool To Tolerances"); - const tx = await sushiswapPoolAdapterEthereumInstance - .connect(riskOperatorSigner) - .setLiquidityPoolToTolerance(pendingLiquidityPoolToTolerances); - await tx.wait(1); - } else { - console.log("cannot update pending LiquidityPool To Tolerances because the signer is not the risk operator"); - } - } else { - console.log("liquidityPoolToTolerances are up to date"); - } - - const liquidityPoolToWantTokenToSlippages = [ - { liquidityPool: USDC_WETH_LP, wantToken: ethereumTokens.WRAPPED_TOKENS.WETH, slippage: "90" }, - { liquidityPool: USDC_WETH_LP, wantToken: ethereumTokens.PLAIN_TOKENS.USDC, slippage: "70" }, - { liquidityPool: WBTC_WETH_LP, wantToken: ethereumTokens.WRAPPED_TOKENS.WETH, slippage: "70" }, - { liquidityPool: WBTC_WETH_LP, wantToken: ethereumTokens.BTC_TOKENS.WBTC, slippage: "70" }, - ]; - const pendingLiquidityPoolToWantTokenToSlippages = []; - for (const liquidityPoolToWantTokenToSlippage of liquidityPoolToWantTokenToSlippages) { - const slippage = await sushiswapPoolAdapterEthereumInstance.liquidityPoolToWantTokenToSlippage( - liquidityPoolToWantTokenToSlippage.liquidityPool, - liquidityPoolToWantTokenToSlippage.wantToken, - ); - if (!BigNumber.from(slippage).eq(BigNumber.from(liquidityPoolToWantTokenToSlippage.slippage))) { - pendingLiquidityPoolToWantTokenToSlippages.push(liquidityPoolToWantTokenToSlippage); - } - } - - if (pendingLiquidityPoolToWantTokenToSlippages.length > 0) { - console.log(JSON.stringify(pendingLiquidityPoolToWantTokenToSlippages, null, 4)); - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log("updating pending LiquidityPool To Want Token To Slippages"); - const tx = await sushiswapPoolAdapterEthereumInstance - .connect(riskOperatorSigner) - .setLiquidityPoolToWantTokenToSlippage(pendingLiquidityPoolToWantTokenToSlippages); - await tx.wait(1); - } else { - console.log( - "cannot update pending LiquidityPool To Want Token To Slippages because the signer is not the risk operator", - ); - } - } else { - console.log("pendingLiquidityPoolToWantTokenToSlippages are up to date"); - } -}; -export default func; -func.tags = ["SushiswapPoolAdapterEthereum"]; -func.dependencies = ["Registry", "OptyFiOracle"]; diff --git a/deploy_mainnet/022_deploy_opLINK-Invst.ts b/deploy_mainnet/010_deploy_opLINK-Invst.ts similarity index 100% rename from deploy_mainnet/022_deploy_opLINK-Invst.ts rename to deploy_mainnet/010_deploy_opLINK-Invst.ts diff --git a/deploy_mainnet/010_deploy_sushiswapmasterchefv1adapter.ts b/deploy_mainnet/010_deploy_sushiswapmasterchefv1adapter.ts deleted file mode 100644 index 20739f6c..00000000 --- a/deploy_mainnet/010_deploy_sushiswapmasterchefv1adapter.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { - Registry, - Registry__factory, - SushiswapMasterChefV1Adapter, - SushiswapMasterChefV1Adapter__factory, -} from "../typechain"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("SushiswapMasterChefV1Adapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiswapMasterChefV1Adapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - const sushiswapFarmAdapterEthereum = await (await deployments.get("SushiswapMasterChefV1Adapter")).address; - - const sushiswapFarmAdapterEthereumInstance = ( - await ethers.getContractAt(SushiswapMasterChefV1Adapter__factory.abi, sushiswapFarmAdapterEthereum) - ); - const registryProxyInstance = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const operator = await registryProxyInstance.operator(); - const operatorSigner = await ethers.getSigner(operator); - - const CRV_WETH_LP = "0x58Dc5a51fE44589BEb22E8CE67720B5BC5378009"; - const YFI_WETH_LP = "0x088ee5007C98a9677165D78dD2109AE4a3D04d0C"; - - const underlyingTokenToPids = [ - { underlyingToken: CRV_WETH_LP, pid: 17 }, - { underlyingToken: YFI_WETH_LP, pid: 11 }, - ]; - - const pendingUnderlyingTokens = []; - const pendingPids = []; - - for (const underlyingTokenToPid of underlyingTokenToPids) { - const _pid = await sushiswapFarmAdapterEthereumInstance.underlyingTokenToPid(underlyingTokenToPid.underlyingToken); - if (!BigNumber.from(_pid).eq(underlyingTokenToPid.pid)) { - pendingUnderlyingTokens.push(underlyingTokenToPid.underlyingToken); - pendingPids.push(underlyingTokenToPid.pid); - } - } - - if (pendingUnderlyingTokens.length > 0) { - console.log("Pending underlying token to pids ", JSON.stringify(pendingUnderlyingTokens, null, 4)); - console.log("Pending underlying token to pids ", JSON.stringify(pendingPids, null, 4)); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - console.log("updating pending underlying token to pids"); - const tx = await sushiswapFarmAdapterEthereumInstance - .connect(operatorSigner) - .setUnderlyingTokenToPid(pendingUnderlyingTokens, pendingPids); - await tx.wait(1); - } else { - console.log("cannot update pending underlying token to pids as signer is not the operator"); - } - } else { - console.log("underlying token to pids is up to date"); - } - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - if (networkName === "tenderly") { - await tenderly.verify({ - name: "SushiswapMasterChefV1Adapter", - address: sushiswapFarmAdapterEthereum, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "SushiswapMasterChefV1Adapter", - address: sushiswapFarmAdapterEthereum, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["SushiswapMasterChefV1Adapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/023_deploy_opENS-Invst.ts b/deploy_mainnet/011_deploy_opENS-Invst.ts similarity index 100% rename from deploy_mainnet/023_deploy_opENS-Invst.ts rename to deploy_mainnet/011_deploy_opENS-Invst.ts diff --git a/deploy_mainnet/011_deploy_sushiswapmasterchefv2adapter.ts b/deploy_mainnet/011_deploy_sushiswapmasterchefv2adapter.ts deleted file mode 100644 index a8ff16e9..00000000 --- a/deploy_mainnet/011_deploy_sushiswapmasterchefv2adapter.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { BigNumber } from "ethers"; -import { getAddress } from "ethers/lib/utils"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; -import { - Registry, - Registry__factory, - SushiswapMasterChefV2AdapterEthereum, - SushiswapMasterChefV2AdapterEthereum__factory, -} from "../typechain"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("SushiswapMasterChefV2AdapterEthereum"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const optyfiOracleAddress = await (await deployments.get("OptyFiOracle")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiswapMasterChefV2AdapterEthereum", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress, optyfiOracleAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - const sushiswapFarmAdapterEthereum = await (await deployments.get("SushiswapMasterChefV2AdapterEthereum")).address; - - const sushiswapFarmAdapterEthereumInstance = ( - await ethers.getContractAt(SushiswapMasterChefV2AdapterEthereum__factory.abi, sushiswapFarmAdapterEthereum) - ); - const registryProxyInstance = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const operator = await registryProxyInstance.operator(); - const operatorSigner = await ethers.getSigner(operator); - - const CVX_WETH_LP = "0x05767d9EF41dC40689678fFca0608878fb3dE906"; - - const underlyingTokenToPids = [{ underlyingToken: CVX_WETH_LP, pid: 1 }]; - - const pendingUnderlyingTokenToPids = []; - - for (const underlyingTokenToPid of underlyingTokenToPids) { - const _pid = await sushiswapFarmAdapterEthereumInstance.underlyingTokenToPid(underlyingTokenToPid.underlyingToken); - if (!BigNumber.from(_pid).eq(underlyingTokenToPid.pid)) { - pendingUnderlyingTokenToPids.push(underlyingTokenToPid); - } - } - - if (pendingUnderlyingTokenToPids.length > 0) { - console.log("Pending underlying tokens to pids ", JSON.stringify(pendingUnderlyingTokenToPids, null, 4)); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - console.log("updating underlying tokens to pids"); - const tx = await sushiswapFarmAdapterEthereumInstance - .connect(operatorSigner) - .setUnderlyingTokenToPid(pendingUnderlyingTokenToPids); - await tx.wait(1); - } else { - console.log("cannot update underlying tokens to pids as signer is not the operator"); - } - } else { - console.log("underlying token to pids is up to date"); - } - - const currentOptyFiOracle = await sushiswapFarmAdapterEthereumInstance.optyFiOracle(); - const newOptyFiOracle = (await deployments.get("OptyFiOracle")).address; - - if (getAddress(currentOptyFiOracle) !== getAddress(newOptyFiOracle)) { - console.log("Modifying OptyFiOracle ", newOptyFiOracle); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - console.log("updating OptyFiOracle"); - const tx = await sushiswapFarmAdapterEthereumInstance.connect(operatorSigner).setOptyFiOracle(newOptyFiOracle); - await tx.wait(1); - } else { - console.log("cannot update OptyFiOracle as signer is not the operator"); - } - } else { - console.log("OptyFiOracle is up to date"); - } - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - if (networkName === "tenderly") { - await tenderly.verify({ - name: "SushiswapMasterChefV2AdapterEthereum", - address: sushiswapFarmAdapterEthereum, - constructorArguments: [registryProxyAddress, optyfiOracleAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "SushiswapMasterChefV2AdapterEthereum", - address: sushiswapFarmAdapterEthereum, - constructorArguments: [registryProxyAddress, optyfiOracleAddress], - }); - } - } - } -}; -export default func; -func.tags = ["SushiswapMasterChefV2AdapterEthereum"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/024_deploy_opCOMP-Invst.ts b/deploy_mainnet/012_deploy_opCOMP-Invst.ts similarity index 100% rename from deploy_mainnet/024_deploy_opCOMP-Invst.ts rename to deploy_mainnet/012_deploy_opCOMP-Invst.ts diff --git a/deploy_mainnet/012_deploy_sushi-bar-adapter.ts b/deploy_mainnet/012_deploy_sushi-bar-adapter.ts deleted file mode 100644 index 817bae59..00000000 --- a/deploy_mainnet/012_deploy_sushi-bar-adapter.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("SushiBarAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiBarAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - const sushiBarAdapter = await deployments.get("SushiBarAdapter"); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - if (networkName === "tenderly") { - await tenderly.verify({ - name: "SushiBarAdapter", - address: sushiBarAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "SushiBarAdapter", - address: sushiBarAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["SushiBarAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/013_deploy_curvecryptopooladapter.ts b/deploy_mainnet/013_deploy_curvecryptopooladapter.ts deleted file mode 100644 index 412c30a1..00000000 --- a/deploy_mainnet/013_deploy_curvecryptopooladapter.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { default as CurveExports } from "@optyfi/defi-legos/ethereum/curve/contracts"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const artifact = await deployments.getArtifact("CurveCryptoPoolAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const deployer = (await ethers.getSigners())[0].address; - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CurveCryptoPoolAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress, CurveExports.CurveFactory.address, CurveExports.CurveMetaRegistry.address], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const curveCryptoPoolAdapter = await deployments.get("CurveCryptoPoolAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "CurveCryptoPoolAdapter", - address: curveCryptoPoolAdapter.address, - constructorArguments: [ - registryProxyAddress, - CurveExports.CurveFactory.address, - CurveExports.CurveMetaRegistry.address, - ], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "CurveCryptoPoolAdapter", - address: curveCryptoPoolAdapter.address, - constructorArguments: [ - registryProxyAddress, - CurveExports.CurveFactory.address, - CurveExports.CurveMetaRegistry.address, - ], - }); - } - } - } -}; -export default func; -func.tags = ["CurveCryptoPoolAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/025_deploy_opIMX-Invst.ts b/deploy_mainnet/013_deploy_opIMX-Invst.ts similarity index 100% rename from deploy_mainnet/025_deploy_opIMX-Invst.ts rename to deploy_mainnet/013_deploy_opIMX-Invst.ts diff --git a/deploy_mainnet/014_deploy_metapoolgaugeadapter.ts b/deploy_mainnet/014_deploy_metapoolgaugeadapter.ts deleted file mode 100644 index a6ca456f..00000000 --- a/deploy_mainnet/014_deploy_metapoolgaugeadapter.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getChainId, - ethers, - network, - tenderly, - run, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const artifact = await deployments.getArtifact("CurveMetapoolGaugeAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const deployer = (await ethers.getSigners())[0].address; - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CurveMetapoolGaugeAdapter", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const curveMetapoolGaugeAdapter = await deployments.get("CurveMetapoolGaugeAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "CurveMetapoolGaugeAdapter", - address: curveMetapoolGaugeAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "CurveMetapoolGaugeAdapter", - address: curveMetapoolGaugeAdapter.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["CurveMetapoolGaugeAdapter"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/026_deploy_opALCX-Invst.ts b/deploy_mainnet/014_deploy_opALCX-Invst.ts similarity index 100% rename from deploy_mainnet/026_deploy_opALCX-Invst.ts rename to deploy_mainnet/014_deploy_opALCX-Invst.ts diff --git a/deploy_mainnet/027_deploy_opCRV-Invst.ts b/deploy_mainnet/015_deploy_opCRV-Invst.ts similarity index 100% rename from deploy_mainnet/027_deploy_opCRV-Invst.ts rename to deploy_mainnet/015_deploy_opCRV-Invst.ts diff --git a/deploy_mainnet/028_deploy_opCVX-Invst.ts b/deploy_mainnet/016_deploy_opCVX-Invst.ts similarity index 100% rename from deploy_mainnet/028_deploy_opCVX-Invst.ts rename to deploy_mainnet/016_deploy_opCVX-Invst.ts diff --git a/deploy_mainnet/029_deploy_opYFI-Invst.ts b/deploy_mainnet/017_deploy_opYFI-Invst.ts similarity index 100% rename from deploy_mainnet/029_deploy_opYFI-Invst.ts rename to deploy_mainnet/017_deploy_opYFI-Invst.ts diff --git a/deploy_mainnet/030_deploy_opDAI-Save.ts b/deploy_mainnet/018_deploy_opDAI-Save.ts similarity index 100% rename from deploy_mainnet/030_deploy_opDAI-Save.ts rename to deploy_mainnet/018_deploy_opDAI-Save.ts diff --git a/deploy_mainnet/031_deploy_opUSDT-Save.ts b/deploy_mainnet/019_deploy_opUSDT-Save.ts similarity index 100% rename from deploy_mainnet/031_deploy_opUSDT-Save.ts rename to deploy_mainnet/019_deploy_opUSDT-Save.ts diff --git a/deploy_mainnet/032_deploy_opWBTC-Save.ts b/deploy_mainnet/020_deploy_opWBTC-Save.ts similarity index 100% rename from deploy_mainnet/032_deploy_opWBTC-Save.ts rename to deploy_mainnet/020_deploy_opWBTC-Save.ts diff --git a/deploy_mainnet/033_deploy_opUSD3-Earn.ts b/deploy_mainnet/021_deploy_opUSD3-Earn.ts similarity index 100% rename from deploy_mainnet/033_deploy_opUSD3-Earn.ts rename to deploy_mainnet/021_deploy_opUSD3-Earn.ts diff --git a/deploy_mainnet/034_deploy_opWBTC-Earn.ts b/deploy_mainnet/022_deploy_opWBTC-Earn.ts similarity index 100% rename from deploy_mainnet/034_deploy_opWBTC-Earn.ts rename to deploy_mainnet/022_deploy_opWBTC-Earn.ts diff --git a/deploy_mainnet/035_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts similarity index 100% rename from deploy_mainnet/035_deploy_opUSDC-Earn.ts rename to deploy_mainnet/023_deploy_opUSDC-Earn.ts diff --git a/deploy_mainnet/036_deploy_opUSDC-Save.ts b/deploy_mainnet/024_deploy_opUSDC-Save.ts similarity index 100% rename from deploy_mainnet/036_deploy_opUSDC-Save.ts rename to deploy_mainnet/024_deploy_opUSDC-Save.ts diff --git a/deploy_mainnet/037_deploy_opUSDC-Invst.ts b/deploy_mainnet/025_deploy_opUSDC-Invst.ts similarity index 100% rename from deploy_mainnet/037_deploy_opUSDC-Invst.ts rename to deploy_mainnet/025_deploy_opUSDC-Invst.ts diff --git a/deploy_mainnet/038_deploy_opWETH-Invst.ts b/deploy_mainnet/026_deploy_opWETH-Invst.ts similarity index 100% rename from deploy_mainnet/038_deploy_opWETH-Invst.ts rename to deploy_mainnet/026_deploy_opWETH-Invst.ts diff --git a/deploy_mainnet/040_set_price_oracle_feed.ts b/deploy_mainnet/027_set_price_oracle_feed.ts similarity index 100% rename from deploy_mainnet/040_set_price_oracle_feed.ts rename to deploy_mainnet/027_set_price_oracle_feed.ts diff --git a/deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts b/deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts deleted file mode 100644 index e3bfacfb..00000000 --- a/deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { BigNumber } from "ethers"; -import { getNamedAccounts } from "hardhat"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; - -const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvironment) => { - const { deployer } = await getNamedAccounts(); - const { getAddress } = ethers.utils; - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const registryV2Instance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); - const curveSwapPoolAdapter = await deployments.get("CurveSwapPoolAdapter"); - const curveMetaPoolSwapAdapter = await deployments.get("CurveMetapoolSwapAdapter"); - const curveCryptoPoolAdapter = await deployments.get("CurveCryptoPoolAdapter"); - const curveMetapoolGaugeAdapter = await deployments.get("CurveMetapoolGaugeAdapter"); - const sushiswapPoolAdapterEthereum = await deployments.get("SushiswapPoolAdapterEthereum"); - const newoStakingAdapter = await deployments.get("NewoStakingAdapter"); - const aaveV1Adapter = await deployments.get("AaveV1Adapter"); - const aaveV2Adapter = await deployments.get("AaveV2Adapter"); - const compoundAdapter = await deployments.get("CompoundAdapter"); - const convexFinanceAdapter = await deployments.get("ConvexFinanceAdapter"); - const sushiswapMasterChefV1Adapter = await deployments.get("SushiswapMasterChefV1Adapter"); - const sushiBarAdapter = await deployments.get("SushiBarAdapter"); - const sushiswapMasterChefV2AdapterEthereum = await deployments.get("SushiswapMasterChefV2AdapterEthereum"); - const operatorAddress = await registryV2Instance.getOperator(); - const operatorSigner = await ethers.getSigner(operatorAddress); - const riskOperatorAddress = await registryV2Instance.getRiskOperator(); - const riskOperatorSigner = await ethers.getSigner(riskOperatorAddress); - - // approve liquidity pools and map to adapter - const poolsWithRatings: { [key: string]: { rate: number; adapter: string } } = { - "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7": { rate: 80, adapter: curveSwapPoolAdapter.address }, // curve Pool for DAI/USDC/USDT - "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022": { rate: 80, adapter: curveSwapPoolAdapter.address }, // curve ETH/stETH StableSwap - "0x5a6A4D54456819380173272A5E8E9B9904BdF41B": { rate: 80, adapter: curveMetaPoolSwapAdapter.address }, // curve MIM Metapool - "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B": { rate: 80, adapter: curveMetaPoolSwapAdapter.address }, // curve swap pool for FRAX3CRV - "0x24a42fD28C976A61Df5D00D0599C34c4f90748c8": { rate: 90, adapter: aaveV1Adapter.address }, // aave v1 lending pool address provider - "0x52D306e36E3B6B02c153d0266ff0f85d18BCD413": { rate: 90, adapter: aaveV2Adapter.address }, // aave v2 lending pool address provider - "0x39AA39c021dfbaE8faC545936693aC917d5E7563": { rate: 90, adapter: compoundAdapter.address }, // compound usdc pool - "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5": { rate: 90, adapter: compoundAdapter.address }, // compound eth pool - "0xabB54222c2b77158CC975a2b715a3d703c256F05": { rate: 80, adapter: convexFinanceAdapter.address }, // convex pool cvxMIM-3LP3CRV-f - "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA": { rate: 80, adapter: convexFinanceAdapter.address }, // convex pool for cvxFRAX3CRV-f - "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0": { rate: 80, adapter: convexFinanceAdapter.address }, // convex pool for cvxsteCR:{rate:80, adapter:} - "0x87650D7bbfC3A9F10587d7778206671719d9910D": { rate: 80, adapter: curveSwapPoolAdapter.address }, // ousd+3Crv curve pool - "0x4f062658EaAF2C1ccf8C8e36D6824CDf41167956": { rate: 80, adapter: curveSwapPoolAdapter.address }, // gusd3CRV curve pool - "0x3eF6A01A0f81D6046290f3e2A8c5b843e738E604": { rate: 80, adapter: curveSwapPoolAdapter.address }, // husd3CRV curve pool - "0x3E01dD8a5E1fb3481F0F589056b428Fc308AF0Fb": { rate: 80, adapter: curveSwapPoolAdapter.address }, // usdk3CRV curve pool - "0x0f9cb53Ebe405d49A0bbdBD291A65Ff571bC83e1": { rate: 80, adapter: curveSwapPoolAdapter.address }, // usdn3CRV curve pool - "0xE7a24EF0C5e95Ffb0f6684b813A78F2a3AD7D171": { rate: 80, adapter: curveSwapPoolAdapter.address }, // LinkUSD3CRV curve pool - "0x8474DdbE98F5aA3179B3B3F5942D724aFcdec9f6": { rate: 80, adapter: curveSwapPoolAdapter.address }, // musd3CRV curve pool - "0xC18cC39da8b11dA8c3541C598eE022258F9744da": { rate: 80, adapter: curveSwapPoolAdapter.address }, // rsv3CRV curve pool - "0x8038C01A0390a8c547446a0b2c18fc9aEFEcc10c": { rate: 80, adapter: curveSwapPoolAdapter.address }, // dusd3CRV curve pool - "0x890f4e345B1dAED0367A877a1612f86A1f86985f": { rate: 80, adapter: curveSwapPoolAdapter.address }, // ust+3CRV curve pool - "0x42d7025938bEc20B69cBae5A77421082407f053A": { rate: 80, adapter: curveSwapPoolAdapter.address }, // usdp+3crv curve pool - "0x3689f325E88c2363274E5F3d44b6DaB8f9e1f524": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxusdc3CRV - "0x67c4f788FEB82FAb27E3007daa3d7b90959D5b89": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxust3CRV - "0xBC9016C379fb218B95Fe3730D5F49F3149E86CAB": { rate: 50, adapter: newoStakingAdapter.address }, // stkNEWO - "0xdb36b23964FAB32dCa717c99D6AEFC9FB5748f3a": { rate: 50, adapter: newoStakingAdapter.address }, // newoSushiNEWO-USDC - "0xc08ED9a9ABEAbcC53875787573DC32Eee5E43513": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-NEWO-USDC - "0xD75EA151a61d06868E31F8988D28DFE5E9df57B4": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-AAVE-WETH - "0xc2EdaD668740f1aA35E4D8f227fB8E17dcA888Cd": { rate: 90, adapter: sushiswapMasterChefV1Adapter.address }, // Sushiswap's MasterChef - "0xe65cdB6479BaC1e22340E4E755fAE7E509EcD06c": { rate: 90, adapter: compoundAdapter.address }, // compound AAVE pool - "0xB27C7b131Cf4915BeC6c4Bc1ce2F33f9EE434b9f": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-APE-USDT - "0xEF0881eC094552b2e128Cf945EF17a6752B4Ec5d": { rate: 90, adapter: sushiswapMasterChefV2AdapterEthereum.address }, // Sushiswap's MasterChef V2 - "0x795065dCc9f64b5614C407a6EFDC400DA6221FB0": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-SUSHI-WETH - "0x4B0181102A0112A2ef11AbEE5563bb4a3176c9d7": { rate: 90, adapter: compoundAdapter.address }, // compound SUSHI pool - "0x1bEC4db6c3Bc499F3DbF289F5499C30d541FEc97": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-MANA-WETH - "0x8798249c2E607446EfB7Ad49eC89dD1865Ff4272": { rate: 90, adapter: sushiBarAdapter.address }, // xSUSHI - "0x32512Bee3848bfcBb7bEAf647aa697a100f3b706": { rate: 80, adapter: convexFinanceAdapter.address }, //cvxcDAI+cUSDC - "0xA1c3492b71938E144ad8bE4c2fB6810b01A43dD8": { rate: 80, adapter: convexFinanceAdapter.address }, //cvxcDAI+cUSDC+cUSDT - "0xC40D16476380e4037e6b1A2594cAF6a6cc8Da967": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // LINK-WETH-SLP - "0xFAce851a4921ce59e912d19329929CE6da6EB0c7": { rate: 90, adapter: compoundAdapter.address }, // compound LINK pool - "0xF178C0b5Bb7e7aBF4e12A4838C7b7c5bA2C623c0": { rate: 80, adapter: curveSwapPoolAdapter.address }, // linkCRV pool - "0xD37969740d78C94C648d74671B8BE31eF43c30aB": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxusdc3CRV - "0xa1181481bEb2dc5De0DaF2c85392d81C704BF75D": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // ENS-WETH-SLP - "0x31503dcb60119A812feE820bb7042752019F2355": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // COMP-WETH-SLP - "0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4": { rate: 90, adapter: compoundAdapter.address }, // compound COMP pool - "0x18Cd890F4e23422DC4aa8C2D6E0Bd3F3bD8873d8": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // IMX-WETH-SLP - "0xC558F600B34A5f69dD2f0D06Cb8A88d829B7420a": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // LDO-WETH-SLP - "0x99B42F2B49C395D2a77D973f6009aBb5d67dA343": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // YGG-WETH-SLP - "0xC3f279090a47e80990Fe3a9c30d24Cb117EF91a8": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // ALCX-WETH-SLP - "0x58Dc5a51fE44589BEb22E8CE67720B5BC5378009": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // CRV-WETH-SLP - "0x05767d9EF41dC40689678fFca0608878fb3dE906": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // CVX-WETH-SLP - "0x088ee5007C98a9677165D78dD2109AE4a3D04d0C": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // YFI-WETH-SLP - "0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946": { rate: 90, adapter: compoundAdapter.address }, // compound YFI pool - "0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C": { rate: 80, adapter: convexFinanceAdapter.address }, // cvx3Crv - "0xc5424B857f758E906013F3555Dad202e4bdB4567": { rate: 80, adapter: curveSwapPoolAdapter.address }, // eCRV - "0xAF1d4C576bF55f6aE493AEebAcC3a227675e5B98": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxeCRV - "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643": { rate: 90, adapter: compoundAdapter.address }, // compound DAI pool - "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9": { rate: 90, adapter: compoundAdapter.address }, // compound USDT pool - "0xC11b1268C1A384e55C48c2391d8d480264A3A7F4": { rate: 90, adapter: compoundAdapter.address }, // compound WBTC pool - "0x7fC77b5c7614E1533320Ea6DDc2Eb61fa00A9714": { rate: 80, adapter: curveSwapPoolAdapter.address }, // crvRenWSBTC pool - "0x93054188d876f558f4a66B2EF1d97d16eDf0895B": { rate: 80, adapter: curveSwapPoolAdapter.address }, // crvRenWSBTC pool - "0xbA723E335eC2939D52a2efcA2a8199cb4CB93cC3": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxcrvRenWSBTC - "0x74b79021Ea6De3f0D1731fb8BdfF6eE7DF10b8Ae": { rate: 80, adapter: convexFinanceAdapter.address }, // cvxcrvRenWBTC - "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-USDC-WETH - "0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58": { rate: 50, adapter: sushiswapPoolAdapterEthereum.address }, // SUSHI-WBTC-WETH - "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dUSDC - "0x118823514681353634FF95837939E783D85B18AF": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dWETH - "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dWBTC - "0x5714EB15A226059202CdfA1bF304167e36752862": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dCUSDC - "0x3Be69a1D7B8821cDcCE90509aBB62D250A5AeFcc": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dCWETH - "0xEd279fDD11cA84bEef15AF5D39BB4d4bEE23F0cA": { rate: 80, adapter: curveMetaPoolSwapAdapter.address }, // curve swap pool LUSD3CRV-f - "0x74ED5d42203806c8CDCf2F04Ca5F60DC777b901c": { rate: 80, adapter: curveCryptoPoolAdapter.address }, // curve crypto pool bLUSDLUSD3-f - "0xdA0DD1798BE66E17d5aB1Dc476302b56689C2DB4": { rate: 80, adapter: curveMetapoolGaugeAdapter.address }, // curve gauge for bLUSDLUSD3-f - "0x9Dd451aB7bB62DA57b638070760A747bB6b1c5b1": { rate: 90, adapter: compoundAdapter.address }, //dAMM finance - dAWBTC - "0xf1667d8FA2a9d4a00717CB4F74CC698546ACF602": { rate: 90, adapter: compoundAdapter.address }, // dAMM finance - dFRAX - "0xBf0852A95eC76e87f7431Fa505B27937C9836372": { rate: 90, adapter: compoundAdapter.address }, //dAMM finance - dUSDT - }; - - const onlyMapPoolsToAdapters = []; - const approveLiquidityPoolAndMap = []; - const ratePools: [string, number][] = []; - - for (const pool of Object.keys(poolsWithRatings)) { - const { rating, isLiquidityPool } = await registryV2Instance.getLiquidityPool(pool); - const adapter = await registryV2Instance.liquidityPoolToAdapter(pool); - if (!isLiquidityPool && getAddress(adapter) != getAddress(poolsWithRatings[pool].adapter)) { - approveLiquidityPoolAndMap.push([pool, poolsWithRatings[pool].adapter]); - } - if (isLiquidityPool && getAddress(adapter) != getAddress(poolsWithRatings[pool].adapter)) { - onlyMapPoolsToAdapters.push([pool, poolsWithRatings[pool].adapter]); - } - if (rating != poolsWithRatings[pool].rate) { - ratePools.push([pool, poolsWithRatings[pool].rate]); - } - } - - console.log("==Approve liquidity pool and map to adapter=="); - if (approveLiquidityPoolAndMap.length > 0) { - console.log(`approve ${approveLiquidityPoolAndMap.length} pools And Map `, approveLiquidityPoolAndMap); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - // approve liquidity pool and map adapter - console.log(`operator approving and mapping ${approveLiquidityPoolAndMap.length} pools ...`); - const feeData = await ethers.provider.getFeeData(); - const approveLiquidityPoolAndMapAdapterTx = await registryV2Instance - .connect(operatorSigner) - ["approveLiquidityPoolAndMapToAdapter((address,address)[])"](approveLiquidityPoolAndMap, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await approveLiquidityPoolAndMapAdapterTx.wait(); - } else { - console.log("cannot approve and map pools as signer is not the operator"); - } - } else { - console.log("Already approved liquidity pool and map to adapter"); - } - - console.log("==Only map liquidity pool to adapter=="); - if (onlyMapPoolsToAdapters.length > 0) { - console.log(`map ${onlyMapPoolsToAdapters.length} pools `, onlyMapPoolsToAdapters); - // only map pool to adapter - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - console.log(`operator only mapping ${onlyMapPoolsToAdapters.length} pools ...`); - const feeData = await ethers.provider.getFeeData(); - const mapToAdapterTx = await registryV2Instance - .connect(operatorSigner) - ["setLiquidityPoolToAdapter((address,address)[])"](onlyMapPoolsToAdapters, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await mapToAdapterTx.wait(); - } else { - console.log("cannot map pools as signer is not the operator"); - } - } else { - console.log("Already mapped to adapter"); - } - - console.log("==Only rate liquidity pool=="); - if (ratePools.length > 0) { - console.log(`${ratePools.length} pools to rate ...`, ratePools); - // rate pools - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log(`risk operator rating ${ratePools.length} pools ...`, ratePools); - const feeData = await ethers.provider.getFeeData(); - const rateAdapterTx = await registryV2Instance - .connect(riskOperatorSigner) - ["rateLiquidityPool((address,uint8)[])"](ratePools, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await rateAdapterTx.wait(); - } else { - console.log("cannot rate pools as signer is not the risk operator"); - } - } else { - console.log("Already rate liquidity pool"); - } -}; -export default func; -func.tags = ["ApproveAndMapLiquidityPoolToAdapter"]; -func.dependencies = [ - "Registry", - "CurveSwapPoolAdapter", - "CurveMetapoolSwapAdapter", - "SushiswapMasterChefV1Adapter", - "SushiswapMasterChefV2AdapterEthereum", - "SushiswapPoolAdapterEthereum", - "NewoStakingAdapter", - "AaveV1Adapter", - "AaveV2Adapter", - "CompoundAdapter", - "ConvexFinanceAdapter", - "CurveCryptoPoolAdapter", - "CurveMetapoolGaugeAdapter", -]; diff --git a/deploy_mainnet/041_deploy_curveexchangeadapter.ts b/deploy_mainnet/041_deploy_curveexchangeadapter.ts deleted file mode 100644 index a734bccf..00000000 --- a/deploy_mainnet/041_deploy_curveexchangeadapter.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import CurveExports from "@optyfi/defi-legos/ethereum/curve/index"; -import { waitforme } from "../helpers/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("CurveExchangeAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CurveExchangeAdapterEthereum", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [ - registryProxyAddress, - CurveExports.CurveRegistryExchange.address, - CurveExports.CurveMetaRegistry.address, - EthereumTokens.WRAPPED_TOKENS.WETH, - EthereumTokens.PLAIN_TOKENS.ETH, - CurveExports.CurveSwapPool["seth_eth+seth"].pool, - CurveExports.CurveSwapPool["aethc_eth+aethc"].pool, - CurveExports.CurveSwapPool["reth_eth+reth"].pool, - CurveExports.CurveSwapPool["steth_eth+steth"].pool, - ], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const curveExchangeAdapter = await deployments.get("CurveExchangeAdapter"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "CurveExchangeAdapter", - address: curveExchangeAdapter.address, - constructorArguments: [ - registryProxyAddress, - CurveExports.CurveRegistryExchange.address, - CurveExports.CurveMetaRegistry.address, - EthereumTokens.WRAPPED_TOKENS.WETH, - EthereumTokens.PLAIN_TOKENS.ETH, - CurveExports.CurveSwapPool["seth_eth+seth"].pool, - CurveExports.CurveSwapPool["aethc_eth+aethc"].pool, - CurveExports.CurveSwapPool["reth_eth+reth"].pool, - CurveExports.CurveSwapPool["steth_eth+steth"].pool, - ], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "CurveExchangeAdapter", - address: curveExchangeAdapter.address, - constructorArguments: [ - registryProxyAddress, - CurveExports.CurveRegistryExchange.address, - CurveExports.CurveMetaRegistry.address, - EthereumTokens.WRAPPED_TOKENS.WETH, - EthereumTokens.PLAIN_TOKENS.ETH, - CurveExports.CurveSwapPool["seth_eth+seth"].pool, - CurveExports.CurveSwapPool["aethc_eth+aethc"].pool, - CurveExports.CurveSwapPool["reth_eth+reth"].pool, - CurveExports.CurveSwapPool["steth_eth+steth"].pool, - ], - }); - } - } - } -}; -export default func; -func.tags = ["CurveExchangeAdapterEthereum"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/042_deploy_uniswapv2exchangeadapterethereum.ts b/deploy_mainnet/042_deploy_uniswapv2exchangeadapterethereum.ts deleted file mode 100644 index 02336aa0..00000000 --- a/deploy_mainnet/042_deploy_uniswapv2exchangeadapterethereum.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import EthereumUniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; -import { waitforme } from "../helpers/utils"; -import { Registry, Registry__factory, UniswapV2ExchangeAdapter, UniswapV2ExchangeAdapter__factory } from "../typechain"; -import { getAddress } from "ethers/lib/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("UniswapV2ExchangeAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const optyfiOracleAddress = await (await deployments.get("OptyFiOracle")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("UniswapV2ExchangeAdapterEthereum", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress, EthereumUniswapV2.router02.address, optyfiOracleAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const uniswapV2ExchangeAdapterEthereum = await deployments.get("UniswapV2ExchangeAdapterEthereum"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "UniswapV2ExchangeAdapterEthereum", - address: uniswapV2ExchangeAdapterEthereum.address, - constructorArguments: [registryProxyAddress, EthereumUniswapV2.router02.address, optyfiOracleAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "UniswapV2ExchangeAdapterEthereum", - address: uniswapV2ExchangeAdapterEthereum.address, - constructorArguments: [registryProxyAddress, EthereumUniswapV2.router02.address, optyfiOracleAddress], - }); - } - } - } - - const uniswapV2PoolAdapterEthereumAddress = await (await deployments.get("UniswapV2ExchangeAdapterEthereum")).address; - const uniswapV2PoolAdapterEthereumInstance = ( - await ethers.getContractAt(UniswapV2ExchangeAdapter__factory.abi, uniswapV2PoolAdapterEthereumAddress) - ); - const registryProxyInstance = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const riskOperator = await registryProxyInstance.riskOperator(); - const riskOperatorSigner = await ethers.getSigner(riskOperator); - - const USDC_WETH_LP = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"; - - const liquidityPoolToWantTokenToSlippages = [ - { liquidityPool: USDC_WETH_LP, wantToken: EthereumTokens.WRAPPED_TOKENS.WETH, slippage: "70" }, - { liquidityPool: USDC_WETH_LP, wantToken: EthereumTokens.PLAIN_TOKENS.USDC, slippage: "70" }, - ]; - const pendingLiquidityPoolToWantTokenToSlippages = []; - for (const liquidityPoolToWantTokenToSlippage of liquidityPoolToWantTokenToSlippages) { - const slippage = await uniswapV2PoolAdapterEthereumInstance.liquidityPoolToWantTokenToSlippage( - liquidityPoolToWantTokenToSlippage.liquidityPool, - liquidityPoolToWantTokenToSlippage.wantToken, - ); - if (!BigNumber.from(slippage).eq(BigNumber.from(liquidityPoolToWantTokenToSlippage.slippage))) { - pendingLiquidityPoolToWantTokenToSlippages.push(liquidityPoolToWantTokenToSlippage); - } - } - - if (pendingLiquidityPoolToWantTokenToSlippages.length > 0) { - console.log(JSON.stringify(pendingLiquidityPoolToWantTokenToSlippages, null, 4)); - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log("updating pending LiquidityPool To Want Token To Slippages"); - const tx = await uniswapV2PoolAdapterEthereumInstance - .connect(riskOperatorSigner) - .setLiquidityPoolToWantTokenToSlippage(pendingLiquidityPoolToWantTokenToSlippages); - await tx.wait(1); - } else { - console.log( - "cannot update pending LiquidityPool To Want Token To Slippages because the signer is not the risk operator", - ); - } - } else { - console.log("pendingLiquidityPoolToWantTokenToSlippages are up to date"); - } -}; -export default func; -func.tags = ["UniswapV2ExchangeAdapterEthereum"]; -func.dependencies = ["Registry", "OptyFiOracle"]; diff --git a/deploy_mainnet/043_deploy_sushiswapexchangeadapterethereum.ts b/deploy_mainnet/043_deploy_sushiswapexchangeadapterethereum.ts deleted file mode 100644 index 019b4ac0..00000000 --- a/deploy_mainnet/043_deploy_sushiswapexchangeadapterethereum.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { BigNumber } from "ethers"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; -import { waitforme } from "../helpers/utils"; -import { Registry, Registry__factory, UniswapV2ExchangeAdapter, UniswapV2ExchangeAdapter__factory } from "../typechain"; -import { getAddress } from "ethers/lib/utils"; - -const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; - -const func: DeployFunction = async ({ - deployments, - getNamedAccounts, - getChainId, - network, - tenderly, - run, - ethers, -}: HardhatRuntimeEnvironment) => { - const { deploy } = deployments; - const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("UniswapV2ExchangeAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const optyfiOracleAddress = await (await deployments.get("OptyFiOracle")).address; - - const chainId = await getChainId(); - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiswapExchangeAdapterEthereum", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress, EthereumSushiswap.SushiswapRouter.address, optyfiOracleAddress], - log: true, - skipIfAlreadyDeployed: true, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const sushiswapExchangeAdapterEthereum = await deployments.get("SushiswapExchangeAdapterEthereum"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "SushiswapExchangeAdapterEthereum", - address: sushiswapExchangeAdapterEthereum.address, - constructorArguments: [registryProxyAddress, EthereumSushiswap.SushiswapRouter.address, optyfiOracleAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "SushiswapExchangeAdapterEthereum", - address: sushiswapExchangeAdapterEthereum.address, - constructorArguments: [registryProxyAddress, EthereumSushiswap.SushiswapRouter.address, optyfiOracleAddress], - }); - } - } - } - const sushiswapExchangeAdapterEthereumAddress = await ( - await deployments.get("SushiswapExchangeAdapterEthereum") - ).address; - const sushiswapExchangeAdapterEthereumInstance = ( - await ethers.getContractAt(UniswapV2ExchangeAdapter__factory.abi, sushiswapExchangeAdapterEthereumAddress) - ); - const registryProxyInstance = await ethers.getContractAt(Registry__factory.abi, registryProxyAddress); - const riskOperator = await registryProxyInstance.riskOperator(); - const riskOperatorSigner = await ethers.getSigner(riskOperator); - - const USDC_WETH_LP = "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0"; - - const liquidityPoolToWantTokenToSlippages = [ - { liquidityPool: USDC_WETH_LP, wantToken: EthereumTokens.WRAPPED_TOKENS.WETH, slippage: "100" }, - { liquidityPool: USDC_WETH_LP, wantToken: EthereumTokens.PLAIN_TOKENS.USDC, slippage: "70" }, - ]; - const pendingLiquidityPoolToWantTokenToSlippages = []; - for (const liquidityPoolToWantTokenToSlippage of liquidityPoolToWantTokenToSlippages) { - const slippage = await sushiswapExchangeAdapterEthereumInstance.liquidityPoolToWantTokenToSlippage( - liquidityPoolToWantTokenToSlippage.liquidityPool, - liquidityPoolToWantTokenToSlippage.wantToken, - ); - if (!BigNumber.from(slippage).eq(BigNumber.from(liquidityPoolToWantTokenToSlippage.slippage))) { - pendingLiquidityPoolToWantTokenToSlippages.push(liquidityPoolToWantTokenToSlippage); - } - } - - if (pendingLiquidityPoolToWantTokenToSlippages.length > 0) { - console.log(JSON.stringify(pendingLiquidityPoolToWantTokenToSlippages, null, 4)); - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log("updating pending LiquidityPool To Want Token To Slippages"); - const tx = await sushiswapExchangeAdapterEthereumInstance - .connect(riskOperatorSigner) - .setLiquidityPoolToWantTokenToSlippage(pendingLiquidityPoolToWantTokenToSlippages); - await tx.wait(1); - } else { - console.log( - "cannot update pending LiquidityPool To Want Token To Slippages because the signer is not the risk operator", - ); - } - } else { - console.log("pendingLiquidityPoolToWantTokenToSlippages are up to date"); - } -}; -export default func; -func.tags = ["SushiswapExchangeAdapterEthereum"]; -func.dependencies = ["Registry", "OptyFiOracle"]; diff --git a/deploy_mainnet/044_approveandmapswappooltoadapter.ts b/deploy_mainnet/044_approveandmapswappooltoadapter.ts deleted file mode 100644 index 8e8aa6e5..00000000 --- a/deploy_mainnet/044_approveandmapswappooltoadapter.ts +++ /dev/null @@ -1,122 +0,0 @@ -import { BigNumber } from "ethers"; -import { getNamedAccounts } from "hardhat"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; - -const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvironment) => { - const { deployer } = await getNamedAccounts(); - const { getAddress } = ethers.utils; - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - const registryV2Instance = await ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); - const curveExchangeAdapterEthereum = await deployments.get("CurveExchangeAdapterEthereum"); - const uniswapV2ExchangeAdapterEthereum = await deployments.get("UniswapV2ExchangeAdapterEthereum"); - const sushiswapExchangeAdapterEthereum = await deployments.get("SushiswapExchangeAdapterEthereum"); - const operatorAddress = await registryV2Instance.getOperator(); - const operatorSigner = await ethers.getSigner(operatorAddress); - const riskOperatorAddress = await registryV2Instance.getRiskOperator(); - const riskOperatorSigner = await ethers.getSigner(riskOperatorAddress); - - // approve swap pools and map to adapter - const poolsWithRatings: { [key: string]: { rate: number; adapter: string } } = { - "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7": { rate: 99, adapter: curveExchangeAdapterEthereum.address }, // curve Pool for DAI/USDC/USDT - "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022": { rate: 99, adapter: curveExchangeAdapterEthereum.address }, // curve ETH/stETH StableSwap - "0xdcef968d416a41cdac0ed8702fac8128a64241a2": { rate: 99, adapter: curveExchangeAdapterEthereum.address }, // curve FRAX/USDC StableSwap - "0x795065dCc9f64b5614C407a6EFDC400DA6221FB0": { rate: 99, adapter: sushiswapExchangeAdapterEthereum.address }, // SUSHI-SUSHI-WETH - "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0": { rate: 99, adapter: sushiswapExchangeAdapterEthereum.address }, // SUSHI-USDC-WETH - "0xCEfF51756c56CeFFCA006cD410B03FFC46dd3a58": { rate: 99, adapter: sushiswapExchangeAdapterEthereum.address }, // SUSHI-WBTC-WETH - "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc": { rate: 99, adapter: uniswapV2ExchangeAdapterEthereum.address }, // UNI-USDC-WETH - }; - - const onlyMapSwapPoolsToAdapters = []; - const approveSwapPoolAndMap = []; - const rateSwapPools: [string, number][] = []; - - for (const pool of Object.keys(poolsWithRatings)) { - const { rating, isLiquidityPool } = await registryV2Instance.getSwapPool(pool); - const adapter = await registryV2Instance.getSwapPoolToAdapter(pool); - if (!isLiquidityPool && getAddress(adapter) != getAddress(poolsWithRatings[pool].adapter)) { - approveSwapPoolAndMap.push([pool, poolsWithRatings[pool].adapter]); - } - if (isLiquidityPool && getAddress(adapter) != getAddress(poolsWithRatings[pool].adapter)) { - onlyMapSwapPoolsToAdapters.push([pool, poolsWithRatings[pool].adapter]); - } - if (rating != poolsWithRatings[pool].rate) { - rateSwapPools.push([pool, poolsWithRatings[pool].rate]); - } - } - - console.log("==Approve swap pool and map to adapter=="); - if (approveSwapPoolAndMap.length > 0) { - console.log(`approve ${approveSwapPoolAndMap.length} pools And Map `, approveSwapPoolAndMap); - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - // approve swap pool and map adapter - console.log(`operator approving and mapping ${approveSwapPoolAndMap.length} pools ...`); - const feeData = await ethers.provider.getFeeData(); - const approveSwapPoolAndMapAdapterTx = await registryV2Instance - .connect(operatorSigner) - ["approveSwapPoolAndMapToAdapter((address,address)[])"](approveSwapPoolAndMap, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await approveSwapPoolAndMapAdapterTx.wait(); - } else { - console.log("cannot approve and map swap pools as signer is not the operator"); - } - } else { - console.log("Already approved swap pool and map to adapter"); - } - - console.log("==Only map swap pool to adapter=="); - if (onlyMapSwapPoolsToAdapters.length > 0) { - console.log(`map ${onlyMapSwapPoolsToAdapters.length} pools `, onlyMapSwapPoolsToAdapters); - // only map swap pool to adapter - if (getAddress(operatorSigner.address) === getAddress(deployer)) { - console.log(`operator only mapping ${onlyMapSwapPoolsToAdapters.length} pools ...`); - const feeData = await ethers.provider.getFeeData(); - const mapToAdapterTx = await registryV2Instance - .connect(operatorSigner) - ["setSwapPoolToAdapter((address,address)[])"](onlyMapSwapPoolsToAdapters, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await mapToAdapterTx.wait(); - } else { - console.log("cannot map swap pools as signer is not the operator"); - } - } else { - console.log("Already mapped to adapter"); - } - - console.log("==Only rate swap pool=="); - if (rateSwapPools.length > 0) { - console.log(`${rateSwapPools.length} pools to rate ...`, rateSwapPools); - // rate pools - if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { - console.log(`risk operator rating ${rateSwapPools.length} pools ...`, rateSwapPools); - const feeData = await ethers.provider.getFeeData(); - const rateAdapterTx = await registryV2Instance - .connect(riskOperatorSigner) - ["rateSwapPool((address,uint8)[])"](rateSwapPools, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await rateAdapterTx.wait(); - } else { - console.log("cannot rate swap pools as signer is not the risk operator"); - } - } else { - console.log("Already rate swap pool"); - } -}; -export default func; -func.tags = ["ApproveAndMapSwapPoolToAdapter"]; -func.dependencies = [ - "Registry", - "CurveExchangeAdapterEthereum", - "UniswapV2ExchangeAdapterEthereum", - "SushiswapExchangeAdapterEthereum", -]; diff --git a/helpers/data/adapter-with-strategies.ts b/helpers/data/adapter-with-strategies.ts index 740f314f..e15aa87c 100644 --- a/helpers/data/adapter-with-strategies.ts +++ b/helpers/data/adapter-with-strategies.ts @@ -2660,7 +2660,7 @@ const mainnetVaults: VaultType = { underlyingTokensHash: MULTI_CHAIN_VAULT_TOKENS.mainnet.USDC.hash, vaultConfig: vaultConfigRP0, userDepositCapUT: BigNumber.from("100000000000"), // 100,000 USDC user deposit cap - minimumDepositValueUT: BigNumber.from("1000000000"), // 1000 USDC minimum deposit + minimumDepositValueUT: BigNumber.from("0"), // 0 USDC minimum deposit totalValueLockedLimitUT: BigNumber.from("10000000000000"), // 10,000,000 USDC TVL limit }, WETH: { diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index cde9e667..f7bab949 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -25,14 +25,14 @@ export class StrategyManager { this.aaveV1AdapterObj = new AaveV1Adapter(vaultHelperMainnetInstance); this.aaveV2AdapterObj = new Aavev2Adapter(vaultHelperMainnetInstance, optyFiOracleAddress); this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; - this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; - this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; - this.liquidityPoolToAdapter["0xC11b1268C1A384e55C48c2391d8d480264A3A7F4"] = this.compoundAdapterObj; - this.liquidityPoolToAdapter["0x39AA39c021dfbaE8faC545936693aC917d5E7563"] = this.compoundAdapterObj; - this.liquidityPoolToAdapter["0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; // cDAI + this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; // cUSDT + this.liquidityPoolToAdapter["0xC11b1268C1A384e55C48c2391d8d480264A3A7F4"] = this.compoundAdapterObj; // cWBTC + this.liquidityPoolToAdapter["0x39AA39c021dfbaE8faC545936693aC917d5E7563"] = this.compoundAdapterObj; // cUSDC + this.liquidityPoolToAdapter["0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5"] = this.compoundAdapterObj; // cETH this.liquidityPoolToAdapter["0x24a42fD28C976A61Df5D00D0599C34c4f90748c8"] = this.aaveV1AdapterObj; - this.liquidityPoolToAdapter["0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4"] = this.compoundAdapterObj; - this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; + this.liquidityPoolToAdapter["0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4"] = this.compoundAdapterObj; // cCOMP + this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; // cYFI } getDepositPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { diff --git a/helpers/utils.ts b/helpers/utils.ts index 568ec7b8..729c537f 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -481,7 +481,7 @@ export async function assertPostVaultDepositState( expect(userBalanceBeforeVT).to.eq(userBalanceAfterVT); expect(userBalanceAfterUT).to.eq(userBalanceBeforeUT); expect(vaultTotalSupplyAfterVT).to.eq(vaultTotalSupplyBeforeVT); - expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 13).toNumber()); } export async function assertPostUserWithdrawState( diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index 460cd411..d0a17a26 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -18,10 +18,8 @@ import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/a import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; import { Registry, - RiskManager, Vault, ERC20, - RiskManager__factory, Registry__factory, Vault__factory, ERC20__factory, @@ -42,7 +40,6 @@ chai.use(solidity); const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; const IGNORE_VAULTS = process.env.IGNORE_VAULTS; - const strategyHashReadIndexes: { [key: string]: { valueLPIndex: number } } = { "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { // dai-DEPOSIT-AaveV2-aDAI @@ -50,7 +47,31 @@ const strategyHashReadIndexes: { [key: string]: { valueLPIndex: number } } = { }, "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { // dai-DEPOSIT-Compound-cDAI - valueLPIndex: 2, + valueLPIndex: 1, + }, + "0x74ceffc4d239683893dd31f6c4bed33f65aafb1bdad2c47d61bae7db81a42e4d": { + // usdt-DEPOSIT-AaveV2-aUSDT + valueLPIndex: 0, + }, + "0x4103355b18f7a7ea81aebc211548510bb077426632fbe4899f4cf162c70ba396": { + // usdt-DEPOSIT-Compound-cUSDT + valueLPIndex: 1, + }, + "0x46b9c41c6ff6c82958774b97fc426f046011e4177b8f64ded0d1a704d083b3c6": { + // wbtc-DEPOSIT-AaveV2-aWBTC + valueLPIndex: 0, + }, + "0x35f4123193f545465801d2cd7418a60c7cfe6ade80b8be4f941124705fb7a39c": { + // wbtc-DEPOSIT-Compound-cWBTC + valueLPIndex: 1, + }, + "0xdc405bd6462f69e015108f9c274278cf552f891ed2015820827a672abafd48fc": { + // usdc-DEPOSIT-AaveV2-aUSDC + valueLPIndex: 0, + }, + "0x58404c3f191270f62e374653c7aa923e5487ed261523b0aef0a432a01a8ea088": { + // usdc-DEPOSIT-Compound-cUSDC + valueLPIndex: 1, }, "0x514e845e4f1401cfe30f214a1386dfd06a98e15316dde7f669889a82ddffdeb8": { // weth-DEPOSIT-AaveV2-aWETH @@ -80,9 +101,6 @@ describe(`${fork}-Vault-rev7`, () => { this.registry = ( await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) ); - this.riskManager = ( - await ethers.getContractAt(RiskManager__factory.abi, (await deployments.get("RiskManager")).address) - ); this.vaults = {}; this.tokens = {}; this.signers = {} as Signers; @@ -703,57 +721,59 @@ describe(`${fork}-Vault-rev7`, () => { } }); after(async function () { - const vaultWithdrawLP = await this.strategyManager.liquidityPoolToAdapter[ - steps[steps.length - 1].pool - ].getOutputTokenBalance( - this.vaults[riskProfile][token], - steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, - steps[steps.length - 1].isSwap, - ethers.provider, - ); + if ((await this.vaults[riskProfile][token].getStrategies()).length !== 0) { + const vaultWithdrawLP = await this.strategyManager.liquidityPoolToAdapter[ + steps[steps.length - 1].pool + ].getOutputTokenBalance( + this.vaults[riskProfile][token], + steps.length === 1 ? this.tokens[token].address : steps[steps.length - 2].outputToken, + steps[steps.length - 1].pool, + steps[steps.length - 1].outputToken, + steps[steps.length - 1].isSwap, + ethers.provider, + ); - const { - userBalanceBeforeUT, - userBalanceBeforeVT, - vaultTotalSupplyBeforeVT, - vaultValueBeforeUT, - vaultBalanceBeforeLP, - vaultBalanceBeforeUT, - } = await getPreActionState( - this.signers.alice, - this.tokens[token], - this.vaults[riskProfile][token], - this.strategyManager, - steps, - ); + const { + userBalanceBeforeUT, + userBalanceBeforeVT, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + } = await getPreActionState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ); - const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] - .connect(this.signers.strategyOperator) - .vaultWithdrawSomeFromStrategy(strategyHash, vaultWithdrawLP); - await vaultWithdrawSomeFromStrategyTx.wait(1); + const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.strategyOperator) + .vaultWithdrawSomeFromStrategy(strategyHash, vaultWithdrawLP); + await vaultWithdrawSomeFromStrategyTx.wait(1); - await assertPostVaultWithdrawState( - this.signers.alice, - this.tokens[token], - this.vaults[riskProfile][token], - this.strategyManager, - steps, - ethers.provider, - userBalanceBeforeUT, - vaultWithdrawLP, - vaultTotalSupplyBeforeVT, - vaultValueBeforeUT, - userBalanceBeforeVT, - vaultBalanceBeforeLP, - vaultBalanceBeforeUT, - ); + await assertPostVaultWithdrawState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + vaultWithdrawLP, + vaultTotalSupplyBeforeVT, + vaultValueBeforeUT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + ); - const removeStrategyTx = await this.vaults[riskProfile][token] - .connect(this.signers.operator) - .removeStrategy(strategyHash); - await removeStrategyTx.wait(1); + const removeStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.operator) + .removeStrategy(strategyHash); + await removeStrategyTx.wait(1); + } }); }); } @@ -774,7 +794,7 @@ async function userDeposit( const underlyingTokenSymbol = await underlyingTokenInstance.symbol(); const decimals = await underlyingTokenInstance.decimals(); if (_userDepositInDecimals.eq(BigNumber.from("0"))) { - _userDepositInDecimals = BigNumber.from("10").pow(decimals); + _userDepositInDecimals = BigNumber.from("2").mul(parseUnits("1", decimals)); } const vaultTokenSymbol = await vaultInstance.symbol(); From 48be89f32d0d50ee8d26ba41bd7adc725f754cc4 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Fri, 23 Dec 2022 01:54:14 -0500 Subject: [PATCH 36/44] test(strategies): refactor output index --- contracts/mocks/contracts/TestVault.sol | 4 + contracts/protocol/tokenization/Vault.sol | 13 +-- .../tokenization/VaultHelperMainnet.sol | 4 + helpers/adapters/ethereum/AaveV1Adapter.ts | 21 ++-- helpers/adapters/ethereum/AaveV2Adapter.ts | 29 +++--- helpers/adapters/ethereum/CompoundAdapter.ts | 43 +++++--- helpers/strategy-manager.ts | 38 ++++--- test/test-opty/3_vault_strategies.spec.ts | 98 ++++++++++++++----- test/test-opty/vaultweiroll.spec.ts | 89 ++++++++++++----- 9 files changed, 229 insertions(+), 110 deletions(-) diff --git a/contracts/mocks/contracts/TestVault.sol b/contracts/mocks/contracts/TestVault.sol index 6976155f..fabf9722 100644 --- a/contracts/mocks/contracts/TestVault.sol +++ b/contracts/mocks/contracts/TestVault.sol @@ -53,4 +53,8 @@ contract TestVault { function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { return _arg; } + + function getERC20Balance(address _owner, ERC20 _token) external view returns (uint256) { + return _token.balanceOf(_owner); + } } diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index ac08e3d9..d6fe4f4a 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -508,15 +508,12 @@ contract Vault is * @inheritdoc IVault */ function getLastStrategyStepBalanceLP(bytes32 _strategyHash) public view override returns (uint256) { - DataTypes.StrategyPlanInput memory _strategyPlanInput = - IStrategyRegistry(registryContract.getStrategyRegistry()).getLastStepBalanceLPPlan( - address(this), - _strategyHash - ); return - abi.decode( - _readExecute(_strategyPlanInput.commands, _strategyPlanInput.state)[_strategyPlanInput.outputIndex], - (uint256) + _getUint256( + IStrategyRegistry(registryContract.getStrategyRegistry()).getLastStepBalanceLPPlan( + address(this), + _strategyHash + ) ); } diff --git a/contracts/protocol/tokenization/VaultHelperMainnet.sol b/contracts/protocol/tokenization/VaultHelperMainnet.sol index 485de5a6..43088619 100644 --- a/contracts/protocol/tokenization/VaultHelperMainnet.sol +++ b/contracts/protocol/tokenization/VaultHelperMainnet.sol @@ -123,5 +123,9 @@ contract VaultHelperMainnet { return (_amountOut * (10000 - _slippage)) / 10000; } + function getERC20Balance(IERC20 _token, address _owner) external view returns (uint256) { + return _token.balanceOf(_owner); + } + receive() external payable {} } diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index 76d6f982..919325f1 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -6,7 +6,6 @@ import { getAddress } from "ethers/lib/utils"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; -import { ERC20__factory, IWETH9__factory } from "../../../typechain"; import { JsonRpcProvider } from "@ethersproject/providers"; export class AaveV1Adapter implements AdapterInterface { vaultHelperMainnetInstance; @@ -95,8 +94,12 @@ export class AaveV1Adapter implements AdapterInterface { outputToken: string, _isSwap: boolean, ): ReturnValue { - const outputTokenInstance = new ethers.Contract(outputToken, ERC20__factory.abi); - const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const amountLP = planner.add( + this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( + outputToken, + vaultInstance.address, + ).staticcall(), + ); return amountLP as ReturnValue; } @@ -124,14 +127,14 @@ export class AaveV1Adapter implements AdapterInterface { pool: string, outputToken: string, _isSwap: boolean, - _provider: JsonRpcProvider, + provider: JsonRpcProvider, ): Promise { - const outputTokenInstance = new ethers.Contract( - outputToken, - ERC20__factory.abi, - _provider, + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperMainnetInstance.address, + this.vaultHelperMainnetInstance.interface, + provider, ); - return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); } async getValueInInputToken( diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index f8c0a903..ee1754ca 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -6,7 +6,6 @@ import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { getAddress } from "ethers/lib/utils"; import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; -import { ERC20__factory } from "../../../typechain"; import { JsonRpcProvider } from "@ethersproject/providers"; const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; @@ -92,8 +91,12 @@ export class Aavev2Adapter implements AdapterInterface { outputToken: string, _isSwap: boolean, ): ReturnValue { - const outputTokenInstance = weirollContract.createContract(new ethers.Contract(outputToken, ERC20__factory.abi)); - const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const amountLP = planner.add( + this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( + outputToken, + vaultInstance.address, + ).staticcall(), + ); return amountLP as ReturnValue; } @@ -122,13 +125,13 @@ export class Aavev2Adapter implements AdapterInterface { vaultInstance: Contract, vaultUnderlyingToken: string, ): weirollPlanner { - const rewardContract = new ethers.Contract(STK_AAVE, ERC20__factory.abi); - const rewardInstance = weirollContract.createContract(rewardContract); - const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const rewardAmount = planner.add( + this.vaultHelperMainnetInstance["getERC20Balance(address,address)"](STK_AAVE, vaultInstance.address).staticcall(), + ); const minumumOutputAmount = planner.add( this.vaultHelperMainnetInstance[ "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" - ](this.optyFiOracleAddress, rewardInstance.address, vaultUnderlyingToken, rewardAmount, 100).staticcall(), + ](this.optyFiOracleAddress, STK_AAVE, vaultUnderlyingToken, rewardAmount, 100).staticcall(), ); let univ3Path; switch (getAddress(vaultUnderlyingToken)) { @@ -220,14 +223,14 @@ export class Aavev2Adapter implements AdapterInterface { pool: string, outputToken: string, _isSwap: boolean, - _provider: JsonRpcProvider, + provider: JsonRpcProvider, ): Promise { - const outputTokenInstance = new ethers.Contract( - outputToken, - ERC20__factory.abi, - _provider, + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperMainnetInstance.address, + this.vaultHelperMainnetInstance.interface, + provider, ); - return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); } async getValueInInputToken( diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index b27f0934..0a131ed4 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -5,7 +5,7 @@ import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; import { ReturnValue } from "../../type"; -import { ERC20__factory, ICompound__factory, IWETH9__factory } from "../../../typechain"; +import { ICompound__factory } from "../../../typechain"; import { getAddress, parseEther } from "ethers/lib/utils"; import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; @@ -101,8 +101,12 @@ export class CompoundAdapter implements AdapterInterface { outputToken: string, _isSwap: boolean, ): ReturnValue { - const outputTokenInstance = weirollContract.createContract(new ethers.Contract(outputToken, ERC20__factory.abi)); - const amountLP = planner.add(outputTokenInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const amountLP = planner.add( + this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( + outputToken, + vaultInstance.address, + ).staticcall(), + ); return amountLP as ReturnValue; } @@ -133,13 +137,22 @@ export class CompoundAdapter implements AdapterInterface { ): weirollPlanner { const uniswapV2RouterContract = new ethers.Contract(UniswapV2.router02.address, UniswapV2.router02.abi); const uniswapV2RouterInstance = weirollContract.createContract(uniswapV2RouterContract); - const rewardContract = new ethers.Contract(EthereumTokens.REWARD_TOKENS.COMP, ERC20__factory.abi); - const rewardInstance = weirollContract.createContract(rewardContract); - const rewardAmount = planner.add(rewardInstance["balanceOf(address)"](vaultInstance.address).staticcall()); + const rewardAmount = planner.add( + this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( + EthereumTokens.REWARD_TOKENS.COMP, + vaultInstance.address, + ).staticcall(), + ); const minumumOutputAmount = planner.add( this.vaultHelperMainnetInstance[ "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" - ](this.optyFiOracleAddress, rewardInstance.address, vaultUnderlyingToken, rewardAmount, 100).staticcall(), + ]( + this.optyFiOracleAddress, + EthereumTokens.REWARD_TOKENS.COMP, + vaultUnderlyingToken, + rewardAmount, + 100, + ).staticcall(), ); switch (getAddress(vaultUnderlyingToken)) { case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { @@ -215,14 +228,14 @@ export class CompoundAdapter implements AdapterInterface { pool: string, outputToken: string, _isSwap: boolean, - _provider: JsonRpcProvider, + provider: JsonRpcProvider, ): Promise { - const outputTokenInstance = new ethers.Contract( - outputToken, - ERC20__factory.abi, - _provider, + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperMainnetInstance.address, + this.vaultHelperMainnetInstance.interface, + provider, ); - return await outputTokenInstance["balanceOf(address)"](vaultInstance.address); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); } async getValueInInputToken( @@ -232,12 +245,12 @@ export class CompoundAdapter implements AdapterInterface { outputToken: string, outputTokenAmount: BigNumber, isSwap: boolean, - _provider: JsonRpcProvider, + provider: JsonRpcProvider, ): Promise { const cTokenInstance = new ethers.Contract( pool, ICompound__factory.abi, - _provider, + provider, ); const exchangeRateStored = await cTokenInstance.exchangeRateStored(); return outputTokenAmount.mul(exchangeRateStored).div(parseEther("1")); diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index f7bab949..7104d05f 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -44,11 +44,13 @@ export class StrategyManager { const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; if (index > 0) { - const inputTokenContract = weirollContract.createContract( - new ethers.Contract(strategySteps[index - 1].outputToken, ERC20__factory.abi), - ); const inputTokenAmount = ( - planner.add(inputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) + planner.add( + this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + strategySteps[index - 1].outputToken, + vaultContract.address, + ).staticcall(), + ) ); adapterObj.getDepositPlan( planner, @@ -97,11 +99,13 @@ export class StrategyManager { amountLP as ReturnValue, ); } else { - const outputTokenContract = weirollContract.createContract( - new ethers.Contract(strategySteps[iteratorIndex].outputToken, ERC20__factory.abi), - ); const amountLP = ( - planner.add(outputTokenContract["balanceOf(address)"](vaultContract.address).staticcall()) + planner.add( + this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + strategySteps[iteratorIndex].outputToken, + vaultContract.address, + ).staticcall(), + ) ); adapterObj.getWithdrawPlan( planner, @@ -120,11 +124,13 @@ export class StrategyManager { getOraValueUTPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { const planner = new weirollPlanner(); - const outputTokenContract = weirollContract.createContract( - new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), - ); const amountLP = ( - planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address).staticcall()) + planner.add( + this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + strategySteps[strategySteps.length - 1].outputToken, + vaultInstance.address, + ).staticcall(), + ) ); let amountUT; for (const [index, _strategyStep] of strategySteps.entries()) { @@ -215,10 +221,12 @@ export class StrategyManager { vaultInstance: Contract, ): WeirollPlan { const planner = new weirollPlanner(); - const outputTokenContract = weirollContract.createContract( - new ethers.Contract(strategySteps[strategySteps.length - 1].outputToken, ERC20__factory.abi), + const amountLP = planner.add( + this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + strategySteps[strategySteps.length - 1].outputToken, + vaultInstance.address, + ).staticcall(), ); - const amountLP = planner.add(outputTokenContract["balanceOf(address)"](vaultInstance.address).staticcall()); planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index d0a17a26..48b229d5 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -40,46 +40,68 @@ chai.use(solidity); const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; const IGNORE_VAULTS = process.env.IGNORE_VAULTS; -const strategyHashReadIndexes: { [key: string]: { valueLPIndex: number } } = { +const strategyHashReadIndexes: { + [key: string]: { oraValueUTIndex: number; oraValueLPIndex: number; lastStepLPBalanceIndex: number }; +} = { "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { // dai-DEPOSIT-AaveV2-aDAI - valueLPIndex: 0, + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, }, "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { // dai-DEPOSIT-Compound-cDAI - valueLPIndex: 1, + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, }, "0x74ceffc4d239683893dd31f6c4bed33f65aafb1bdad2c47d61bae7db81a42e4d": { // usdt-DEPOSIT-AaveV2-aUSDT - valueLPIndex: 0, + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, }, "0x4103355b18f7a7ea81aebc211548510bb077426632fbe4899f4cf162c70ba396": { // usdt-DEPOSIT-Compound-cUSDT - valueLPIndex: 1, + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, }, "0x46b9c41c6ff6c82958774b97fc426f046011e4177b8f64ded0d1a704d083b3c6": { // wbtc-DEPOSIT-AaveV2-aWBTC - valueLPIndex: 0, + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, }, "0x35f4123193f545465801d2cd7418a60c7cfe6ade80b8be4f941124705fb7a39c": { // wbtc-DEPOSIT-Compound-cWBTC - valueLPIndex: 1, + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, }, "0xdc405bd6462f69e015108f9c274278cf552f891ed2015820827a672abafd48fc": { // usdc-DEPOSIT-AaveV2-aUSDC - valueLPIndex: 0, + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, }, "0x58404c3f191270f62e374653c7aa923e5487ed261523b0aef0a432a01a8ea088": { // usdc-DEPOSIT-Compound-cUSDC - valueLPIndex: 1, + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, }, "0x514e845e4f1401cfe30f214a1386dfd06a98e15316dde7f669889a82ddffdeb8": { // weth-DEPOSIT-AaveV2-aWETH - valueLPIndex: 0, + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, }, "0x87ed056054f13b934a9864226aa8c1f52ad63a7ad25bb2c74c1f920c9635c04c": { // weth-DEPOSIT-Compound-cETH - valueLPIndex: 1, + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, }, }; @@ -115,8 +137,14 @@ describe(`${fork}-Vault-rev7`, () => { this.signers.strategyOperator = await ethers.getSigner(await this.registry.getStrategyOperator()); this.signers.governance = await ethers.getSigner(await this.registry.getGovernance()); for (const riskProfile of Object.keys(MultiChainVaults[fork])) { + if (riskProfile !== "Save") { + continue; + } this.vaults[riskProfile] = {}; for (const token of Object.keys(MultiChainVaults[fork][riskProfile])) { + if (!["DAI", "USDT", "WBTC", "USDC", "WETH"].includes(token)) { + continue; + } if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { continue; } @@ -171,7 +199,13 @@ describe(`${fork}-Vault-rev7`, () => { }); describe(`${fork}-Vault-rev7 strategies`, () => { for (const riskProfile of Object.keys(StrategiesByTokenByChain[fork])) { + if (riskProfile !== "Save") { + continue; + } for (const token of Object.keys(StrategiesByTokenByChain[fork][riskProfile])) { + if (!["DAI", "USDT", "WBTC", "USDC", "WETH"].includes(token)) { + continue; + } if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { continue; } @@ -192,24 +226,30 @@ describe(`${fork}-Vault-rev7`, () => { const addStrategyPlantx = await this.strategyRegistry .connect(this.signers.operator) .addStrategyPlan(this.vaults[riskProfile][token].address, strategyHash, { - oraValueUTPlan: this.strategyManager.getOraValueUTPlan( - StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, - steps, - this.vaults[riskProfile][token], - ), + oraValueUTPlan: { + ...this.strategyManager.getOraValueUTPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + outputIndex: strategyHashReadIndexes[strategyHash].oraValueUTIndex, + }, oraValueLPPlan: { ...this.strategyManager.getOraSomeValueLPPlan( StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, steps, this.vaults[riskProfile][token], ), - outputIndex: strategyHashReadIndexes[strategyHash].valueLPIndex, + outputIndex: strategyHashReadIndexes[strategyHash].oraValueLPIndex, + }, + lastStepBalanceLPPlan: { + ...this.strategyManager.getLastStrategyStepBalancePlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + outputIndex: strategyHashReadIndexes[strategyHash].lastStepLPBalanceIndex, }, - lastStepBalanceLPPlan: this.strategyManager.getLastStrategyStepBalancePlan( - StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, - steps, - this.vaults[riskProfile][token], - ), depositSomeToStrategyPlan: this.strategyManager.getDepositPlan( StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, steps, @@ -403,7 +443,8 @@ describe(`${fork}-Vault-rev7`, () => { const vaultDepositTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) .vaultDepositSomeToStrategy(strategyHash, vaultDepositUT); - await vaultDepositTx.wait(1); + const res = await vaultDepositTx.wait(1); + console.log("gas ", res.gasUsed.toString()); await assertPostVaultDepositState( this.signers.alice, @@ -751,7 +792,8 @@ describe(`${fork}-Vault-rev7`, () => { const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) .vaultWithdrawSomeFromStrategy(strategyHash, vaultWithdrawLP); - await vaultWithdrawSomeFromStrategyTx.wait(1); + const res = await vaultWithdrawSomeFromStrategyTx.wait(1); + console.log("gas ", res.gasUsed.toString()); await assertPostVaultWithdrawState( this.signers.alice, @@ -846,7 +888,6 @@ async function userDeposit( const tx1 = await underlyingTokenInstance .connect(signers[i]) .approve(vaultInstance.address, _userDepositInDecimals); - await tx1.wait(1); const { userBalanceBeforeUT, userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT } = await getPreActionState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); @@ -854,7 +895,9 @@ async function userDeposit( const tx2 = await vaultInstance .connect(signers[i]) .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", []); - await tx2.wait(1); + const res = await tx2.wait(1); + + console.log("gas ", res.gasUsed.toString()); await assertPostUserDepositState( signers[i], @@ -995,7 +1038,8 @@ async function userWithdraw( const tx = await vaultInstance .connect(signers[i]) .userWithdrawVault(signers[i].address, userWithdrawBalance.mul(3).div(4), []); - await tx.wait(); + const res = await tx.wait(); + console.log("gas ", res.gasUsed.toString()); await assertPostUserWithdrawState( signers[i], diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts index f5c1d4f0..e7b87be8 100644 --- a/test/test-opty/vaultweiroll.spec.ts +++ b/test/test-opty/vaultweiroll.spec.ts @@ -5,6 +5,8 @@ import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { Contract as weirollContract, Planner as weirollPlanner } from "@weiroll/weiroll.js"; import { Signers } from "../../helpers/utils"; import { + CommandBuilder, + CommandBuilder__factory, CompoundAdapter, CompoundAdapter__factory, ERC20, @@ -13,6 +15,7 @@ import { ICompound__factory, TestVault, VaultWeiroll, + VaultWeiroll__factory, } from "../../typechain"; import { setTokenBalanceInStorage } from "./utils"; import { parseUnits } from "ethers/lib/utils"; @@ -39,23 +42,36 @@ describe("VaultWeiroll", async function () { const signers: SignerWithAddress[] = await ethers.getSigners(); this.signers.deployer = signers[0]; this.signers.alice = signers[3]; - vaultWeirollUSDC = ( - await deployContract(this.signers.deployer, await artifacts.readArtifact("VaultWeiroll"), [ - EthereumTokens.PLAIN_TOKENS.USDC, - "6", - "USD Coin", - "USDC", - ]) + usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDT); + const commandBuilderFactory = await ethers.getContractFactory( + CommandBuilder__factory.abi, + CommandBuilder__factory.bytecode, ); + const commandBuilder = await commandBuilderFactory.deploy(); + + const vaultWeirollUSDCFactory = await ethers.getContractFactory( + VaultWeiroll__factory.abi, + linkLibrary( + VaultWeiroll__factory.bytecode, + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", + commandBuilder.address, + ), + ); + vaultWeirollUSDC = await vaultWeirollUSDCFactory.deploy( + usdc.address, + await usdc.decimals(), + await usdc.name(), + await usdc.symbol(), + ); + testVault = await deployContract(this.signers.deployer, await artifacts.readArtifact("TestVault")); - usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); - cUSDC = await ethers.getContractAt(ICompound__factory.abi, "0x39AA39c021dfbaE8faC545936693aC917d5E7563"); - cToken = await ethers.getContractAt(ERC20__factory.abi, "0x39AA39c021dfbaE8faC545936693aC917d5E7563"); + cUSDC = await ethers.getContractAt(ICompound__factory.abi, "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"); + cToken = await ethers.getContractAt(ERC20__factory.abi, "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"); compoundAdapter = ( await ethers.getContractAt(CompoundAdapter__factory.abi, "0x9680624ad6bf5a34ce496a483400585136c575a4") ); await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); - await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("20", "6")); + await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("20", await usdc.decimals())); await vaultWeirollUSDC.connect(this.signers.alice).giveAllowances([usdc.address], [cUSDC.address]); compoundContract = weirollContract.createContract(cUSDC); cTokenContract = weirollContract.createContract(cToken); @@ -68,7 +84,10 @@ describe("VaultWeiroll", async function () { it("userDepositVault", async function () { // oraValueUT planner const oraValueUTPlanner = new weirollPlanner(); - const lpTokenBalance = oraValueUTPlanner.add(cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall()); + const lpTokenBalance = oraValueUTPlanner.add( + // cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall() + testVaultContract.getERC20Balance(vaultWeirollUSDC.address, cTokenContract.address).staticcall(), + ); const amountInToken = oraValueUTPlanner.add( compoundAdapterContract.getSomeAmountInToken(usdc.address, cUSDC.address, lpTokenBalance).staticcall(), ); @@ -78,7 +97,8 @@ describe("VaultWeiroll", async function () { // lastStepBalanceLP planner const lastStepBalanceLPPlanner = new weirollPlanner(); const lastStepBalance = lastStepBalanceLPPlanner.add( - cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall(), + // cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall(), + testVaultContract.getERC20Balance(vaultWeirollUSDC.address, cTokenContract.address).staticcall(), ); lastStepBalanceLPPlanner.add(testVaultContract.pureFunctionUint256(lastStepBalance).staticcall()); await vaultWeirollUSDC.setLastStepBalanceLP( @@ -129,15 +149,17 @@ describe("VaultWeiroll", async function () { ); // user deposit no strategy - await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("20", "6"))) + await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("20", await usdc.decimals()))) .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("20", "6")); + .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("20", await usdc.decimals())); // user withdraw no strategy - await expect(vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("10", "6"))) + await expect( + vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("10", await usdc.decimals())), + ) .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs(this.signers.alice.address, ethers.constants.AddressZero, parseUnits("10", "6")); - expect(await await usdc.balanceOf(this.signers.alice.address)).to.eq(parseUnits("10", "6")); + .withArgs(this.signers.alice.address, ethers.constants.AddressZero, parseUnits("10", await usdc.decimals())); + expect(await await usdc.balanceOf(this.signers.alice.address)).to.eq(parseUnits("10", await usdc.decimals())); // deposit all to strategy await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); @@ -149,10 +171,14 @@ describe("VaultWeiroll", async function () { console.log("price per full share ", (await vaultWeirollUSDC.getPPS()).toString()); // 2. user deposit - await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("10", "6")); - await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("10", "6"))) + await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("10", await usdc.decimals())); + await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("10", await usdc.decimals()))) .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("10.000001", "6")); + .withArgs( + ethers.constants.AddressZero, + this.signers.alice.address, + parseUnits("10.000001", await usdc.decimals()), + ); // 2. deposit all to strategy await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); @@ -165,9 +191,11 @@ describe("VaultWeiroll", async function () { // 2. user withdraw console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); - await expect(await vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("5", "6"))) + await expect( + await vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("5", await usdc.decimals())), + ) .to.emit(usdc, "Transfer") - .withArgs(vaultWeirollUSDC.address, this.signers.alice.address, parseUnits("4.999999", "6")); + .withArgs(vaultWeirollUSDC.address, this.signers.alice.address, parseUnits("4.999999", await usdc.decimals())); console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); console.log("PPS ", (await vaultWeirollUSDC.getPPS()).toString()); console.log("Total Supply ", (await vaultWeirollUSDC.totalSupply()).toString()); @@ -180,3 +208,18 @@ describe("VaultWeiroll", async function () { console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); }); }); + +// reference : https://github.com/ethers-io/ethers.js/issues/195#issuecomment-1212815642 +function linkLibrary(bytecode: string, name: string, address: string): string { + let linkedBytecode = bytecode; + // eslint-disable-next-line no-useless-escape + const placeholder = `__\$${ethers.utils.solidityKeccak256(["string"], [name]).slice(2, 36)}\$__`; + const formattedAddress = ethers.utils.getAddress(address).toLowerCase().replace("0x", ""); + if (linkedBytecode.indexOf(placeholder) === -1) { + throw new Error(`Unable to find placeholder for library ${name}`); + } + while (linkedBytecode.indexOf(placeholder) !== -1) { + linkedBytecode = linkedBytecode.replace(placeholder, formattedAddress); + } + return linkedBytecode; +} From 7463278697215186aaf64130101ef8fa7fadf8bf Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 2 Jan 2023 17:39:23 -0500 Subject: [PATCH 37/44] refactor(test): split helper contracts --- .../adapters/ethereum/AaveV1Helper.sol | 49 ++ .../adapters/ethereum/CompoundHelper.sol | 62 ++ .../adapters/ethereum/CurveHelper.sol | 239 +++++++ .../protocol/adapters/ethereum/SwapHelper.sol | 46 ++ .../protocol/tokenization/VaultHelper.sol | 37 ++ .../tokenization/VaultHelperMainnet.sol | 131 ---- .../005_deploy_vaulthelper.ts | 23 +- deploy_mainnet/002_deploy_aavev1helper.ts | 64 ++ deploy_mainnet/002_deploy_compoundhelper.ts | 65 ++ deploy_mainnet/002_deploy_curvehelper.ts | 63 ++ deploy_mainnet/002_deploy_swaphelper.ts | 65 ++ deploy_mainnet/003_deploy_opWETH-Save.ts | 33 +- helpers/adapters/ethereum/AaveV1Adapter.ts | 31 +- helpers/adapters/ethereum/AaveV2Adapter.ts | 41 +- helpers/adapters/ethereum/CompoundAdapter.ts | 62 +- helpers/adapters/ethereum/CurveAdapter.ts | 585 ++++++++++++++++++ helpers/strategy-manager.ts | 35 +- helpers/utils.ts | 11 +- ...9_usdc-mim-sushi_weth-ldo-sushi_harvest.ts | 191 ++++-- .../harvest/010_harvest_wbtc-damm_3crv-mim.ts | 169 ++--- test/test-opty/3_vault_strategies.spec.ts | 153 ++--- test/test-opty/utils.ts | 10 +- 22 files changed, 1686 insertions(+), 479 deletions(-) create mode 100644 contracts/protocol/adapters/ethereum/AaveV1Helper.sol create mode 100644 contracts/protocol/adapters/ethereum/CompoundHelper.sol create mode 100644 contracts/protocol/adapters/ethereum/CurveHelper.sol create mode 100644 contracts/protocol/adapters/ethereum/SwapHelper.sol create mode 100644 contracts/protocol/tokenization/VaultHelper.sol delete mode 100644 contracts/protocol/tokenization/VaultHelperMainnet.sol rename deploy_mainnet/002_deploy_vaulthelpermainnet.ts => deploy/005_deploy_vaulthelper.ts (69%) create mode 100644 deploy_mainnet/002_deploy_aavev1helper.ts create mode 100644 deploy_mainnet/002_deploy_compoundhelper.ts create mode 100644 deploy_mainnet/002_deploy_curvehelper.ts create mode 100644 deploy_mainnet/002_deploy_swaphelper.ts create mode 100644 helpers/adapters/ethereum/CurveAdapter.ts diff --git a/contracts/protocol/adapters/ethereum/AaveV1Helper.sol b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol new file mode 100644 index 00000000..4726535e --- /dev/null +++ b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; +import { ILendingPool } from "@optyfi/defi-legos/ethereum/aave/contracts/ILendingPool.sol"; +import { IAaveV1Token } from "@optyfi/defi-legos/ethereum/aave/contracts/IAaveV1Token.sol"; + +contract AaveV1Helper { + using SafeERC20 for IERC20; + + IWETH public immutable WETH; + + // solhint-disable-next-line var-name-mixedcase + address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); + + constructor(IWETH _WETH) { + WETH = _WETH; + } + + function depositETH_AaveV1(address _lendingPool, address _lpToken, uint256 _amount) external { + uint256 _wethBalanceBefore = IERC20(address(WETH)).balanceOf(address(this)); + IERC20(address(WETH)).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _wethBalanceAfter = IERC20(address(WETH)).balanceOf(address(this)); + uint256 _actualAmount = _wethBalanceAfter - _wethBalanceBefore; + WETH.withdraw(_actualAmount); + uint256 _balanceBeforeLP = IERC20(_lpToken).balanceOf(address(this)); + ILendingPool(_lendingPool).deposit{ value: _actualAmount }(ETH, _actualAmount, uint16(0)); + uint256 _balanceAfterLP = IERC20(_lpToken).balanceOf(address(this)); + IERC20(_lpToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function withdrawETH_AaveV1(address _lpToken, uint256 _amount) external { + uint256 _lpTokenBalanceBefore = IERC20(_lpToken).balanceOf(address(this)); + IERC20(_lpToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _lpTokenBalanceAfter = IERC20(_lpToken).balanceOf(address(this)); + uint256 _actualAmount = _lpTokenBalanceAfter - _lpTokenBalanceBefore; + uint256 _balanceBeforeETH = address(this).balance; + IAaveV1Token(_lpToken).redeem(_actualAmount); + uint256 _balanceAfterETH = address(this).balance; + WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); + IERC20(address(WETH)).safeTransfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); + } + + receive() external payable {} +} diff --git a/contracts/protocol/adapters/ethereum/CompoundHelper.sol b/contracts/protocol/adapters/ethereum/CompoundHelper.sol new file mode 100644 index 00000000..fbaa5e9c --- /dev/null +++ b/contracts/protocol/adapters/ethereum/CompoundHelper.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { ICompound } from "@optyfi/defi-legos/ethereum/compound/contracts/ICompound.sol"; +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; + +contract CompoundHelper { + using SafeERC20 for IERC20; + + IWETH public immutable WETH; + + constructor(IWETH _WETH, IERC20 cETH) { + WETH = _WETH; + cETH.safeApprove(address(cETH),type(uint256).max); + } + + function calculateAmountInToken_Compound(address _liquidityPool, uint256 _liquidityPoolTokenAmount) + external + view + returns (uint256) + { + return (_liquidityPoolTokenAmount * ICompound(_liquidityPool).exchangeRateStored()) / 1e18; + } + + function calculateAmountInLPToken_Compound(address _liquidityPool, uint256 _depositAmount) + external + view + returns (uint256) + { + return (_depositAmount * 1e18) / ICompound(_liquidityPool).exchangeRateStored(); + } + + function depositETH_Compound(address _liquidityPool, uint256 _amount) external { + uint256 _wethBalanceBefore = IERC20(address(WETH)).balanceOf(address(this)); + IERC20(address(WETH)).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _wethBalanceAfter = IERC20(address(WETH)).balanceOf(address(this)); + uint256 _actualAmount = _wethBalanceAfter - _wethBalanceBefore; + WETH.withdraw(_actualAmount); + uint256 _balanceBeforeLP = IERC20(_liquidityPool).balanceOf(address(this)); + ICompound(_liquidityPool).mint{ value: _actualAmount }(); + uint256 _balanceAfterLP = IERC20(_liquidityPool).balanceOf(address(this)); + IERC20(_liquidityPool).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function withdrawETH_Compound(address _liquidityPool, uint256 _amount) external { + uint256 _lpTokenBalanceBefore = IERC20(_liquidityPool).balanceOf(address(this)); + IERC20(_liquidityPool).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _lpTokenBalanceAfter = IERC20(_liquidityPool).balanceOf(address(this)); + uint256 _actualAmount = _lpTokenBalanceAfter - _lpTokenBalanceBefore; + uint256 _balanceBeforeETH = address(this).balance; + ICompound(_liquidityPool).redeem(_actualAmount); + uint256 _balanceAfterETH = address(this).balance; + WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); + IERC20(address(WETH)).safeTransfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); + } + + receive() external payable {} +} diff --git a/contracts/protocol/adapters/ethereum/CurveHelper.sol b/contracts/protocol/adapters/ethereum/CurveHelper.sol new file mode 100644 index 00000000..8de53459 --- /dev/null +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -0,0 +1,239 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; +import { ICurveSwap } from "@optyfi/defi-legos/ethereum/curve/contracts/interfacesV0/ICurveSwap.sol"; + + +contract CurveHelper { + using SafeERC20 for IERC20; + + function addLiquidity_two_coin_zero_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([_actualAmount, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_two_coin_one_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, _actualAmount], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_three_coin_zero_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([_actualAmount, 0, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_three_coin_one_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, _actualAmount, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_three_coin_two_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, 0, _actualAmount], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_four_coin_zero_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([_actualAmount, 0, 0, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_four_coin_one_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, _actualAmount, 0, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_four_coin_two_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, 0, _actualAmount, 0], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function addLiquidity_four_coin_three_index_Curve( + address _underlyingToken, + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount + ) external payable { + uint256 _underlyingTokenBalanceBefore = IERC20(_underlyingToken).balanceOf(address(this)); + IERC20(_underlyingToken).safeTransferFrom(msg.sender, address(this), _amount); + uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); + uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; + uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + ICurveSwap(_pool).add_liquidity([0, 0, 0, _actualAmount], _minMintAmount); + uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); + } + + function getMintAmount_two_coin_zero_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([_amount, 0], _isDeposit); + } + + function getMintAmount_two_coin_one_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0, _amount], _isDeposit); + } + + function getMintAmount_three_coin_zero_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([_amount,0,0], _isDeposit); + } + + function getMintAmount_three_coin_one_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0,_amount,0], _isDeposit); + } + + function getMintAmount_three_coin_two_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0,0,_amount], _isDeposit); + } + + function getMintAmount_four_coin_zero_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([_amount,0,0,0], _isDeposit); + } + + function getMintAmount_four_coin_one_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0,_amount,0,0], _isDeposit); + } + + function getMintAmount_four_coin_two_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0,0,_amount,0], _isDeposit); + } + + function getMintAmount_four_coin_three_index_Curve( + address _pool, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + return ICurveSwap(_pool).calc_token_amount([0,0,0,_amount], _isDeposit); + } +} diff --git a/contracts/protocol/adapters/ethereum/SwapHelper.sol b/contracts/protocol/adapters/ethereum/SwapHelper.sol new file mode 100644 index 00000000..183d51f7 --- /dev/null +++ b/contracts/protocol/adapters/ethereum/SwapHelper.sol @@ -0,0 +1,46 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { ISwapRouter } from "../../../interfaces/uniswap/ISwapRouter.sol"; + +// libraries +import { BytesLib } from "../../../utils/BytesLib.sol"; + +contract SwapHelper { + using BytesLib for bytes; + using SafeERC20 for IERC20; + + ISwapRouter public immutable SwapRouter; + + // solhint-disable-next-line var-name-mixedcase + address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); + + constructor(ISwapRouter _SwapRouter) { + SwapRouter = _SwapRouter; + } + + function exactInput_UniswapV3( + bytes memory _path, + uint256 _deadline, + uint256 _amountIn, + uint256 _amountOutMinimum + ) external { + address _inputToken = _path.toAddress(0); + uint256 _amountInBefore = IERC20(_inputToken).balanceOf(address(this)); + IERC20(_inputToken).safeTransferFrom(msg.sender, address(this), _amountIn); + uint256 _amountInAfter = IERC20(_inputToken).balanceOf(address(this)); + SwapRouter.exactInput( + ISwapRouter.ExactInputParams({ + path: _path, + recipient: msg.sender, + deadline: _deadline, + amountIn: _amountInAfter - _amountInBefore, + amountOutMinimum: _amountOutMinimum + }) + ); + } +} diff --git a/contracts/protocol/tokenization/VaultHelper.sol b/contracts/protocol/tokenization/VaultHelper.sol new file mode 100644 index 00000000..925bdda1 --- /dev/null +++ b/contracts/protocol/tokenization/VaultHelper.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier:MIT + +pragma solidity =0.8.11; + +// interfaces +import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; +import { IOptyFiOracle } from "../../utils/optyfi-oracle/contracts/interfaces/IOptyFiOracle.sol"; +import { IERC20Metadata } from "@openzeppelin/contracts-0.8.x/token/ERC20/extensions/IERC20Metadata.sol"; + +// libraries + +contract VaultHelper { + function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { + return _arg; + } + + function getTokenOutPrice_OptyFiOracle( + IOptyFiOracle _optyFiOracle, + address _tokenIn, + address _tokenOut, + uint256 _amountIn + ) external view returns (uint256) { + uint256 _price = _optyFiOracle.getTokenPrice(_tokenIn, _tokenOut); + require(_price > uint256(0), "!price"); + uint256 decimalsIn = uint256(IERC20Metadata(_tokenIn).decimals()); + uint256 decimalsOut = uint256(IERC20Metadata(_tokenOut).decimals()); + return (_amountIn * _price * 10**decimalsOut) / 10**(18 + decimalsIn); + } + + function getMinimumExpectedTokenOutPrice(uint256 _amountOut, uint256 _slippage) external pure returns (uint256) { + return (_amountOut * (10000 - _slippage)) / 10000; + } + + function getERC20Balance(IERC20 _token, address _owner) external view returns (uint256) { + return _token.balanceOf(_owner); + } +} diff --git a/contracts/protocol/tokenization/VaultHelperMainnet.sol b/contracts/protocol/tokenization/VaultHelperMainnet.sol deleted file mode 100644 index 43088619..00000000 --- a/contracts/protocol/tokenization/VaultHelperMainnet.sol +++ /dev/null @@ -1,131 +0,0 @@ -// SPDX-License-Identifier:MIT - -pragma solidity =0.8.11; - -// interfaces -import { ICompound } from "@optyfi/defi-legos/ethereum/compound/contracts/ICompound.sol"; -import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; -import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; -import { ISwapRouter } from "../../interfaces/uniswap/ISwapRouter.sol"; -import { IOptyFiOracle } from "../../utils/optyfi-oracle/contracts/interfaces/IOptyFiOracle.sol"; -import { IERC20Metadata } from "@openzeppelin/contracts-0.8.x/token/ERC20/extensions/IERC20Metadata.sol"; -import { ILendingPool } from "@optyfi/defi-legos/ethereum/aave/contracts/ILendingPool.sol"; -import { IAaveV1Token } from "@optyfi/defi-legos/ethereum/aave/contracts/IAaveV1Token.sol"; - -// libraries -import { BytesLib } from "../../utils/BytesLib.sol"; - -contract VaultHelperMainnet { - using BytesLib for bytes; - - IWETH public immutable WETH; - ISwapRouter public immutable SwapRouter; - - // solhint-disable-next-line var-name-mixedcase - address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - - constructor(IWETH _WETH, ISwapRouter _SwapRouter) { - WETH = _WETH; - SwapRouter = _SwapRouter; - } - - function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { - return _arg; - } - - function calculateAmountInToken_Compound(address _liquidityPool, uint256 _liquidityPoolTokenAmount) - external - view - returns (uint256) - { - return (_liquidityPoolTokenAmount * ICompound(_liquidityPool).exchangeRateStored()) / 1e18; - } - - function calculateAmountInLPToken_Compound(address _liquidityPool, uint256 _depositAmount) - external - view - returns (uint256) - { - return (_depositAmount * 1e18) / ICompound(_liquidityPool).exchangeRateStored(); - } - - function depositETH_Compound(address _liquidityPool, uint256 _amount) external { - IERC20(address(WETH)).transferFrom(msg.sender, address(this), _amount); - WETH.withdraw(_amount); - uint256 _balanceBeforeLP = IERC20(_liquidityPool).balanceOf(address(this)); - ICompound(_liquidityPool).mint{ value: _amount }(); - uint256 _balanceAfterLP = IERC20(_liquidityPool).balanceOf(address(this)); - IERC20(_liquidityPool).transfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); - } - - function withdrawETH_Compound(address _liquidityPool, uint256 _amount) external { - uint256 _balanceBeforeETH = address(this).balance; - IERC20(_liquidityPool).transferFrom(msg.sender, address(this), _amount); - ICompound(_liquidityPool).redeem(_amount); - uint256 _balanceAfterETH = address(this).balance; - WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); - IERC20(address(WETH)).transfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); - } - - function depositETH_AaveV1( - address _lendingPool, - address _lpToken, - uint256 _amount - ) external { - IERC20(address(WETH)).transferFrom(msg.sender, address(this), _amount); - WETH.withdraw(_amount); - uint256 _balanceBeforeLP = IERC20(_lpToken).balanceOf(address(this)); - ILendingPool(_lendingPool).deposit{ value: _amount }(ETH, _amount, uint16(0)); - uint256 _balanceAfterLP = IERC20(_lpToken).balanceOf(address(this)); - IERC20(_lpToken).transfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); - } - - function withdrawETH_AaveV1(address _lpToken, uint256 _amount) external { - uint256 _balanceBeforeETH = address(this).balance; - IERC20(_lpToken).transferFrom(msg.sender, address(this), _amount); - IAaveV1Token(_lpToken).redeem(_amount); - uint256 _balanceAfterETH = address(this).balance; - WETH.deposit{ value: _balanceAfterETH - _balanceBeforeETH }(); - IERC20(address(WETH)).transfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); - } - - function exactInput_UniswapV3( - bytes memory _path, - uint256 _deadline, - uint256 _amountIn, - uint256 _amountOutMinimum - ) external payable { - address _inputToken = _path.toAddress(0); - IERC20(_inputToken).transferFrom(msg.sender, address(this), _amountIn); - SwapRouter.exactInput( - ISwapRouter.ExactInputParams({ - path: _path, - recipient: msg.sender, - deadline: _deadline, - amountIn: _amountIn, - amountOutMinimum: _amountOutMinimum - }) - ); - } - - function getMinimumExpectedTokenOutPrice_OptyFiOracle( - IOptyFiOracle _optyFiOracle, - address _tokenIn, - address _tokenOut, - uint256 _amountIn, - uint256 _slippage - ) external view returns (uint256) { - uint256 _price = _optyFiOracle.getTokenPrice(_tokenIn, _tokenOut); - require(_price > uint256(0), "!price"); - uint256 decimalsIn = uint256(IERC20Metadata(_tokenIn).decimals()); - uint256 decimalsOut = uint256(IERC20Metadata(_tokenOut).decimals()); - uint256 _amountOut = (_amountIn * _price * 10**decimalsOut) / 10**(18 + decimalsIn); - return (_amountOut * (10000 - _slippage)) / 10000; - } - - function getERC20Balance(IERC20 _token, address _owner) external view returns (uint256) { - return _token.balanceOf(_owner); - } - - receive() external payable {} -} diff --git a/deploy_mainnet/002_deploy_vaulthelpermainnet.ts b/deploy/005_deploy_vaulthelper.ts similarity index 69% rename from deploy_mainnet/002_deploy_vaulthelpermainnet.ts rename to deploy/005_deploy_vaulthelper.ts index c330dcdc..8b820f46 100644 --- a/deploy_mainnet/002_deploy_vaulthelpermainnet.ts +++ b/deploy/005_deploy_vaulthelper.ts @@ -1,13 +1,10 @@ import { BigNumber } from "ethers"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { waitforme } from "../helpers/utils"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; -const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; - const func: DeployFunction = async ({ deployments, getNamedAccounts, @@ -19,19 +16,19 @@ const func: DeployFunction = async ({ }: HardhatRuntimeEnvironment) => { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact("VaultHelperMainnet"); + const artifact = await deployments.getArtifact("VaultHelper"); const chainId = await getChainId(); const networkName = network.name; const feeData = await ethers.provider.getFeeData(); - const result = await deploy("VaultHelperMainnet", { + const result = await deploy("VaultHelper", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], + args: [], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -40,27 +37,27 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const vaultHelperMainnet = await deployments.get("VaultHelperMainnet"); + const vaultHelperMainnet = await deployments.get("VaultHelper"); if (networkName === "tenderly") { await tenderly.verify({ - name: "VaultHelperMainnet", + name: "VaultHelper", address: vaultHelperMainnet.address, - constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], + constructorArguments: [], contract: "VaultHelperMainnet", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "VaultHelperMainnet", + name: "VaultHelper", address: vaultHelperMainnet.address, - constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, uniswapV3Router], - contract: "VaultHelperMainnet", + constructorArguments: [], + contract: "VaultHelper", }); } } } }; export default func; -func.tags = ["VaultHelperMainnet"]; +func.tags = ["VaultHelper"]; func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/002_deploy_aavev1helper.ts b/deploy_mainnet/002_deploy_aavev1helper.ts new file mode 100644 index 00000000..fa232c44 --- /dev/null +++ b/deploy_mainnet/002_deploy_aavev1helper.ts @@ -0,0 +1,64 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { waitforme } from "../helpers/utils"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact("AaveV1Helper"); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + const result = await deploy("AaveV1Helper", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [EthereumTokens.WRAPPED_TOKENS.WETH], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const aavev1helper = await deployments.get("AaveV1Helper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "AaveV1Helper", + address: aavev1helper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH], + contract: "AaveV1Helper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "AaveV1Helper", + address: aavev1helper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH], + contract: "AaveV1Helper", + }); + } + } + } +}; +export default func; +func.tags = ["AaveV1Helper"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/002_deploy_compoundhelper.ts b/deploy_mainnet/002_deploy_compoundhelper.ts new file mode 100644 index 00000000..2b37ffc7 --- /dev/null +++ b/deploy_mainnet/002_deploy_compoundhelper.ts @@ -0,0 +1,65 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; +import { waitforme } from "../helpers/utils"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact("CompoundHelper"); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + const result = await deploy("CompoundHelper", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [EthereumTokens.WRAPPED_TOKENS.WETH, Compound.pools.eth.lpToken], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const compoundhelper = await deployments.get("CompoundHelper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "CompoundHelper", + address: compoundhelper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, Compound.pools.eth.lpToken], + contract: "CompoundHelper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "CompoundHelper", + address: compoundhelper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, Compound.pools.eth.lpToken], + contract: "CompoundHelper", + }); + } + } + } +}; +export default func; +func.tags = ["CompoundHelper"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/002_deploy_curvehelper.ts b/deploy_mainnet/002_deploy_curvehelper.ts new file mode 100644 index 00000000..1bab4b3d --- /dev/null +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -0,0 +1,63 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { waitforme } from "../helpers/utils"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact("CurveHelper"); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + const result = await deploy("CurveHelper", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const curvehelper = await deployments.get("CurveHelper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "CurveHelper", + address: curvehelper.address, + constructorArguments: [], + contract: "CurveHelper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "CurveHelper", + address: curvehelper.address, + constructorArguments: [], + contract: "CurveHelper", + }); + } + } + } +}; +export default func; +func.tags = ["CurveHelper"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/002_deploy_swaphelper.ts b/deploy_mainnet/002_deploy_swaphelper.ts new file mode 100644 index 00000000..ff993505 --- /dev/null +++ b/deploy_mainnet/002_deploy_swaphelper.ts @@ -0,0 +1,65 @@ +import { BigNumber } from "ethers"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { waitforme } from "../helpers/utils"; + +const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; + +const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; + +const func: DeployFunction = async ({ + deployments, + getNamedAccounts, + getChainId, + ethers, + network, + tenderly, + run, +}: HardhatRuntimeEnvironment) => { + const { deploy } = deployments; + const { deployer } = await getNamedAccounts(); + const artifact = await deployments.getArtifact("SwapHelper"); + const chainId = await getChainId(); + const networkName = network.name; + + const feeData = await ethers.provider.getFeeData(); + const result = await deploy("SwapHelper", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [uniswapV3Router], + log: true, + skipIfAlreadyDeployed: true, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const swapHelper = await deployments.get("SwapHelper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "SwapHelper", + address: swapHelper.address, + constructorArguments: [uniswapV3Router], + contract: "SwapHelper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "SwapHelper", + address: swapHelper.address, + constructorArguments: [uniswapV3Router], + contract: "SwapHelper", + }); + } + } + } +}; +export default func; +func.tags = ["SwapHelper"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/003_deploy_opWETH-Save.ts b/deploy_mainnet/003_deploy_opWETH-Save.ts index 983495f9..7030ffa0 100644 --- a/deploy_mainnet/003_deploy_opWETH-Save.ts +++ b/deploy_mainnet/003_deploy_opWETH-Save.ts @@ -9,7 +9,15 @@ import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { ERC20, ERC20__factory, Vault, VaultHelperMainnet__factory, Vault__factory } from "../typechain"; +import { + CompoundHelper, + CompoundHelper__factory, + ERC20, + ERC20__factory, + Vault, + VaultHelper__factory, + Vault__factory, +} from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -161,11 +169,8 @@ const func: DeployFunction = async ({ await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) ); - const vaultHelperMainnetInstance = await ethers.getContractAt( - VaultHelperMainnet__factory.abi, - ( - await deployments.get("VaultHelperMainnet") - ).address, + const compoundHelperInstance = ( + await ethers.getContractAt(CompoundHelper__factory.abi, (await deployments.get("CompoundHelper")).address) ); const approvalTokens = []; @@ -177,13 +182,13 @@ const func: DeployFunction = async ({ const wethAaveV2Allowance = await wethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); const awethAaveV2Allowance = await awethInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); - const wethVaultHelperMainnetAllowance = await wethInstance.allowance( + const wethCompoundHelperAllowance = await wethInstance.allowance( vaultInstance.address, - vaultHelperMainnetInstance.address, + compoundHelperInstance.address, ); - const cethVaultHelperMainnetAllowance = await cethInstance.allowance( + const cethCompoundHelperAllowance = await cethInstance.allowance( vaultInstance.address, - vaultHelperMainnetInstance.address, + compoundHelperInstance.address, ); if (!wethAaveV2Allowance.gt(parseEther("1000000"))) { @@ -196,14 +201,14 @@ const func: DeployFunction = async ({ approvalSpender.push(AaveV2.LendingPool.address); } - if (!wethVaultHelperMainnetAllowance.gt(parseEther("1000000"))) { + if (!wethCompoundHelperAllowance.gt(parseEther("1000000"))) { approvalTokens.push(wethInstance.address); - approvalSpender.push(vaultHelperMainnetInstance.address); + approvalSpender.push(compoundHelperInstance.address); } - if (!cethVaultHelperMainnetAllowance.gt(parseEther("1000000"))) { + if (!cethCompoundHelperAllowance.gt(parseEther("1000000"))) { approvalTokens.push(cethInstance.address); - approvalSpender.push(vaultHelperMainnetInstance.address); + approvalSpender.push(compoundHelperInstance.address); } if (approvalTokens.length > 0) { diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index 919325f1..2b4868b3 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -8,11 +8,15 @@ import { ReturnValue } from "../../type"; import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; export class AaveV1Adapter implements AdapterInterface { - vaultHelperMainnetInstance; + vaultHelperInstance; + aaveV1HelperInstance; - constructor(vaultHelperMainnetContract: Contract) { - this.vaultHelperMainnetInstance = weirollContract.createContract( - new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), + constructor(vaultHelperContract: Contract, aaveV1HelperInstance: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.aaveV1HelperInstance = weirollContract.createContract( + new ethers.Contract(aaveV1HelperInstance.address, aaveV1HelperInstance.interface), ); } @@ -29,7 +33,7 @@ export class AaveV1Adapter implements AdapterInterface { const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { planner.add( - this.vaultHelperMainnetInstance["depositETH_AaveV1(address,address,uint256)"]( + this.aaveV1HelperInstance["depositETH_AaveV1(address,address,uint256)"]( lendingPoolContract.address, outputToken, inputTokenAmount, @@ -53,9 +57,7 @@ export class AaveV1Adapter implements AdapterInterface { const lpTokenContract = new ethers.Contract(outputToken, Aave.ATokenAbi); const lpTokenInstance = weirollContract.createContract(lpTokenContract); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - planner.add( - this.vaultHelperMainnetInstance["withdrawETH_AaveV1(address,uint256)"](outputToken, outputTokenAmount), - ); + planner.add(this.aaveV1HelperInstance["withdrawETH_AaveV1(address,uint256)"](outputToken, outputTokenAmount)); } else { planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); } @@ -95,10 +97,7 @@ export class AaveV1Adapter implements AdapterInterface { _isSwap: boolean, ): ReturnValue { const amountLP = planner.add( - this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( - outputToken, - vaultInstance.address, - ).staticcall(), + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), ); return amountLP as ReturnValue; } @@ -129,12 +128,12 @@ export class AaveV1Adapter implements AdapterInterface { _isSwap: boolean, provider: JsonRpcProvider, ): Promise { - const vaultHelperMainnet = new ethers.Contract( - this.vaultHelperMainnetInstance.address, - this.vaultHelperMainnetInstance.interface, + const vaultHelper = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, provider, ); - return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + return await vaultHelper["getERC20Balance(address,address)"](outputToken, vaultInstance.address); } async getValueInInputToken( diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index ee1754ca..9bdad15e 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -11,12 +11,16 @@ import { JsonRpcProvider } from "@ethersproject/providers"; const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; export class Aavev2Adapter implements AdapterInterface { optyFiOracleAddress; - vaultHelperMainnetInstance; + vaultHelperInstance; + swapHelperInstance; - constructor(vaultHelperMainnetContract: Contract, _optyFiOracleAddress: string) { + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, swapHelperContract: Contract) { this.optyFiOracleAddress = _optyFiOracleAddress; - this.vaultHelperMainnetInstance = weirollContract.createContract( - new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.swapHelperInstance = weirollContract.createContract( + new ethers.Contract(swapHelperContract.address, swapHelperContract.interface), ); } @@ -92,10 +96,7 @@ export class Aavev2Adapter implements AdapterInterface { _isSwap: boolean, ): ReturnValue { const amountLP = planner.add( - this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( - outputToken, - vaultInstance.address, - ).staticcall(), + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), ); return amountLP as ReturnValue; } @@ -126,12 +127,18 @@ export class Aavev2Adapter implements AdapterInterface { vaultUnderlyingToken: string, ): weirollPlanner { const rewardAmount = planner.add( - this.vaultHelperMainnetInstance["getERC20Balance(address,address)"](STK_AAVE, vaultInstance.address).staticcall(), + this.vaultHelperInstance["getERC20Balance(address,address)"](STK_AAVE, vaultInstance.address).staticcall(), + ); + const outputAmount = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( + this.optyFiOracleAddress, + STK_AAVE, + vaultUnderlyingToken, + rewardAmount, + ).staticcall(), ); - const minumumOutputAmount = planner.add( - this.vaultHelperMainnetInstance[ - "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" - ](this.optyFiOracleAddress, STK_AAVE, vaultUnderlyingToken, rewardAmount, 100).staticcall(), + const minimumOutputAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100), ); let univ3Path; switch (getAddress(vaultUnderlyingToken)) { @@ -207,11 +214,11 @@ export class Aavev2Adapter implements AdapterInterface { } } planner.add( - this.vaultHelperMainnetInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( + this.swapHelperInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( univ3Path, ethers.constants.MaxUint256, rewardAmount, - minumumOutputAmount, + minimumOutputAmount, ), ); return planner; @@ -226,8 +233,8 @@ export class Aavev2Adapter implements AdapterInterface { provider: JsonRpcProvider, ): Promise { const vaultHelperMainnet = new ethers.Contract( - this.vaultHelperMainnetInstance.address, - this.vaultHelperMainnetInstance.interface, + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, provider, ); return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index 0a131ed4..1230b67d 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -11,14 +11,18 @@ import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; export class CompoundAdapter implements AdapterInterface { - vaultHelperMainnetInstance; + vaultHelperInstance; optyFiOracleAddress; + compoundHelperInstance; - constructor(vaultHelperMainnetContract: Contract, _optyFiOracleAddress: string) { - this.vaultHelperMainnetInstance = weirollContract.createContract( - new ethers.Contract(vaultHelperMainnetContract.address, vaultHelperMainnetContract.interface), + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, compoundHelperContract: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), ); this.optyFiOracleAddress = _optyFiOracleAddress; + this.compoundHelperInstance = weirollContract.createContract( + new ethers.Contract(compoundHelperContract.address, compoundHelperContract.interface), + ); } getDepositPlan( @@ -32,7 +36,7 @@ export class CompoundAdapter implements AdapterInterface { ): weirollPlanner { const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - planner.add(this.vaultHelperMainnetInstance["depositETH_Compound(address,uint256)"](pool, inputTokenAmount)); + planner.add(this.compoundHelperInstance["depositETH_Compound(address,uint256)"](pool, inputTokenAmount)); } else { planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); } @@ -50,7 +54,7 @@ export class CompoundAdapter implements AdapterInterface { ): weirollPlanner { const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { - planner.add(this.vaultHelperMainnetInstance["withdrawETH_Compound(address,uint256)"](pool, outputTokenAmount)); + planner.add(this.compoundHelperInstance["withdrawETH_Compound(address,uint256)"](pool, outputTokenAmount)); } else { planner.add(poolInstance["redeem(uint256)"](outputTokenAmount)); } @@ -67,7 +71,7 @@ export class CompoundAdapter implements AdapterInterface { outputTokenAmount: ReturnValue, ): ReturnValue { const amountUT = planner.add( - this.vaultHelperMainnetInstance["calculateAmountInToken_Compound(address,uint256)"]( + this.compoundHelperInstance["calculateAmountInToken_Compound(address,uint256)"]( pool, outputTokenAmount, ).staticcall(), @@ -85,7 +89,7 @@ export class CompoundAdapter implements AdapterInterface { inputTokenAmount: ReturnValue, ): ReturnValue { const amountLP = planner.add( - this.vaultHelperMainnetInstance["calculateAmountInLPToken_Compound(address,uint256)"]( + this.compoundHelperInstance["calculateAmountInLPToken_Compound(address,uint256)"]( pool, inputTokenAmount, ).staticcall(), @@ -102,10 +106,7 @@ export class CompoundAdapter implements AdapterInterface { _isSwap: boolean, ): ReturnValue { const amountLP = planner.add( - this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( - outputToken, - vaultInstance.address, - ).staticcall(), + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), ); return amountLP as ReturnValue; } @@ -120,12 +121,7 @@ export class CompoundAdapter implements AdapterInterface { const comptrollerContract = new ethers.Contract(Compound.comptroller.address, comptrollerAbi); const comptrollerInstance = weirollContract.createContract(comptrollerContract); planner.add( - comptrollerInstance["claimComp(address[],address[],bool,bool)"]( - [vaultInstance.address], - [pool], - false, - true, - ).staticcall(), + comptrollerInstance["claimComp(address[],address[],bool,bool)"]([vaultInstance.address], [pool], false, true), ); return planner; } @@ -138,28 +134,28 @@ export class CompoundAdapter implements AdapterInterface { const uniswapV2RouterContract = new ethers.Contract(UniswapV2.router02.address, UniswapV2.router02.abi); const uniswapV2RouterInstance = weirollContract.createContract(uniswapV2RouterContract); const rewardAmount = planner.add( - this.vaultHelperMainnetInstance["getERC20Balance(address,address)"]( + this.vaultHelperInstance["getERC20Balance(address,address)"]( EthereumTokens.REWARD_TOKENS.COMP, vaultInstance.address, ).staticcall(), ); - const minumumOutputAmount = planner.add( - this.vaultHelperMainnetInstance[ - "getMinimumExpectedTokenOutPrice_OptyFiOracle(address,address,address,uint256,uint256)" - ]( + const outputAmount = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( this.optyFiOracleAddress, EthereumTokens.REWARD_TOKENS.COMP, vaultUnderlyingToken, rewardAmount, - 100, ).staticcall(), ); + const minimumOutputAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100).staticcall(), + ); switch (getAddress(vaultUnderlyingToken)) { case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - minumumOutputAmount, + minimumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], vaultInstance.address, ethers.constants.MaxUint256, @@ -171,7 +167,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - minumumOutputAmount, + minimumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH], vaultInstance.address, ethers.constants.MaxUint256, @@ -183,7 +179,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - minumumOutputAmount, + minimumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.DAI], vaultInstance.address, ethers.constants.MaxUint256, @@ -195,7 +191,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - minumumOutputAmount, + minimumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDT], vaultInstance.address, ethers.constants.MaxUint256, @@ -207,7 +203,7 @@ export class CompoundAdapter implements AdapterInterface { planner.add( uniswapV2RouterInstance["swapExactTokensForTokens(uint256,uint256,address[],address,uint256)"]( rewardAmount, - minumumOutputAmount, + minimumOutputAmount, [EthereumTokens.REWARD_TOKENS.COMP, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.BTC_TOKENS.WBTC], vaultInstance.address, ethers.constants.MaxUint256, @@ -230,12 +226,12 @@ export class CompoundAdapter implements AdapterInterface { _isSwap: boolean, provider: JsonRpcProvider, ): Promise { - const vaultHelperMainnet = new ethers.Contract( - this.vaultHelperMainnetInstance.address, - this.vaultHelperMainnetInstance.interface, + const vaultHelper = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, provider, ); - return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + return await vaultHelper["getERC20Balance(address,address)"](outputToken, vaultInstance.address); } async getValueInInputToken( diff --git a/helpers/adapters/ethereum/CurveAdapter.ts b/helpers/adapters/ethereum/CurveAdapter.ts new file mode 100644 index 00000000..4ebc7690 --- /dev/null +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -0,0 +1,585 @@ +import { BigNumber, Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import { ReturnValue } from "../../type"; +import { ICurveSwap__factory } from "../../../typechain"; +import { getAddress } from "ethers/lib/utils"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; + +export class CurveSwapPoolAdapter implements AdapterInterface { + vaultHelperInstance; + optyFiOracleAddress; + curveHelperInstance; + + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, curveHelper: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.optyFiOracleAddress = _optyFiOracleAddress; + this.curveHelperInstance = weirollContract.createContract( + new ethers.Contract(curveHelper.address, curveHelper.interface), + ); + } + + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens).toNumber(); + switch (poolRegistry[pool].underlyingTokens.length) { + case 2: { + switch (tokenIndex) { + case 0: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_two_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_two_coin_zero_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 1: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_two_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_two_coin_one_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 3: { + switch (tokenIndex) { + case 0: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_three_coin_zero_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 1: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_three_coin_one_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 2: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_three_coin_two_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 4: { + switch (tokenIndex) { + case 0: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_four_coin_zero_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 1: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_four_coin_one_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 2: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_four_coin_two_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + case 3: { + const mintAmount = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_three_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + const minimumMintAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + ); + planner.add( + this.curveHelperInstance[ + "addLiquidity_four_coin_three_index_Curve(address,address,address,uint256,uint256)" + ](inputToken, pool, outputToken, inputTokenAmount, minimumMintAmount), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + default: + throw new Error(`${pool} not found in registry`); + } + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveSwap__factory.abi)); + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens); + const withdrawAmount = planner.add( + poolInstance["calc_withdraw_one_coin(uint256,int128)"](outputTokenAmount, tokenIndex).staticcall(), + ); + const minimumWithdrawAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](withdrawAmount, 5), + ); + planner.add( + poolInstance["remove_liquidity_one_coin(uint256,int128,uint256)"]( + outputTokenAmount, + tokenIndex, + minimumWithdrawAmount, + ), + ); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveSwap__factory.abi)); + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens); + const amountUT = planner.add( + poolInstance["calc_withdraw_one_coin(uint256,int128)"](outputTokenAmount, tokenIndex).staticcall(), + ); + return amountUT as ReturnValue; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue { + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens).toNumber(); + let amountLP; + switch (poolRegistry[pool].underlyingTokens.length) { + case 2: { + switch (tokenIndex) { + case 0: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_two_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 1: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_two_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 3: { + switch (tokenIndex) { + case 0: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 1: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 2: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_three_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 4: { + switch (tokenIndex) { + case 0: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 1: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 2: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + case 3: { + amountLP = planner.add( + this.curveHelperInstance["getMintAmount_four_coin_three_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ).staticcall(), + ); + break; + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + default: + throw new Error(`${pool} not found in registry`); + } + return amountLP as ReturnValue; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + ): ReturnValue { + const amountLP = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), + ); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + _planner: weirollPlanner, + _vaultInstance: Contract, + _inputToken: string, + _pool: string, + _outputToken: string, + ): weirollPlanner { + throw new Error("not implemented"); + } + + getHarvestRewardsPlan( + _planner: weirollPlanner, + _vaultInstance: Contract, + _vaultUnderlyingToken: string, + ): weirollPlanner { + throw new Error("not implemented"); + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const poolInstance = new ethers.Contract(pool, ICurveSwap__factory.abi, provider); + return await poolInstance["calc_withdraw_one_coin(uint256,int128)"]( + outputTokenAmount, + this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens), + ); + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const curveHelper = new ethers.Contract( + this.curveHelperInstance.address, + this.curveHelperInstance.interface, + provider, + ); + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens).toNumber(); + switch (poolRegistry[pool].underlyingTokens.length) { + case 2: { + switch (tokenIndex) { + case 0: { + return await curveHelper["getMintAmount_two_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 1: { + return await curveHelper["getMintAmount_two_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 3: { + switch (tokenIndex) { + case 0: { + return await curveHelper["getMintAmount_three_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 1: { + return await curveHelper["getMintAmount_three_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 2: { + return await curveHelper["getMintAmount_three_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + case 4: { + switch (tokenIndex) { + case 0: { + return await curveHelper["getMintAmount_four_coin_zero_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 1: { + return await curveHelper["getMintAmount_four_coin_one_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 2: { + return await curveHelper["getMintAmount_four_coin_two_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + case 3: { + return await curveHelper["getMintAmount_four_coin_three_index_Curve(address,uint256,bool)"]( + pool, + inputTokenAmount, + true, + ); + } + default: + throw new Error(`invalid index for ${inputToken} of ${pool}`); + } + break; + } + default: + throw new Error(`${pool} not found in registry`); + } + } + + private getTokenIndex(token: string, underlyingTokens: string[]): BigNumber { + for (const [index, underlyingToken] of underlyingTokens) { + if (getAddress(token) === getAddress(underlyingToken)) { + return BigNumber.from(index); + } + } + throw new Error("no token index found"); + } +} + +const poolRegistry: { [key: string]: { underlyingTokens: string[] } } = { + "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7": { + underlyingTokens: [ + "0x6B175474E89094C44Da98b954EedeAC495271d0F", + "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + "0xdAC17F958D2ee523a2206206994597C13D831ec7", + ], + }, +}; diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index 7104d05f..a860c176 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -2,7 +2,6 @@ import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { BigNumber, Contract } from "ethers"; import { JsonRpcProvider } from "@ethersproject/providers"; -import { ERC20__factory } from "../typechain"; import { AdapterInterface } from "./adapters/AdapterInterface"; import { AaveV1Adapter } from "./adapters/ethereum/AaveV1Adapter"; import { Aavev2Adapter } from "./adapters/ethereum/AaveV2Adapter"; @@ -13,17 +12,23 @@ export class StrategyManager { public readonly compoundAdapterObj: AdapterInterface; public readonly aaveV1AdapterObj: AdapterInterface; public readonly aaveV2AdapterObj: AdapterInterface; - public readonly vaultHelperMainnetContract: weirollContract; + public readonly vaultHelperContract: weirollContract; public readonly liquidityPoolToAdapter: { [key: string]: AdapterInterface } = {}; - constructor(vaultHelperMainnetInstance: Contract, optyFiOracleAddress: string) { - this.vaultHelperMainnetContract = weirollContract.createContract( - new ethers.Contract(vaultHelperMainnetInstance.address, vaultHelperMainnetInstance.interface), + constructor( + optyFiOracleAddress: string, + vaultHelperInstance: Contract, + compoundHelperContract: Contract, + aaveV1HelperContract: Contract, + swapHelperContract: Contract, + ) { + this.vaultHelperContract = weirollContract.createContract( + new ethers.Contract(vaultHelperInstance.address, vaultHelperInstance.interface), ); - this.compoundAdapterObj = new CompoundAdapter(vaultHelperMainnetInstance, optyFiOracleAddress); - this.aaveV1AdapterObj = new AaveV1Adapter(vaultHelperMainnetInstance); - this.aaveV2AdapterObj = new Aavev2Adapter(vaultHelperMainnetInstance, optyFiOracleAddress); + this.compoundAdapterObj = new CompoundAdapter(optyFiOracleAddress, vaultHelperInstance, compoundHelperContract); + this.aaveV1AdapterObj = new AaveV1Adapter(vaultHelperInstance, aaveV1HelperContract); + this.aaveV2AdapterObj = new Aavev2Adapter(optyFiOracleAddress, vaultHelperInstance, swapHelperContract); this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; // cDAI this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; // cUSDT @@ -46,7 +51,7 @@ export class StrategyManager { if (index > 0) { const inputTokenAmount = ( planner.add( - this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + this.vaultHelperContract["getERC20Balance(address,address)"]( strategySteps[index - 1].outputToken, vaultContract.address, ).staticcall(), @@ -101,7 +106,7 @@ export class StrategyManager { } else { const amountLP = ( planner.add( - this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + this.vaultHelperContract["getERC20Balance(address,address)"]( strategySteps[iteratorIndex].outputToken, vaultContract.address, ).staticcall(), @@ -126,7 +131,7 @@ export class StrategyManager { const planner = new weirollPlanner(); const amountLP = ( planner.add( - this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + this.vaultHelperContract["getERC20Balance(address,address)"]( strategySteps[strategySteps.length - 1].outputToken, vaultInstance.address, ).staticcall(), @@ -166,7 +171,7 @@ export class StrategyManager { ); } } - planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } @@ -210,7 +215,7 @@ export class StrategyManager { ); } } - planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } @@ -222,12 +227,12 @@ export class StrategyManager { ): WeirollPlan { const planner = new weirollPlanner(); const amountLP = planner.add( - this.vaultHelperMainnetContract["getERC20Balance(address,address)"]( + this.vaultHelperContract["getERC20Balance(address,address)"]( strategySteps[strategySteps.length - 1].outputToken, vaultInstance.address, ).staticcall(), ); - planner.add(this.vaultHelperMainnetContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); const { commands, state } = planner.plan(); return { commands, state, outputIndex: 0 }; } diff --git a/helpers/utils.ts b/helpers/utils.ts index 729c537f..f9621d82 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -33,6 +33,10 @@ import { StrategyRegistry, ERC20__factory, VaultHelperMainnet, + VaultHelper, + AaveV1Helper, + CompoundHelper, + SwapHelper, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -108,7 +112,10 @@ declare module "mocha" { riskManagerV2: RiskManagerV2; vault: Vault; vaultProxy: InitializableImmutableAdminUpgradeabilityProxy; - vaultHelperMainnet: VaultHelperMainnet; + vaultHelper: VaultHelper; + aaveV1Helper: AaveV1Helper; + compoundHelper: CompoundHelper; + swapHelper: SwapHelper; opUSDCearn: Vault; opUSDCearnProxy: InitializableImmutableAdminUpgradeabilityProxy; opWETHearn: Vault; @@ -551,7 +558,7 @@ export async function assertPostUserWithdrawState( const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); expect(vaultBalanceAfterLP).to.closeTo( expectedVaultBalanceLP, - parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + parseUnits("9", Math.floor(((await outputTokenInstance.decimals()) * 3) / 4)).toNumber(), ); expect(vaultBalanceBeforeLP).gt(vaultBalanceAfterLP); } diff --git a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts index 011fd6aa..ebed7ccb 100644 --- a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -7,7 +7,7 @@ import EthersAdapter from "@gnosis.pm/safe-ethers-lib"; import Safe from "@gnosis.pm/safe-core-sdk"; import { MetaTransactionData, EthAdapter } from "@gnosis.pm/safe-core-sdk-types"; import { ERC20, ERC20__factory, IUniswapV2Router02, IUniswapV2Router02__factory } from "../../typechain"; -import { parseEther, parseUnits } from "ethers/lib/utils"; +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import { getCVXUnclaimed } from "./003_claim_and_swap_CRV_CVX_to_USDC"; import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; @@ -27,6 +27,11 @@ async function main() { const opWETHInvst = ( await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) ); + + const opWBTCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) + ); + const sushiswapRouter = ( await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) ); @@ -79,6 +84,8 @@ async function main() { const cvxMIM3lp3GaugeInstance = await ethers.getContractAt(cvxABI, Convex.pools.mim.stakingPool); + const crvWSBTCGaugeInstance = await ethers.getContractAt(curveGuageABI, "0x6D787113F23bED1D5e1530402B3f364D0A6e5Af3"); + const cvxStEthGaugeInstance = await ethers.getContractAt(cvxABI, Convex.pools.steth.stakingPool); const cvxInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.CVX); @@ -90,6 +97,11 @@ async function main() { const crvUnclaimedForopUSDCEarn = await cvxMIM3lp3GaugeInstance.earned(opUSDCEarn.address); const crvBalanceForopUSDCEarn = await crvInstance.balanceOf(opUSDCEarn.address); + const crvBalanceForopWBTCEarn = await crvInstance.balanceOf(opWBTCEarn.address); + + const crvUnclaimedForopWBTCEarn = await crvWSBTCGaugeInstance.claimable_tokens(opWBTCEarn.address); + + console.log(formatEther(crvUnclaimedForopWBTCEarn)); const cvxUnclaimedForopUSDCEarn = getCVXUnclaimed( crvUnclaimedForopUSDCEarn, @@ -100,7 +112,7 @@ async function main() { ); const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - const ldoUnclaimedForopWETHEarn = parseEther("13.310000000000000000"); + const ldoUnclaimedForopWETHEarn = parseEther("31.630000000000000000"); const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); @@ -111,6 +123,7 @@ async function main() { const oneLDOToWETH = parseEther("0.0007"); //0.0008 const oneSUSHIToUSDC = parseUnits("0.96", "6"); const oneSUSHIToWETH = parseEther("0.0007"); // sushi-dai-weth + const oneCRVToWBTC = parseUnits("0", "8"); const expectedMinUSDCFromCRVForopUSDCEarn = BigNumber.from(crvBalanceForopUSDCEarn.add(crvUnclaimedForopUSDCEarn)) .mul(oneCRVToUSDC) @@ -124,6 +137,10 @@ async function main() { .mul(oneCVXToUSDC) .div(parseEther("1")); + const expectedMinWBTCFromCRVForopWBTCEarn = BigNumber.from(crvBalanceForopWBTCEarn.add(crvUnclaimedForopWBTCEarn)) + .mul(oneCRVToWBTC) + .div(parseEther("1")); + const expectedMinUSDCFromSUSHIForopUSDCInvst = sushiBalanceForopUSDCInvst.mul(oneSUSHIToUSDC).div(parseEther("1")); const expectMinWETHFromSUSHIForopWETHInvst = sushiBalanceForopWETHInvst.mul(oneSUSHIToWETH).div(parseEther("1")); @@ -152,6 +169,12 @@ async function main() { value: "0", data: cvxStEthGaugeInstance.interface.encodeFunctionData("getReward", [opWETHEarn.address, true]), }, + // claim CRV for opWBTCEarn + { + to: crvWSBTCGaugeInstance.address, + value: "0", + data: crvWSBTCGaugeInstance.interface.encodeFunctionData("claim_rewards", [opWBTCEarn.address]), + }, // harvest CRV for opUSDCEarn { to: opUSDCEarn.address, @@ -199,44 +222,58 @@ async function main() { "0x", ]), }, - // harvest SUSHI for opWETHInvst - { - to: opWETHInvst.address, - value: "0", - data: opWETHInvst.interface.encodeFunctionData("harvest", [ - sushiInstance.address, - sushiswapRouter.address, - false, - expectMinWETHFromSUSHIForopWETHInvst, - BigNumber.from(timestamp).add("600"), - [ - sushiInstance.address, - EthereumTokens.PLAIN_TOKENS.USDT, - EthereumTokens.PLAIN_TOKENS.DAI, - EthereumTokens.WRAPPED_TOKENS.WETH, - ], - "0x", - ]), - }, - // harvest SUSHI for opUSDCInvst - { - to: opUSDCInvst.address, - value: "0", - data: opUSDCInvst.interface.encodeFunctionData("harvest", [ - sushiInstance.address, - sushiswapRouter.address, - false, - expectedMinUSDCFromSUSHIForopUSDCInvst, - BigNumber.from(timestamp).add("600"), - [ - sushiInstance.address, - EthereumTokens.PLAIN_TOKENS.USDT, - EthereumTokens.PLAIN_TOKENS.DAI, - EthereumTokens.PLAIN_TOKENS.USDC, - ], - "0x", - ]), - }, + // // harvest CRV for opWBTCEarn + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("harvest", [ + // crvInstance.address, + // "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + // false, + // "0", + // BigNumber.from(timestamp).add("600"), + // [crvInstance.address, EthereumTokens.PLAIN_TOKENS.USDC, EthereumTokens.BTC_TOKENS.WBTC], + // "0x", + // ]), + // }, + // // harvest SUSHI for opWETHInvst + // { + // to: opWETHInvst.address, + // value: "0", + // data: opWETHInvst.interface.encodeFunctionData("harvest", [ + // sushiInstance.address, + // sushiswapRouter.address, + // false, + // expectMinWETHFromSUSHIForopWETHInvst, + // BigNumber.from(timestamp).add("600"), + // [ + // sushiInstance.address, + // EthereumTokens.PLAIN_TOKENS.USDT, + // EthereumTokens.PLAIN_TOKENS.DAI, + // EthereumTokens.WRAPPED_TOKENS.WETH, + // ], + // "0x", + // ]), + // }, + // // harvest SUSHI for opUSDCInvst + // { + // to: opUSDCInvst.address, + // value: "0", + // data: opUSDCInvst.interface.encodeFunctionData("harvest", [ + // sushiInstance.address, + // sushiswapRouter.address, + // false, + // expectedMinUSDCFromSUSHIForopUSDCInvst, + // BigNumber.from(timestamp).add("600"), + // [ + // sushiInstance.address, + // EthereumTokens.PLAIN_TOKENS.USDT, + // EthereumTokens.PLAIN_TOKENS.DAI, + // EthereumTokens.PLAIN_TOKENS.USDC, + // ], + // "0x", + // ]), + // }, // vaultDepositAllToStrategy for opUSDCEarn { to: opUSDCEarn.address, @@ -249,18 +286,24 @@ async function main() { value: "0", data: opWETHEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), }, - // vaultDepositAllToStrategy for opWETHInvst - { - to: opWETHInvst.address, - value: "0", - data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), - }, - // vaultDepositAllToStrategy for opUSDCInvst - { - to: opUSDCInvst.address, - value: "0", - data: opUSDCInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), - }, + // // vaultDepositAllToStrategy for opWBTCEarn + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), + // }, + // // vaultDepositAllToStrategy for opWETHInvst + // { + // to: opWETHInvst.address, + // value: "0", + // data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + // }, + // // vaultDepositAllToStrategy for opUSDCInvst + // { + // to: opUSDCInvst.address, + // value: "0", + // data: opUSDCInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + // }, ]; const safeOwner = ethers.provider.getSigner(0); @@ -279,3 +322,47 @@ async function main() { } main().then(console.log).catch(console.error); + +const curveGuageABI = [ + { + stateMutability: "nonpayable", + type: "function", + name: "claim_rewards", + inputs: [ + { + name: "_addr", + type: "address", + }, + ], + outputs: [], + }, + { + name: "claimable_tokens", + outputs: [ + { + type: "uint256", + name: "", + }, + ], + inputs: [ + { + type: "address", + name: "addr", + }, + ], + stateMutability: "view", + type: "function", + }, + { + name: "minter", + outputs: [ + { + type: "address", + name: "", + }, + ], + inputs: [], + stateMutability: "view", + type: "function", + }, +]; diff --git a/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts index 76b103b5..9f9ac8cb 100644 --- a/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts +++ b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts @@ -18,7 +18,7 @@ import { IUniswapV2Router02, IUniswapV2Router02__factory, } from "../../typechain"; -import { parseEther, parseUnits } from "ethers/lib/utils"; +import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import { comptrollerABI } from "./007_BDAMM"; import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; @@ -93,8 +93,11 @@ async function main() { BigNumber.from("100000000000000000000000000"), ); + console.log(formatEther(crvUnclaimedForopUSD3Earn)); + console.log(formatEther(cvxUnclaimedForopUSD3Earn)); + const oneCRVToUSDC = parseUnits("0.5", "6"); //0.53 crv - usdc - const oneCVXToUSDC = parseUnits("3.3", "6"); // 3.3 + const oneCVXToUSDC = parseUnits("3.1", "6"); // 3.3 const expectedMinUSDCFromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) .mul(oneCRVToUSDC) @@ -131,86 +134,86 @@ async function main() { [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.PLAIN_TOKENS.USDC], ); const transactions: MetaTransactionData[] = [ - { - to: compoundComptrollerInstance.address, - value: "0", - data: compoundComptrollerInstance.interface.encodeFunctionData("claimComp", [ - [opWBTCEarn.address], - [ - "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80", // dAMM finance - dWBTC - ], - false, - true, - ]), - }, - { - to: opWBTCEarn.address, - value: "0", - data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ - [ - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [dWBTCInstance.address, dWBTCInstance.interface.encodeFunctionData("redeem", [dwbtcBalance])], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - sushiswapRouter.address, - sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ - parseUnits("57.886070", "6"), - parseUnits("0.0035", "8"), - [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], - opWBTCEarn.address, - BigNumber.from(timestamp).add("600"), - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - EthereumTokens.PLAIN_TOKENS.USDC, - usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("57.886070", "6")]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", - usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("526.237")]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - bdammRedemption.address, - bdammRedemption.interface.encodeFunctionData("redeem", [ - parseEther("526.237"), - parseUnits("57.886070", "6"), - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [ - uniswapV3Router, - uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ - { - path: uniV3SwapPathForDAMMtoWBTC, - recipient: opWBTCEarn.address, - deadline: BigNumber.from(timestamp).add("600"), - amountIn: parseEther("526.237"), - amountOutMinimum: parseUnits("0", "6"), - }, - ]), - ], - ), - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [opWBTCEarn.address, opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy")], - ), - ], - ]), - }, + // { + // to: compoundComptrollerInstance.address, + // value: "0", + // data: compoundComptrollerInstance.interface.encodeFunctionData("claimComp", [ + // [opWBTCEarn.address], + // [ + // "0x63D6b99659f7b05b054DEEF582F5DaAa51780E80", // dAMM finance - dWBTC + // ], + // false, + // true, + // ]), + // }, + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ + // [ + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [dWBTCInstance.address, dWBTCInstance.interface.encodeFunctionData("redeem", [dwbtcBalance])], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // sushiswapRouter.address, + // sushiswapRouter.interface.encodeFunctionData("swapTokensForExactTokens", [ + // parseUnits("57.886070", "6"), + // parseUnits("0.0035", "8"), + // [EthereumTokens.BTC_TOKENS.WBTC, EthereumTokens.WRAPPED_TOKENS.WETH, EthereumTokens.PLAIN_TOKENS.USDC], + // opWBTCEarn.address, + // BigNumber.from(timestamp).add("600"), + // ]), + // ], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // EthereumTokens.PLAIN_TOKENS.USDC, + // usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseUnits("57.886070", "6")]), + // ], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // "0xfa372fF1547fa1a283B5112a4685F1358CE5574d", + // usdc.interface.encodeFunctionData("approve", [bdammRedemption.address, parseEther("526.237")]), + // ], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // bdammRedemption.address, + // bdammRedemption.interface.encodeFunctionData("redeem", [ + // parseEther("526.237"), + // parseUnits("57.886070", "6"), + // ]), + // ], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // uniswapV3Router, + // uniswapV3RouterInstance.interface.encodeFunctionData("exactInput", [ + // { + // path: uniV3SwapPathForDAMMtoWBTC, + // recipient: opWBTCEarn.address, + // deadline: BigNumber.from(timestamp).add("600"), + // amountIn: parseEther("526.237"), + // amountOutMinimum: parseUnits("0", "6"), + // }, + // ]), + // ], + // ), + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [opWBTCEarn.address, opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy")], + // ), + // ], + // ]), + // }, // claim CRV, CVX for opUSD3Earn { to: cvxMIM3lp3GaugeInstance.address, @@ -267,7 +270,7 @@ async function main() { .add(usdcBalanceForopUSD3Earn), 0, ], - "32000000000000000000", + "31000000000000000000", ]), ], ), diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index 48b229d5..e45a0db2 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -14,7 +14,11 @@ import { Signers, to_10powNumber_BN, } from "../../helpers/utils"; -import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; +import { + MultiChainVaults, + StrategiesByTokenByChain, + strategyHashReadIndexes, +} from "../../helpers/data/adapter-with-strategies"; import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; import { Registry, @@ -25,8 +29,11 @@ import { ERC20__factory, IComptroller__factory, StrategyRegistry__factory, - VaultHelperMainnet, - VaultHelperMainnet__factory, + VaultHelper, + AaveV1Helper__factory, + CompoundHelper__factory, + SwapHelper__factory, + VaultHelper__factory, } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; @@ -40,70 +47,6 @@ chai.use(solidity); const fork = process.env.FORK as eEVMNetwork; const DEBUG = process.env.DEBUG === "true" ? true : false; const IGNORE_VAULTS = process.env.IGNORE_VAULTS; -const strategyHashReadIndexes: { - [key: string]: { oraValueUTIndex: number; oraValueLPIndex: number; lastStepLPBalanceIndex: number }; -} = { - "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { - // dai-DEPOSIT-AaveV2-aDAI - oraValueUTIndex: 1, - oraValueLPIndex: 0, - lastStepLPBalanceIndex: 1, - }, - "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { - // dai-DEPOSIT-Compound-cDAI - oraValueUTIndex: 1, - oraValueLPIndex: 1, - lastStepLPBalanceIndex: 1, - }, - "0x74ceffc4d239683893dd31f6c4bed33f65aafb1bdad2c47d61bae7db81a42e4d": { - // usdt-DEPOSIT-AaveV2-aUSDT - oraValueUTIndex: 1, - oraValueLPIndex: 0, - lastStepLPBalanceIndex: 1, - }, - "0x4103355b18f7a7ea81aebc211548510bb077426632fbe4899f4cf162c70ba396": { - // usdt-DEPOSIT-Compound-cUSDT - oraValueUTIndex: 1, - oraValueLPIndex: 1, - lastStepLPBalanceIndex: 1, - }, - "0x46b9c41c6ff6c82958774b97fc426f046011e4177b8f64ded0d1a704d083b3c6": { - // wbtc-DEPOSIT-AaveV2-aWBTC - oraValueUTIndex: 1, - oraValueLPIndex: 0, - lastStepLPBalanceIndex: 1, - }, - "0x35f4123193f545465801d2cd7418a60c7cfe6ade80b8be4f941124705fb7a39c": { - // wbtc-DEPOSIT-Compound-cWBTC - oraValueUTIndex: 1, - oraValueLPIndex: 1, - lastStepLPBalanceIndex: 1, - }, - "0xdc405bd6462f69e015108f9c274278cf552f891ed2015820827a672abafd48fc": { - // usdc-DEPOSIT-AaveV2-aUSDC - oraValueUTIndex: 1, - oraValueLPIndex: 0, - lastStepLPBalanceIndex: 1, - }, - "0x58404c3f191270f62e374653c7aa923e5487ed261523b0aef0a432a01a8ea088": { - // usdc-DEPOSIT-Compound-cUSDC - oraValueUTIndex: 1, - oraValueLPIndex: 1, - lastStepLPBalanceIndex: 1, - }, - "0x514e845e4f1401cfe30f214a1386dfd06a98e15316dde7f669889a82ddffdeb8": { - // weth-DEPOSIT-AaveV2-aWETH - oraValueUTIndex: 1, - oraValueLPIndex: 0, - lastStepLPBalanceIndex: 1, - }, - "0x87ed056054f13b934a9864226aa8c1f52ad63a7ad25bb2c74c1f920c9635c04c": { - // weth-DEPOSIT-Compound-cETH - oraValueUTIndex: 1, - oraValueLPIndex: 1, - lastStepLPBalanceIndex: 1, - }, -}; describe(`${fork}-Vault-rev7`, () => { before(async function () { @@ -113,12 +56,34 @@ describe(`${fork}-Vault-rev7`, () => { await deployments.get("StrategyRegistry") ).address, ); - this.vaultHelperMainnet = ( - await ethers.getContractAt(VaultHelperMainnet__factory.abi, (await deployments.get("VaultHelperMainnet")).address) + this.vaultHelper = ( + await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + ); + this.aaveV1Helper = await ethers.getContractAt( + AaveV1Helper__factory.abi, + ( + await deployments.get("AaveV1Helper") + ).address, ); + this.compoundHelper = await ethers.getContractAt( + CompoundHelper__factory.abi, + ( + await deployments.get("CompoundHelper") + ).address, + ); + this.swapHelper = await ethers.getContractAt( + SwapHelper__factory.abi, + ( + await deployments.get("SwapHelper") + ).address, + ); + this.strategyManager = new StrategyManager( - this.vaultHelperMainnet, (await deployments.get("OptyFiOracle")).address, + this.vaultHelper, + this.compoundHelper, + this.aaveV1Helper, + this.swapHelper, ); this.registry = ( await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) @@ -137,14 +102,8 @@ describe(`${fork}-Vault-rev7`, () => { this.signers.strategyOperator = await ethers.getSigner(await this.registry.getStrategyOperator()); this.signers.governance = await ethers.getSigner(await this.registry.getGovernance()); for (const riskProfile of Object.keys(MultiChainVaults[fork])) { - if (riskProfile !== "Save") { - continue; - } this.vaults[riskProfile] = {}; for (const token of Object.keys(MultiChainVaults[fork][riskProfile])) { - if (!["DAI", "USDT", "WBTC", "USDC", "WETH"].includes(token)) { - continue; - } if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { continue; } @@ -179,18 +138,6 @@ describe(`${fork}-Vault-rev7`, () => { this.tokens[token] = ( await ethers.getContractAt(ERC20__factory.abi, MULTI_CHAIN_VAULT_TOKENS[fork][token].address) ); - let _userDepositInDecimals = await this.vaults[riskProfile][token].minimumDepositValueUT(); - const decimals = await this.tokens[token].decimals(); - if (_userDepositInDecimals.eq(BigNumber.from("0"))) { - _userDepositInDecimals = BigNumber.from("3").mul(parseUnits("1", decimals)); - } - const _userDeposit = new BN(_userDepositInDecimals.toString()).div( - new BN(to_10powNumber_BN(await this.vaults[riskProfile][token].decimals()).toString()), - ); - await setTokenBalanceInStorage(this.tokens[token], this.signers.alice.address, _userDeposit.toString()); - await setTokenBalanceInStorage(this.tokens[token], this.signers.bob.address, _userDeposit.toString()); - expect(await this.tokens[token].balanceOf(this.signers.alice.address)).to.eq(_userDepositInDecimals); - expect(await this.tokens[token].balanceOf(this.signers.bob.address)).to.eq(_userDepositInDecimals); } } if (fork == eEVMNetwork.mainnet) { @@ -203,7 +150,7 @@ describe(`${fork}-Vault-rev7`, () => { continue; } for (const token of Object.keys(StrategiesByTokenByChain[fork][riskProfile])) { - if (!["DAI", "USDT", "WBTC", "USDC", "WETH"].includes(token)) { + if (!["WETH"].includes(token)) { continue; } if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { @@ -213,7 +160,6 @@ describe(`${fork}-Vault-rev7`, () => { const strategyDetail = StrategiesByTokenByChain[fork][riskProfile][token][strategy]; const tokenHash = generateTokenHashV2([strategyDetail.token], NETWORKS_CHAIN_ID_HEX[fork]); const strategyHash = generateStrategyHashV2(strategyDetail.strategy, tokenHash); - console.log("strategyHash ", strategyHash); const lastPool = strategyDetail.strategy[strategyDetail.strategy.length - 1].contract; const steps = strategyDetail.strategy.map(item => ({ pool: item.contract, @@ -223,6 +169,7 @@ describe(`${fork}-Vault-rev7`, () => { describe(`${fork}-${riskProfile}-${token}-${strategy}`, () => { before(async function () { + console.log(`${token}-${strategy} `, strategyHash); const addStrategyPlantx = await this.strategyRegistry .connect(this.signers.operator) .addStrategyPlan(this.vaults[riskProfile][token].address, strategyHash, { @@ -335,6 +282,18 @@ describe(`${fork}-Vault-rev7`, () => { // } // } } + let _userDepositInDecimals = await this.vaults[riskProfile][token].minimumDepositValueUT(); + const decimals = await this.tokens[token].decimals(); + if (_userDepositInDecimals.eq(BigNumber.from("0"))) { + _userDepositInDecimals = BigNumber.from("3").mul(parseUnits("1", decimals)); + } + const _userDeposit = new BN(_userDepositInDecimals.toString()).div( + new BN(to_10powNumber_BN(await this.tokens[token].decimals()).toString()), + ); + await setTokenBalanceInStorage(this.tokens[token], this.signers.alice.address, _userDeposit.toString()); + await setTokenBalanceInStorage(this.tokens[token], this.signers.bob.address, _userDeposit.toString()); + expect(await this.tokens[token].balanceOf(this.signers.alice.address)).to.eq(_userDepositInDecimals); + expect(await this.tokens[token].balanceOf(this.signers.bob.address)).to.eq(_userDepositInDecimals); }); it(`${riskProfile}-${token}-${strategy}-(first) alice and bob should deposit into Vault successfully`, async function () { const signers = [this.signers.alice, this.signers.bob]; @@ -443,8 +402,7 @@ describe(`${fork}-Vault-rev7`, () => { const vaultDepositTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) .vaultDepositSomeToStrategy(strategyHash, vaultDepositUT); - const res = await vaultDepositTx.wait(1); - console.log("gas ", res.gasUsed.toString()); + await vaultDepositTx.wait(1); await assertPostVaultDepositState( this.signers.alice, @@ -792,8 +750,7 @@ describe(`${fork}-Vault-rev7`, () => { const vaultWithdrawSomeFromStrategyTx = await this.vaults[riskProfile][token] .connect(this.signers.strategyOperator) .vaultWithdrawSomeFromStrategy(strategyHash, vaultWithdrawLP); - const res = await vaultWithdrawSomeFromStrategyTx.wait(1); - console.log("gas ", res.gasUsed.toString()); + await vaultWithdrawSomeFromStrategyTx.wait(1); await assertPostVaultWithdrawState( this.signers.alice, @@ -888,6 +845,7 @@ async function userDeposit( const tx1 = await underlyingTokenInstance .connect(signers[i]) .approve(vaultInstance.address, _userDepositInDecimals); + await tx1.wait(1); const { userBalanceBeforeUT, userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT } = await getPreActionState(signers[i], underlyingTokenInstance, vaultInstance, strategyManager, steps); @@ -895,9 +853,7 @@ async function userDeposit( const tx2 = await vaultInstance .connect(signers[i]) .userDepositVault(signers[i].address, _userDepositInDecimals, "0x", []); - const res = await tx2.wait(1); - - console.log("gas ", res.gasUsed.toString()); + await tx2.wait(1); await assertPostUserDepositState( signers[i], @@ -1038,8 +994,7 @@ async function userWithdraw( const tx = await vaultInstance .connect(signers[i]) .userWithdrawVault(signers[i].address, userWithdrawBalance.mul(3).div(4), []); - const res = await tx.wait(); - console.log("gas ", res.gasUsed.toString()); + await tx.wait(); await assertPostUserWithdrawState( signers[i], diff --git a/test/test-opty/utils.ts b/test/test-opty/utils.ts index 744c6b39..b1749b32 100644 --- a/test/test-opty/utils.ts +++ b/test/test-opty/utils.ts @@ -8,7 +8,7 @@ import polygonTokens from "@optyfi/defi-legos/polygon/tokens"; import avaxTokens from "@optyfi/defi-legos/avalanche/tokens"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import { StrategyStepType } from "../../helpers/type"; -import { ERC20, IAdapterFull, IWETH, Registry, ERC20Permit, Vault } from "../../typechain"; +import { ERC20, IAdapterFull, Registry, ERC20Permit, Vault, IWETH9__factory, IWETH9 } from "../../typechain"; import { fundWalletToken, getBlockTimestamp } from "../../helpers/contracts-actions"; import { RegistryV1 } from "../../helpers/types/registryV1"; import { StrategyManager } from "../../helpers/strategy-manager"; @@ -60,9 +60,11 @@ export async function setTokenBalanceInStorage( getAddress(token.address), ) ) { - const weth = ( - await ethers.getContractAt("@uniswap/v2-periphery/contracts/interfaces/IWETH.sol:IWETH", token.address) - ); + const weth = await ethers.getContractAt(IWETH9__factory.abi, token.address); + const wethBalance = await weth.balanceOf(account); + if (wethBalance.gt("0")) { + await weth.connect(await ethers.getSigner(account)).withdraw(wethBalance); + } await weth.deposit({ value: parseEther(amount) }); await weth.transfer(account, parseEther(amount)); } else { From e06fe2ec0d98348b7a8886f9772315731a7008e9 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Tue, 10 Jan 2023 09:45:25 -0500 Subject: [PATCH 38/44] feat(adapter): convex and curve --- .eslintignore | 14 +- .prettierignore | 13 +- .solhint.json | 2 +- .solhintignore | 13 +- .../adapters/ethereum/AaveV1Helper.sol | 18 +- .../adapters/ethereum/CompoundHelper.sol | 16 +- .../adapters/ethereum/CurveHelper.sol | 68 ++++- .../protocol/adapters/ethereum/SwapHelper.sol | 34 ++- .../protocol/tokenization/VaultHelper.sol | 3 +- deploy_mainnet/002_deploy_curvehelper.ts | 7 +- deploy_mainnet/002_deploy_swaphelper.ts | 7 +- deploy_mainnet/023_deploy_opUSDC-Earn.ts | 22 +- helpers/adapters/ethereum/AaveV2Adapter.ts | 2 + helpers/adapters/ethereum/CompoundAdapter.ts | 2 + helpers/adapters/ethereum/ConvexAdapter.ts | 160 ++++++++++ .../adapters/ethereum/ConvexStakingAdapter.ts | 287 ++++++++++++++++++ helpers/adapters/ethereum/CurveAdapter.ts | 4 +- .../adapters/ethereum/CurveGaugeAdapter.ts | 234 ++++++++++++++ helpers/utils.ts | 1 - scripts/harvest/007_BDAMM.ts | 2 +- ...9_usdc-mim-sushi_weth-ldo-sushi_harvest.ts | 165 +++++++--- .../harvest/010_harvest_wbtc-damm_3crv-mim.ts | 85 +++++- 22 files changed, 1080 insertions(+), 79 deletions(-) create mode 100644 helpers/adapters/ethereum/ConvexAdapter.ts create mode 100644 helpers/adapters/ethereum/ConvexStakingAdapter.ts create mode 100644 helpers/adapters/ethereum/CurveGaugeAdapter.ts diff --git a/.eslintignore b/.eslintignore index 85d334b9..514236ae 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,7 +10,19 @@ typechain/ specification_docs/ deployments/ contracts/protocol/earn-protocol-configuration -contracts/protocol/adapters +contracts/protocol/adapters/avalanche +contracts/protocol/adapters/defi-adapters +contracts/protocol/adapters/ethereum/curve-crypto-pool-adapter +contracts/protocol/adapters/ethereum/curve-metapool-gauge-adapter +contracts/protocol/adapters/ethereum/curve-metapool-swap-adapter +contracts/protocol/adapters/ethereum/newo-staking-adapter +contracts/protocol/adapters/ethereum/sushi-bar-adapter +contracts/protocol/adapters/ethereum/sushiswap-masterchefv2-adapter-ethereum +contracts/protocol/adapters/ethereum/team-defi-adapters +contracts/protocol/adapters/polygon +contracts/protocol/adapters/swap-adapters +contracts/protocol/adapters/uniswapV2PoolAdapter +contracts/utils/optyfi-oracle tenderly-actions/ helpers/types/ deployments/ diff --git a/.prettierignore b/.prettierignore index 0bc354d5..64d913fd 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,7 +12,18 @@ **/typechain **/specification_docs contracts/protocol/earn-protocol-configuration -contracts/protocol/adapters +contracts/protocol/adapters/avalanche +contracts/protocol/adapters/defi-adapters +contracts/protocol/adapters/ethereum/curve-crypto-pool-adapter +contracts/protocol/adapters/ethereum/curve-metapool-gauge-adapter +contracts/protocol/adapters/ethereum/curve-metapool-swap-adapter +contracts/protocol/adapters/ethereum/newo-staking-adapter +contracts/protocol/adapters/ethereum/sushi-bar-adapter +contracts/protocol/adapters/ethereum/sushiswap-masterchefv2-adapter-ethereum +contracts/protocol/adapters/ethereum/team-defi-adapters +contracts/protocol/adapters/polygon +contracts/protocol/adapters/swap-adapters +contracts/protocol/adapters/uniswapV2PoolAdapter **out/ helpers/types/ deployments/ diff --git a/.solhint.json b/.solhint.json index ad5e6d35..5d7b650d 100644 --- a/.solhint.json +++ b/.solhint.json @@ -3,7 +3,7 @@ "plugins": ["prettier"], "rules": { "code-complexity": ["error", 11], - "compiler-version": ["error", "^0.6.12"], + "compiler-version": ["error", ">=0.6.12"], "const-name-snakecase": "off", "constructor-syntax": "error", "func-visibility": ["error", { "ignoreConstructors": true }], diff --git a/.solhintignore b/.solhintignore index 17b4ffda..4f3d7939 100644 --- a/.solhintignore +++ b/.solhintignore @@ -7,5 +7,16 @@ contracts/utils/ contracts/dependencies/ contracts/mocks/ contracts/protocol/earn-protocol-configuration -contracts/protocol/adapters +contracts/protocol/adapters/avalanche +contracts/protocol/adapters/defi-adapters +contracts/protocol/adapters/ethereum/curve-crypto-pool-adapter +contracts/protocol/adapters/ethereum/curve-metapool-gauge-adapter +contracts/protocol/adapters/ethereum/curve-metapool-swap-adapter +contracts/protocol/adapters/ethereum/newo-staking-adapter +contracts/protocol/adapters/ethereum/sushi-bar-adapter +contracts/protocol/adapters/ethereum/sushiswap-masterchefv2-adapter-ethereum +contracts/protocol/adapters/ethereum/team-defi-adapters +contracts/protocol/adapters/polygon +contracts/protocol/adapters/swap-adapters +contracts/protocol/adapters/uniswapV2PoolAdapter contracts/utils/optyfi-oracle \ No newline at end of file diff --git a/contracts/protocol/adapters/ethereum/AaveV1Helper.sol b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol index 4726535e..539769fa 100644 --- a/contracts/protocol/adapters/ethereum/AaveV1Helper.sol +++ b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol @@ -12,16 +12,25 @@ import { IAaveV1Token } from "@optyfi/defi-legos/ethereum/aave/contracts/IAaveV1 contract AaveV1Helper { using SafeERC20 for IERC20; + // solhint-disable-next-line var-name-mixedcase IWETH public immutable WETH; // solhint-disable-next-line var-name-mixedcase address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - constructor(IWETH _WETH) { - WETH = _WETH; + /* solhint-disable no-empty-blocks*/ + constructor(IWETH _weth) { + WETH = _weth; } - function depositETH_AaveV1(address _lendingPool, address _lpToken, uint256 _amount) external { + /* solhint-enable no-empty-blocks*/ + + // solhint-disable-next-line func-name-mixedcase + function depositETH_AaveV1( + address _lendingPool, + address _lpToken, + uint256 _amount + ) external { uint256 _wethBalanceBefore = IERC20(address(WETH)).balanceOf(address(this)); IERC20(address(WETH)).safeTransferFrom(msg.sender, address(this), _amount); uint256 _wethBalanceAfter = IERC20(address(WETH)).balanceOf(address(this)); @@ -33,6 +42,7 @@ contract AaveV1Helper { IERC20(_lpToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function withdrawETH_AaveV1(address _lpToken, uint256 _amount) external { uint256 _lpTokenBalanceBefore = IERC20(_lpToken).balanceOf(address(this)); IERC20(_lpToken).safeTransferFrom(msg.sender, address(this), _amount); @@ -45,5 +55,7 @@ contract AaveV1Helper { IERC20(address(WETH)).safeTransfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); } + /* solhint-disable no-empty-blocks*/ receive() external payable {} + /* solhint-enable no-empty-blocks*/ } diff --git a/contracts/protocol/adapters/ethereum/CompoundHelper.sol b/contracts/protocol/adapters/ethereum/CompoundHelper.sol index fbaa5e9c..a092350f 100644 --- a/contracts/protocol/adapters/ethereum/CompoundHelper.sol +++ b/contracts/protocol/adapters/ethereum/CompoundHelper.sol @@ -11,13 +11,16 @@ import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; contract CompoundHelper { using SafeERC20 for IERC20; + // solhint-disable-next-line var-name-mixedcase IWETH public immutable WETH; - - constructor(IWETH _WETH, IERC20 cETH) { - WETH = _WETH; - cETH.safeApprove(address(cETH),type(uint256).max); + + // solhint-disable-next-line var-name-mixedcase + constructor(IWETH _weth, IERC20 cETH) { + WETH = _weth; + cETH.safeApprove(address(cETH), type(uint256).max); } + // solhint-disable-next-line func-name-mixedcase function calculateAmountInToken_Compound(address _liquidityPool, uint256 _liquidityPoolTokenAmount) external view @@ -26,6 +29,7 @@ contract CompoundHelper { return (_liquidityPoolTokenAmount * ICompound(_liquidityPool).exchangeRateStored()) / 1e18; } + // solhint-disable-next-line func-name-mixedcase function calculateAmountInLPToken_Compound(address _liquidityPool, uint256 _depositAmount) external view @@ -34,6 +38,7 @@ contract CompoundHelper { return (_depositAmount * 1e18) / ICompound(_liquidityPool).exchangeRateStored(); } + // solhint-disable-next-line func-name-mixedcase function depositETH_Compound(address _liquidityPool, uint256 _amount) external { uint256 _wethBalanceBefore = IERC20(address(WETH)).balanceOf(address(this)); IERC20(address(WETH)).safeTransferFrom(msg.sender, address(this), _amount); @@ -46,6 +51,7 @@ contract CompoundHelper { IERC20(_liquidityPool).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function withdrawETH_Compound(address _liquidityPool, uint256 _amount) external { uint256 _lpTokenBalanceBefore = IERC20(_liquidityPool).balanceOf(address(this)); IERC20(_liquidityPool).safeTransferFrom(msg.sender, address(this), _amount); @@ -58,5 +64,7 @@ contract CompoundHelper { IERC20(address(WETH)).safeTransfer(msg.sender, _balanceAfterETH - _balanceBeforeETH); } + /* solhint-disable no-empty-blocks*/ receive() external payable {} + /* solhint-enable no-empty-blocks*/ } diff --git a/contracts/protocol/adapters/ethereum/CurveHelper.sol b/contracts/protocol/adapters/ethereum/CurveHelper.sol index 8de53459..97647620 100644 --- a/contracts/protocol/adapters/ethereum/CurveHelper.sol +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -1,17 +1,28 @@ // SPDX-License-Identifier:MIT -pragma solidity =0.8.11; +pragma solidity ^0.6.12; // interfaces -import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; import { ICurveSwap } from "@optyfi/defi-legos/ethereum/curve/contracts/interfacesV0/ICurveSwap.sol"; +// helper contracts +import { Modifiers } from "../../earn-protocol-configuration/contracts/Modifiers.sol"; -contract CurveHelper { +// libraries +import { Errors } from "../../../utils/Errors.sol"; + +contract CurveHelper is Modifiers { using SafeERC20 for IERC20; + /* solhint-disable no-empty-blocks*/ + constructor(address _registry) public Modifiers(_registry) {} + + /* solhint-enable no-empty-blocks*/ + + // solhint-disable-next-line func-name-mixedcase function addLiquidity_two_coin_zero_index_Curve( address _underlyingToken, address _pool, @@ -29,6 +40,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_two_coin_one_index_Curve( address _underlyingToken, address _pool, @@ -46,6 +58,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_three_coin_zero_index_Curve( address _underlyingToken, address _pool, @@ -63,6 +76,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_three_coin_one_index_Curve( address _underlyingToken, address _pool, @@ -80,6 +94,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_three_coin_two_index_Curve( address _underlyingToken, address _pool, @@ -97,6 +112,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_four_coin_zero_index_Curve( address _underlyingToken, address _pool, @@ -114,7 +130,8 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } - function addLiquidity_four_coin_one_index_Curve( + // solhint-disable-next-line func-name-mixedcase + function addLiquidity_four_coin_one_index_Curve( address _underlyingToken, address _pool, address _liquidityPoolToken, @@ -131,6 +148,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_four_coin_two_index_Curve( address _underlyingToken, address _pool, @@ -148,6 +166,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function addLiquidity_four_coin_three_index_Curve( address _underlyingToken, address _pool, @@ -165,6 +184,7 @@ contract CurveHelper { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_two_coin_zero_index_Curve( address _pool, uint256 _amount, @@ -173,6 +193,7 @@ contract CurveHelper { return ICurveSwap(_pool).calc_token_amount([_amount, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_two_coin_one_index_Curve( address _pool, uint256 _amount, @@ -181,59 +202,82 @@ contract CurveHelper { return ICurveSwap(_pool).calc_token_amount([0, _amount], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_three_coin_zero_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([_amount,0,0], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([_amount, 0, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_three_coin_one_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([0,_amount,0], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([0, _amount, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_three_coin_two_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([0,0,_amount], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([0, 0, _amount], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_four_coin_zero_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([_amount,0,0,0], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([_amount, 0, 0, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_four_coin_one_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([0,_amount,0,0], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([0, _amount, 0, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_four_coin_two_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([0,0,_amount,0], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([0, 0, _amount, 0], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_four_coin_three_index_Curve( address _pool, uint256 _amount, bool _isDeposit ) external view returns (uint256) { - return ICurveSwap(_pool).calc_token_amount([0,0,0,_amount], _isDeposit); + return ICurveSwap(_pool).calc_token_amount([0, 0, 0, _amount], _isDeposit); + } + + function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external onlyRiskOperator { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); + for (uint256 _i; _i < _tokensLen; _i++) { + _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); + } + } + + function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external onlyRiskOperator { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); + for (uint256 _i; _i < _tokensLen; _i++) { + _tokens[_i].safeApprove(_spenders[_i], 0); + } } } diff --git a/contracts/protocol/adapters/ethereum/SwapHelper.sol b/contracts/protocol/adapters/ethereum/SwapHelper.sol index 183d51f7..6a36fcba 100644 --- a/contracts/protocol/adapters/ethereum/SwapHelper.sol +++ b/contracts/protocol/adapters/ethereum/SwapHelper.sol @@ -1,28 +1,36 @@ // SPDX-License-Identifier:MIT -pragma solidity =0.8.11; +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; // interfaces -import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { ISwapRouter } from "../../../interfaces/uniswap/ISwapRouter.sol"; +// helper contracts +import { Modifiers } from "../../earn-protocol-configuration/contracts/Modifiers.sol"; + // libraries import { BytesLib } from "../../../utils/BytesLib.sol"; +import { Errors } from "../../../utils/Errors.sol"; -contract SwapHelper { +contract SwapHelper is Modifiers { using BytesLib for bytes; using SafeERC20 for IERC20; + // solhint-disable-next-line var-name-mixedcase ISwapRouter public immutable SwapRouter; // solhint-disable-next-line var-name-mixedcase address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); - constructor(ISwapRouter _SwapRouter) { + // solhint-disable-next-line var-name-mixedcase + constructor(address _registry, ISwapRouter _SwapRouter) public Modifiers(_registry) { SwapRouter = _SwapRouter; } + // solhint-disable-next-line func-name-mixedcase function exactInput_UniswapV3( bytes memory _path, uint256 _deadline, @@ -43,4 +51,20 @@ contract SwapHelper { }) ); } + + function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external onlyRiskOperator { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); + for (uint256 _i; _i < _tokensLen; _i++) { + _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); + } + } + + function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external onlyRiskOperator { + uint256 _tokensLen = _tokens.length; + require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); + for (uint256 _i; _i < _tokensLen; _i++) { + _tokens[_i].safeApprove(_spenders[_i], 0); + } + } } diff --git a/contracts/protocol/tokenization/VaultHelper.sol b/contracts/protocol/tokenization/VaultHelper.sol index 925bdda1..35d9e2ed 100644 --- a/contracts/protocol/tokenization/VaultHelper.sol +++ b/contracts/protocol/tokenization/VaultHelper.sol @@ -7,13 +7,12 @@ import { IERC20 } from "@openzeppelin/contracts-0.8.x/token/ERC20/IERC20.sol"; import { IOptyFiOracle } from "../../utils/optyfi-oracle/contracts/interfaces/IOptyFiOracle.sol"; import { IERC20Metadata } from "@openzeppelin/contracts-0.8.x/token/ERC20/extensions/IERC20Metadata.sol"; -// libraries - contract VaultHelper { function pureFunctionUint256(uint256 _arg) external pure returns (uint256) { return _arg; } + // solhint-disable-next-line func-name-mixedcase function getTokenOutPrice_OptyFiOracle( IOptyFiOracle _optyFiOracle, address _tokenIn, diff --git a/deploy_mainnet/002_deploy_curvehelper.ts b/deploy_mainnet/002_deploy_curvehelper.ts index 1bab4b3d..19e6123c 100644 --- a/deploy_mainnet/002_deploy_curvehelper.ts +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -17,6 +17,7 @@ const func: DeployFunction = async ({ const { deploy } = deployments; const { deployer } = await getNamedAccounts(); const artifact = await deployments.getArtifact("CurveHelper"); + const registryProxyAddress = (await deployments.get("RegistryProxy")).address; const chainId = await getChainId(); const networkName = network.name; @@ -28,7 +29,7 @@ const func: DeployFunction = async ({ bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [], + args: [registryProxyAddress], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -42,7 +43,7 @@ const func: DeployFunction = async ({ await tenderly.verify({ name: "CurveHelper", address: curvehelper.address, - constructorArguments: [], + constructorArguments: [registryProxyAddress], contract: "CurveHelper", }); } else if (!["31337"].includes(chainId)) { @@ -51,7 +52,7 @@ const func: DeployFunction = async ({ await run("verify:verify", { name: "CurveHelper", address: curvehelper.address, - constructorArguments: [], + constructorArguments: [registryProxyAddress], contract: "CurveHelper", }); } diff --git a/deploy_mainnet/002_deploy_swaphelper.ts b/deploy_mainnet/002_deploy_swaphelper.ts index ff993505..51abd462 100644 --- a/deploy_mainnet/002_deploy_swaphelper.ts +++ b/deploy_mainnet/002_deploy_swaphelper.ts @@ -19,6 +19,7 @@ const func: DeployFunction = async ({ const { deploy } = deployments; const { deployer } = await getNamedAccounts(); const artifact = await deployments.getArtifact("SwapHelper"); + const registryProxyAddress = (await deployments.get("RegistryProxy")).address; const chainId = await getChainId(); const networkName = network.name; @@ -30,7 +31,7 @@ const func: DeployFunction = async ({ bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [uniswapV3Router], + args: [registryProxyAddress, uniswapV3Router], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -44,7 +45,7 @@ const func: DeployFunction = async ({ await tenderly.verify({ name: "SwapHelper", address: swapHelper.address, - constructorArguments: [uniswapV3Router], + constructorArguments: [registryProxyAddress, uniswapV3Router], contract: "SwapHelper", }); } else if (!["31337"].includes(chainId)) { @@ -53,7 +54,7 @@ const func: DeployFunction = async ({ await run("verify:verify", { name: "SwapHelper", address: swapHelper.address, - constructorArguments: [uniswapV3Router], + constructorArguments: [registryProxyAddress, uniswapV3Router], contract: "SwapHelper", }); } diff --git a/deploy_mainnet/023_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts index 2aabc805..ae2abcf9 100644 --- a/deploy_mainnet/023_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/023_deploy_opUSDC-Earn.ts @@ -4,11 +4,12 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { getAddress, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import Curve from "@optyfi/defi-legos/ethereum/curve/index"; import EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; +import { ERC20, ERC20__factory, Vault, VaultHelper, VaultHelper__factory, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -155,11 +156,15 @@ const func: DeployFunction = async ({ const vaultInstance = ( await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); + const vaultHelperInstance = ( + await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + ); const approvalTokens = []; const approvalSpender = []; const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.WETH); const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const threeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Curve.CurveSwapPool.usdc_3crv.lpToken); const wethSushiswapAllowance = await wethInstance.allowance( vaultInstance.address, @@ -169,6 +174,11 @@ const func: DeployFunction = async ({ vaultInstance.address, EthereumSushiswap.SushiswapRouter.address, ); + const usdcVaultHelperAllowance = await usdcInstance.allowance(vaultInstance.address, vaultHelperInstance.address); + const threeCrvCurveGaugeAllowance = await threeCrvInstance.allowance( + vaultInstance.address, + Curve.CurveSwapPool.usdc_3crv.gauge, + ); if (!wethSushiswapAllowance.gt(parseEther("1000000"))) { approvalTokens.push(wethInstance.address); @@ -180,6 +190,16 @@ const func: DeployFunction = async ({ approvalSpender.push(EthereumSushiswap.SushiswapRouter.address); } + if (!usdcVaultHelperAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(vaultHelperInstance.address); + } + + if (!threeCrvCurveGaugeAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.gauge); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/helpers/adapters/ethereum/AaveV2Adapter.ts b/helpers/adapters/ethereum/AaveV2Adapter.ts index 9bdad15e..0527ebcc 100644 --- a/helpers/adapters/ethereum/AaveV2Adapter.ts +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -72,6 +72,7 @@ export class Aavev2Adapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): ReturnValue { + // TODO : consider unclaimed and claimed Reward token return outputTokenAmount; } @@ -249,6 +250,7 @@ export class Aavev2Adapter implements AdapterInterface { _isSwap: boolean, _provider: JsonRpcProvider, ): Promise { + // TODO : consider unclaimed and claimed Reward token return outputTokenAmount; } diff --git a/helpers/adapters/ethereum/CompoundAdapter.ts b/helpers/adapters/ethereum/CompoundAdapter.ts index 1230b67d..30aea7f7 100644 --- a/helpers/adapters/ethereum/CompoundAdapter.ts +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -70,6 +70,7 @@ export class CompoundAdapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): ReturnValue { + // TODO : consider unclaimed and claimed Reward token const amountUT = planner.add( this.compoundHelperInstance["calculateAmountInToken_Compound(address,uint256)"]( pool, @@ -243,6 +244,7 @@ export class CompoundAdapter implements AdapterInterface { isSwap: boolean, provider: JsonRpcProvider, ): Promise { + // TODO : consider unclaimed and claimed Reward token const cTokenInstance = new ethers.Contract( pool, ICompound__factory.abi, diff --git a/helpers/adapters/ethereum/ConvexAdapter.ts b/helpers/adapters/ethereum/ConvexAdapter.ts new file mode 100644 index 00000000..6e1e3970 --- /dev/null +++ b/helpers/adapters/ethereum/ConvexAdapter.ts @@ -0,0 +1,160 @@ +import { BigNumber, Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import { ReturnValue } from "../../type"; +import { IConvexDeposit__factory } from "../../../typechain"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; + +export class ConvexAdapter implements AdapterInterface { + vaultHelperInstance; + optyFiOracleAddress; + readonly boosterDepositPool = "0xF403C135812408BFbE8713b5A23a04b3D48AAE31"; + + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.optyFiOracleAddress = _optyFiOracleAddress; + } + + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const boosterDepositPoolInstance = weirollContract.createContract( + new ethers.Contract(this.boosterDepositPool, IConvexDeposit__factory.abi), + ); + planner.add( + boosterDepositPoolInstance["deposit(uint256,uint256,bool)"](lpTokenToPoolData[pool], inputTokenAmount, false), + ); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const boosterDepositPoolInstance = weirollContract.createContract( + new ethers.Contract(this.boosterDepositPool, IConvexDeposit__factory.abi), + ); + planner.add( + boosterDepositPoolInstance["withdraw(uint256,uint256)"](lpTokenToPoolData[pool], outputTokenAmount, false), + ); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue { + return inputTokenAmount; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + ): ReturnValue { + const amountLP = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), + ); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + _planner: weirollPlanner, + _vaultInstance: Contract, + _inputToken: string, + _pool: string, + _outputToken: string, + ): weirollPlanner { + throw new Error("not implemented"); + } + + getHarvestRewardsPlan( + _planner: weirollPlanner, + _vaultInstance: Contract, + _vaultUnderlyingToken: string, + ): weirollPlanner { + throw new Error("not implemented"); + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return inputTokenAmount; + } +} + +const lpTokenToPoolData: { [key: string]: { pid: number } } = { + "0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C": { + pid: 9, + }, +}; diff --git a/helpers/adapters/ethereum/ConvexStakingAdapter.ts b/helpers/adapters/ethereum/ConvexStakingAdapter.ts new file mode 100644 index 00000000..8c62808a --- /dev/null +++ b/helpers/adapters/ethereum/ConvexStakingAdapter.ts @@ -0,0 +1,287 @@ +import { BigNumber, Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { ReturnValue } from "../../type"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; +import { IConvexStake__factory } from "../../../typechain"; +import { getAddress } from "ethers/lib/utils"; + +export class CurveSwapPoolAdapter implements AdapterInterface { + vaultHelperInstance; + optyFiOracleAddress; + swapHelperInstance; + + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, swapHelper: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.optyFiOracleAddress = _optyFiOracleAddress; + this.swapHelperInstance = weirollContract.createContract( + new ethers.Contract(swapHelper.address, swapHelper.interface), + ); + } + + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const stakingVault = weirollContract.createContract(new ethers.Contract(pool, IConvexStake__factory.abi)); + planner.add(stakingVault["stake(uint256)"](inputTokenAmount)); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const stakingVault = weirollContract.createContract(new ethers.Contract(pool, IConvexStake__factory.abi)); + planner.add(stakingVault["withdraw(uint256,boll)"](outputTokenAmount, true)); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue { + return inputTokenAmount; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + ): ReturnValue { + const amountLP = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), + ); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + planner: weirollPlanner, + _vaultInstance: Contract, + _inputToken: string, + pool: string, + _outputToken: string, + ): weirollPlanner { + const stakingVault = weirollContract.createContract(new ethers.Contract(pool, IConvexStake__factory.abi)); + planner.add(stakingVault["getReward"]); + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + vaultUnderlyingToken: string, + ): weirollPlanner { + // harvest CRV + // harvest CVX + const rewardAmountCRV = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"]( + EthereumTokens.REWARD_TOKENS.CRV, + vaultInstance.address, + ).staticcall(), + ); + const rewardAmountCVX = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"]( + EthereumTokens.REWARD_TOKENS.CVX, + vaultInstance.address, + ).staticcall(), + ); + const outputAmountForCRV = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( + this.optyFiOracleAddress, + EthereumTokens.REWARD_TOKENS.CRV, + vaultUnderlyingToken, + rewardAmountCRV, + ).staticcall(), + ); + const minimumOutputAmountForCRV = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmountForCRV, 100), + ); + + const outputAmountForCVX = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( + this.optyFiOracleAddress, + EthereumTokens.REWARD_TOKENS.CVX, + vaultUnderlyingToken, + rewardAmountCVX, + ).staticcall(), + ); + const minimumOutputAmountForCVX = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmountForCVX, 100), + ); + let univ3PathCRV; + let univ3PathCVX; + switch (getAddress(vaultUnderlyingToken)) { + case getAddress(EthereumTokens.PLAIN_TOKENS.USDT): { + univ3PathCRV = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 3000, EthereumTokens.PLAIN_TOKENS.USDT], + ); + univ3PathCVX = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CVX, + 10000, + EthereumTokens.PLAIN_TOKENS.USDC, + 100, + EthereumTokens.PLAIN_TOKENS.USDT, + ], + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { + univ3PathCRV = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 10000, EthereumTokens.PLAIN_TOKENS.USDC], + ); + univ3PathCVX = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.PLAIN_TOKENS.USDC], + ); + break; + } + case getAddress(EthereumTokens.WRAPPED_TOKENS.THREE_CRV): { + univ3PathCRV = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CRV, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDC, + 3000, + EthereumTokens.WRAPPED_TOKENS.THREE_CRV, + ], + ); + univ3PathCVX = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CVX, + 10000, + EthereumTokens.PLAIN_TOKENS.USDC, + 3000, + EthereumTokens.WRAPPED_TOKENS.THREE_CRV, + ], + ); + break; + } + case getAddress(EthereumTokens.BTC_TOKENS.WBTC): { + univ3PathCRV = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CRV, + 3000, + EthereumTokens.PLAIN_TOKENS.USDT, + 3000, + EthereumTokens.BTC_TOKENS.WBTC, + ], + ); + univ3PathCVX = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CVX, + 10000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.BTC_TOKENS.WBTC, + ], + ); + break; + } + } + + planner.add( + this.swapHelperInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( + univ3PathCRV, + ethers.constants.MaxUint256, + rewardAmountCRV, + minimumOutputAmountForCRV, + ), + ); + planner.add( + this.swapHelperInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( + univ3PathCVX, + ethers.constants.MaxUint256, + rewardAmountCVX, + minimumOutputAmountForCVX, + ), + ); + return planner; + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return inputTokenAmount; + } +} diff --git a/helpers/adapters/ethereum/CurveAdapter.ts b/helpers/adapters/ethereum/CurveAdapter.ts index 4ebc7690..aede8a9b 100644 --- a/helpers/adapters/ethereum/CurveAdapter.ts +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -9,14 +9,12 @@ import { JsonRpcProvider } from "@ethersproject/providers"; export class CurveSwapPoolAdapter implements AdapterInterface { vaultHelperInstance; - optyFiOracleAddress; curveHelperInstance; - constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, curveHelper: Contract) { + constructor(vaultHelperContract: Contract, curveHelper: Contract) { this.vaultHelperInstance = weirollContract.createContract( new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), ); - this.optyFiOracleAddress = _optyFiOracleAddress; this.curveHelperInstance = weirollContract.createContract( new ethers.Contract(curveHelper.address, curveHelper.interface), ); diff --git a/helpers/adapters/ethereum/CurveGaugeAdapter.ts b/helpers/adapters/ethereum/CurveGaugeAdapter.ts new file mode 100644 index 00000000..6d73ee64 --- /dev/null +++ b/helpers/adapters/ethereum/CurveGaugeAdapter.ts @@ -0,0 +1,234 @@ +import { BigNumber, Contract } from "ethers"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { ReturnValue } from "../../type"; +import { ICurveGauge__factory, ITokenMinter__factory } from "../../../typechain"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; +import { getAddress } from "ethers/lib/utils"; + +export class CurveGaugeAdapter implements AdapterInterface { + vaultHelperInstance; + optyFiOracleAddress; + swapHelperInstance; + readonly tokenMinter = "0xd061D61a4d941c39E5453435B6345Dc261C2fcE0"; + + constructor( + _optyFiOracleAddress: string, + vaultHelperContract: Contract, + curveHelper: Contract, + swapHelperContract: Contract, + ) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + this.optyFiOracleAddress = _optyFiOracleAddress; + this.swapHelperInstance = weirollContract.createContract( + new ethers.Contract(swapHelperContract.address, swapHelperContract.interface), + ); + } + + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const gaugeInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveGauge__factory.abi)); + planner.add(gaugeInstance["deposit(uint256)"](inputTokenAmount)); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const gaugeInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveGauge__factory.abi)); + planner.add(gaugeInstance["withdraw(uint256)"](outputTokenAmount)); + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue { + return inputTokenAmount; + } + + getOutputTokenBalancePlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + ): ReturnValue { + const amountLP = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"](outputToken, vaultInstance.address).staticcall(), + ); + return amountLP as ReturnValue; + } + + getClaimRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + _outputToken: string, + ): weirollPlanner { + const minterInstance = weirollContract.createContract( + new ethers.Contract(this.tokenMinter, ITokenMinter__factory.abi), + ); + planner.add(minterInstance["mint(address)"](pool)); + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + vaultUnderlyingToken: string, + ): weirollPlanner { + const rewardAmount = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"]( + EthereumTokens.REWARD_TOKENS.CRV, + vaultInstance.address, + ).staticcall(), + ); + const outputAmount = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( + this.optyFiOracleAddress, + EthereumTokens.REWARD_TOKENS.CRV, + vaultUnderlyingToken, + rewardAmount, + ).staticcall(), + ); + const minimumOutputAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100), + ); + let univ3Path; + switch (getAddress(vaultUnderlyingToken)) { + case getAddress(EthereumTokens.BTC_TOKENS.WBTC): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CRV, + 3000, + EthereumTokens.PLAIN_TOKENS.USDT, + 3000, + EthereumTokens.BTC_TOKENS.WBTC, + ], + ); + break; + } + case getAddress(EthereumTokens.PLAIN_TOKENS.USDC): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 10000, EthereumTokens.PLAIN_TOKENS.USDC], + ); + break; + } + case getAddress(EthereumTokens.WRAPPED_TOKENS.WETH): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 3000, EthereumTokens.WRAPPED_TOKENS.WETH], + ); + break; + } + case getAddress(EthereumTokens.WRAPPED_TOKENS.THREE_CRV): { + univ3Path = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CRV, + 3000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDC, + 3000, + EthereumTokens.WRAPPED_TOKENS.THREE_CRV, + ], + ); + break; + } + default: { + throw new Error(`not implemented for ${vaultUnderlyingToken}`); + } + } + planner.add( + this.swapHelperInstance["exactInput_UniswapV3(bytes,uint256,uint256,uint256)"]( + univ3Path, + ethers.constants.MaxUint256, + rewardAmount, + minimumOutputAmount, + ), + ); + return planner; + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelperMainnet = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelperMainnet["getERC20Balance(address,address)"](outputToken, vaultInstance.address); + } + + async getValueInInputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + outputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + // TODO : consider unclaimed and claimed Reward token + return outputTokenAmount; + } + + async getValueInOutputToken( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + inputTokenAmount: BigNumber, + _isSwap: boolean, + _provider: JsonRpcProvider, + ): Promise { + return inputTokenAmount; + } +} diff --git a/helpers/utils.ts b/helpers/utils.ts index f9621d82..458ec2c5 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -32,7 +32,6 @@ import { CurveExchangeAdapter, StrategyRegistry, ERC20__factory, - VaultHelperMainnet, VaultHelper, AaveV1Helper, CompoundHelper, diff --git a/scripts/harvest/007_BDAMM.ts b/scripts/harvest/007_BDAMM.ts index 115d2834..c337f5e7 100644 --- a/scripts/harvest/007_BDAMM.ts +++ b/scripts/harvest/007_BDAMM.ts @@ -12,7 +12,7 @@ import { StrategyProvider__factory, } from "../../typechain"; import { BigNumber } from "ethers"; -import { formatEther, parseEther, parseUnits } from "ethers/lib/utils"; +import { parseEther, parseUnits } from "ethers/lib/utils"; import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const cryptoPoolGaugeABI = [ diff --git a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts index ebed7ccb..57ffabe8 100644 --- a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -32,6 +32,10 @@ async function main() { await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWBTC-Earn_Proxy")).address) ); + const opUSD3Earn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSD3-Earn_Proxy")).address) + ); + const sushiswapRouter = ( await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) ); @@ -112,18 +116,38 @@ async function main() { ); const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - const ldoUnclaimedForopWETHEarn = parseEther("31.630000000000000000"); + const ldoUnclaimedForopWETHEarn = parseEther("11.280000000000000000"); const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); const sushiBalanceForopWETHInvst = await sushiInstance.balanceOf(opWETHInvst.address); - const oneCRVToUSDC = parseUnits("0.5", "6"); //0.53 crv - usdc - const oneCVXToUSDC = parseUnits("3.2", "6"); // 3.3 - const oneLDOToWETH = parseEther("0.0007"); //0.0008 + const crvBalanceForOPUSD3Earn = await crvInstance.balanceOf(opUSD3Earn.address); + const crvUnclaimedForopUSD3Earn = await cvxMIM3lp3GaugeInstance.earned(opUSD3Earn.address); + const cvxBalanceForopUSD3Earn = await cvxInstance.balanceOf(opUSD3Earn.address); + const cvxUnclaimedForopUSD3Earn = getCVXUnclaimed( + crvUnclaimedForopUSD3Earn, + await cvxInstance.totalSupply(), + BigNumber.from("100000000000000000000000"), + BigNumber.from("1000"), + BigNumber.from("100000000000000000000000000"), + ); + + const oneCRVToUSDC = parseUnits("0.4", "6"); //0.53 crv - usdc + const oneCVXToUSDC = parseUnits("3", "6"); // 3.3 + const oneLDOToWETH = parseEther("0.0008"); //0.0008 const oneSUSHIToUSDC = parseUnits("0.96", "6"); const oneSUSHIToWETH = parseEther("0.0007"); // sushi-dai-weth const oneCRVToWBTC = parseUnits("0", "8"); + const oneCRVToUSD3 = parseUnits("0.5", "18"); + const oneCVXToUSD3 = parseUnits("3", "18"); + + const expectedMinUSD3FromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) + .mul(oneCRVToUSD3) + .div(parseEther("1")); + const expectedMinUSD3FromCVXForopUSD3Earn = BigNumber.from(cvxBalanceForopUSD3Earn.add(cvxUnclaimedForopUSD3Earn)) + .mul(oneCVXToUSD3) + .div(parseEther("1")); const expectedMinUSDCFromCRVForopUSDCEarn = BigNumber.from(crvBalanceForopUSDCEarn.add(crvUnclaimedForopUSDCEarn)) .mul(oneCRVToUSDC) @@ -156,6 +180,35 @@ async function main() { [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.PLAIN_TOKENS.USDC], ); + const uniV3SwapPathForCRVtoWBTC = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 3000, EthereumTokens.PLAIN_TOKENS.USDT, 3000, EthereumTokens.BTC_TOKENS.WBTC], + ); + + const uniV3SwapPathForCRVtoUSD3 = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CRV, + 10000, + EthereumTokens.WRAPPED_TOKENS.WETH, + 500, + EthereumTokens.PLAIN_TOKENS.USDC, + 3000, + EthereumTokens.WRAPPED_TOKENS.THREE_CRV, + ], + ); + + const uniV3SwapPathForCVXtoUSD3 = ethers.utils.solidityPack( + ["address", "uint24", "address", "uint24", "address"], + [ + EthereumTokens.REWARD_TOKENS.CVX, + 10000, + EthereumTokens.PLAIN_TOKENS.USDC, + 3000, + EthereumTokens.WRAPPED_TOKENS.THREE_CRV, + ], + ); + const transactions: MetaTransactionData[] = [ // claim CRV, CVX for opUSDC Earn { @@ -169,12 +222,18 @@ async function main() { value: "0", data: cvxStEthGaugeInstance.interface.encodeFunctionData("getReward", [opWETHEarn.address, true]), }, - // claim CRV for opWBTCEarn + // claim CRV, CVX for opUSD3Earn { - to: crvWSBTCGaugeInstance.address, + to: cvxMIM3lp3GaugeInstance.address, value: "0", - data: crvWSBTCGaugeInstance.interface.encodeFunctionData("claim_rewards", [opWBTCEarn.address]), + data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSD3Earn.address, true]), }, + // // claim CRV for opWBTCEarn + // { + // to: crvWSBTCGaugeInstance.address, + // value: "0", + // data: crvWSBTCGaugeInstance.interface.encodeFunctionData("claim_rewards", [opWBTCEarn.address]), + // }, // harvest CRV for opUSDCEarn { to: opUSDCEarn.address, @@ -228,33 +287,61 @@ async function main() { // value: "0", // data: opWBTCEarn.interface.encodeFunctionData("harvest", [ // crvInstance.address, - // "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", - // false, + // uniswapV3Router, // "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + // true, // "0", // BigNumber.from(timestamp).add("600"), - // [crvInstance.address, EthereumTokens.PLAIN_TOKENS.USDC, EthereumTokens.BTC_TOKENS.WBTC], - // "0x", - // ]), - // }, - // // harvest SUSHI for opWETHInvst - // { - // to: opWETHInvst.address, - // value: "0", - // data: opWETHInvst.interface.encodeFunctionData("harvest", [ - // sushiInstance.address, - // sushiswapRouter.address, - // false, - // expectMinWETHFromSUSHIForopWETHInvst, - // BigNumber.from(timestamp).add("600"), - // [ - // sushiInstance.address, - // EthereumTokens.PLAIN_TOKENS.USDT, - // EthereumTokens.PLAIN_TOKENS.DAI, - // EthereumTokens.WRAPPED_TOKENS.WETH, - // ], - // "0x", + // [/*crvInstance.address, EthereumTokens.PLAIN_TOKENS.USDC, EthereumTokens.BTC_TOKENS.WBTC*/], + // uniV3SwapPathForCRVtoWBTC, // ]), // }, + // harvest CVX for opUSD3Earn + { + to: opUSD3Earn.address, + value: "0", + data: opUSD3Earn.interface.encodeFunctionData("harvest", [ + EthereumTokens.REWARD_TOKENS.CVX, + uniswapV3Router, + true, + expectedMinUSD3FromCVXForopUSD3Earn, + BigNumber.from(timestamp).add("600"), + [], + uniV3SwapPathForCVXtoUSD3, + ]), + }, + // harvest CRV for opUSD3Earn + { + to: opUSD3Earn.address, + value: "0", + data: opUSD3Earn.interface.encodeFunctionData("harvest", [ + EthereumTokens.REWARD_TOKENS.CRV, + uniswapV3Router, + true, + expectedMinUSD3FromCRVForopUSD3Earn, + BigNumber.from(timestamp).add("600"), + [], + uniV3SwapPathForCRVtoUSD3, + ]), + }, + // harvest SUSHI for opWETHInvst + { + to: opWETHInvst.address, + value: "0", + data: opWETHInvst.interface.encodeFunctionData("harvest", [ + sushiInstance.address, + sushiswapRouter.address, + false, + expectMinWETHFromSUSHIForopWETHInvst, + BigNumber.from(timestamp).add("600"), + [ + sushiInstance.address, + // EthereumTokens.PLAIN_TOKENS.USDT, + // EthereumTokens.PLAIN_TOKENS.DAI, + EthereumTokens.WRAPPED_TOKENS.WETH, + ], + "0x", + ]), + }, // // harvest SUSHI for opUSDCInvst // { // to: opUSDCInvst.address, @@ -292,18 +379,24 @@ async function main() { // value: "0", // data: opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), // }, - // // vaultDepositAllToStrategy for opWETHInvst - // { - // to: opWETHInvst.address, - // value: "0", - // data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), - // }, + // vaultDepositAllToStrategy for opWETHInvst + { + to: opWETHInvst.address, + value: "0", + data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, // // vaultDepositAllToStrategy for opUSDCInvst // { // to: opUSDCInvst.address, // value: "0", // data: opUSDCInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), // }, + // vaultDepositAllToStrategy for opWBTCEarn + { + to: opUSD3Earn.address, + value: "0", + data: opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, ]; const safeOwner = ethers.provider.getSigner(0); diff --git a/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts index 9f9ac8cb..cd612e1e 100644 --- a/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts +++ b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts @@ -98,6 +98,8 @@ async function main() { const oneCRVToUSDC = parseUnits("0.5", "6"); //0.53 crv - usdc const oneCVXToUSDC = parseUnits("3.1", "6"); // 3.3 + const oneCRVToUSD3 = parseUnits("0.5", "18"); + const oneCVXToUSD3 = parseUnits("3", "18"); const expectedMinUSDCFromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) .mul(oneCRVToUSDC) @@ -133,6 +135,7 @@ async function main() { ["address", "uint24", "address"], [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.PLAIN_TOKENS.USDC], ); + const crvWSBTCGaugeInstance = await ethers.getContractAt(curveGuageABI, "0x6D787113F23bED1D5e1530402B3f364D0A6e5Af3"); const transactions: MetaTransactionData[] = [ // { // to: compoundComptrollerInstance.address, @@ -270,7 +273,7 @@ async function main() { .add(usdcBalanceForopUSD3Earn), 0, ], - "31000000000000000000", + "11000000000000000000", ]), ], ), @@ -281,6 +284,19 @@ async function main() { ], ]), }, + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("adminCall", [ + // ethers.utils.defaultAbiCoder.encode( + // ["address", "bytes"], + // [ + // crvWSBTCGaugeInstance.address, + // crvWSBTCGaugeInstance.interface.encodeFunctionData("claim_rewards(address,address)", [opWBTCEarn.address, opWBTCEarn.address]) + // ] + // ) + // ]) + // } ]; const safeTransaction = await safeSdk.createTransaction(transactions); @@ -337,3 +353,70 @@ const cvxABI = [ type: "function", }, ]; + +const curveGuageABI = [ + // { + // stateMutability: "nonpayable", + // type: "function", + // name: "claim_rewards", + // inputs: [ + // { + // name: "_addr", + // type: "address", + // }, + // ], + // outputs: [], + // }, + // { + // "stateMutability": "nonpayable", + // "type": "function", + // "name": "claim_rewards", + // "inputs": [], + // "outputs": [] + // }, + { + stateMutability: "nonpayable", + type: "function", + name: "claim_rewards", + inputs: [ + { + name: "_addr", + type: "address", + }, + { + name: "_receiver", + type: "address", + }, + ], + outputs: [], + }, + { + name: "claimable_tokens", + outputs: [ + { + type: "uint256", + name: "", + }, + ], + inputs: [ + { + type: "address", + name: "addr", + }, + ], + stateMutability: "view", + type: "function", + }, + { + name: "minter", + outputs: [ + { + type: "address", + name: "", + }, + ], + inputs: [], + stateMutability: "view", + type: "function", + }, +]; From 9fb5776cd7254a248f37ea2ab080a8ed5409118f Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 11 Jan 2023 15:12:58 -0500 Subject: [PATCH 39/44] feat(test): fix curve adapter tests --- .../adapters/ethereum/CurveHelper.sol | 11 +++ contracts/protocol/tokenization/Vault.sol | 2 +- deploy_mainnet/002_deploy_curvehelper.ts | 41 +++++++++++ deploy_mainnet/023_deploy_opUSDC-Earn.ts | 69 +++++++++++++++++-- helpers/adapters/ethereum/AaveV1Adapter.ts | 4 +- helpers/adapters/ethereum/ConvexAdapter.ts | 10 ++- .../adapters/ethereum/ConvexStakingAdapter.ts | 51 ++++++++++++-- helpers/adapters/ethereum/CurveAdapter.ts | 46 +++++++------ helpers/strategy-manager.ts | 29 +++++++- helpers/utils.ts | 14 ++-- ...9_usdc-mim-sushi_weth-ldo-sushi_harvest.ts | 24 +++---- 11 files changed, 242 insertions(+), 59 deletions(-) diff --git a/contracts/protocol/adapters/ethereum/CurveHelper.sol b/contracts/protocol/adapters/ethereum/CurveHelper.sol index 97647620..13646adc 100644 --- a/contracts/protocol/adapters/ethereum/CurveHelper.sol +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -265,6 +265,17 @@ contract CurveHelper is Modifiers { return ICurveSwap(_pool).calc_token_amount([0, 0, 0, _amount], _isDeposit); } + function getCalc_withdraw_one_coin( + address _pool, + uint256 _lpTokenAmount, + int128 _index + ) external view returns (uint256) { + if (_lpTokenAmount > 0) { + return ICurveSwap(_pool).calc_withdraw_one_coin(_lpTokenAmount, _index); + } + return 0; + } + function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external onlyRiskOperator { uint256 _tokensLen = _tokens.length; require(_tokensLen == _spenders.length, Errors.LENGTH_MISMATCH); diff --git a/contracts/protocol/tokenization/Vault.sol b/contracts/protocol/tokenization/Vault.sol index d6fe4f4a..1ab12ecd 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -661,7 +661,7 @@ contract Vault is // If slippage occurs, reduce _oraUserWithdrawUT by slippage amount if (_receivedStratWithdrawUT < _expectedStratWithdrawUT) { - _oraUserWithdrawUT = _oraUserWithdrawUT.add(_receivedStratWithdrawUT); + _oraUserWithdrawUT = _oraUserWithdrawUT.sub(_expectedStratWithdrawUT.sub(_receivedStratWithdrawUT)); } } uint256 _withdrawFeeUT = calcWithdrawalFeeUT(_oraUserWithdrawUT); diff --git a/deploy_mainnet/002_deploy_curvehelper.ts b/deploy_mainnet/002_deploy_curvehelper.ts index 19e6123c..d3f23c0f 100644 --- a/deploy_mainnet/002_deploy_curvehelper.ts +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -1,6 +1,12 @@ +import hre from "hardhat"; import { BigNumber } from "ethers"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; +import { getAddress, parseEther } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import Curve from "@optyfi/defi-legos/ethereum/curve/index"; +import { CurveHelper, CurveHelper__factory, ERC20, ERC20__factory, Registry } from "../typechain"; +import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; import { waitforme } from "../helpers/utils"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -18,6 +24,7 @@ const func: DeployFunction = async ({ const { deployer } = await getNamedAccounts(); const artifact = await deployments.getArtifact("CurveHelper"); const registryProxyAddress = (await deployments.get("RegistryProxy")).address; + const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); const chainId = await getChainId(); const networkName = network.name; @@ -58,6 +65,40 @@ const func: DeployFunction = async ({ } } } + + const approvalTokens = []; + const approvalSpender = []; + + const curveHelperInstance = ( + await ethers.getContractAt(CurveHelper__factory.abi, (await deployments.get("CurveHelper")).address) + ); + + const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + + const usdcThreeCrvAllowance = await usdcInstance.allowance( + curveHelperInstance.address, + Curve.CurveSwapPool.usdc_3crv.pool, + ); + + if (!usdcThreeCrvAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.pool); + } + + if (approvalTokens.length > 0) { + console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); + console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); + const riskOperatorSigner = await hre.ethers.getSigner(await registryInstance.getRiskOperator()); + if (getAddress(riskOperatorSigner.address) === getAddress(deployer)) { + const tx = await curveHelperInstance.connect(riskOperatorSigner).giveAllowances(approvalTokens, approvalSpender, { + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await tx.wait(1); + } else { + console.log("cannot approve pools as signer is not the governance"); + } + } }; export default func; func.tags = ["CurveHelper"]; diff --git a/deploy_mainnet/023_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts index ae2abcf9..123c69a9 100644 --- a/deploy_mainnet/023_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/023_deploy_opUSDC-Earn.ts @@ -5,11 +5,15 @@ import { getAddress, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import Curve from "@optyfi/defi-legos/ethereum/curve/index"; +import Convex from "@optyfi/defi-legos/ethereum/convex/index"; import EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/index"; +import AaveV2 from "@optyfi/defi-legos/ethereum/aavev2/index"; +import AaveV1 from "@optyfi/defi-legos/ethereum/aave/index"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; -import { ERC20, ERC20__factory, Vault, VaultHelper, VaultHelper__factory, Vault__factory } from "../typechain"; +import { CurveHelper, CurveHelper__factory, ERC20, ERC20__factory, Vault, Vault__factory } from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -156,16 +160,25 @@ const func: DeployFunction = async ({ const vaultInstance = ( await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) ); - const vaultHelperInstance = ( - await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + const curveHelperInstance = ( + await ethers.getContractAt(CurveHelper__factory.abi, (await deployments.get("CurveHelper")).address) ); const approvalTokens = []; const approvalSpender = []; + const BOOSTER_DEPOSIT_POOL = "0xF403C135812408BFbE8713b5A23a04b3D48AAE31"; const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.WETH); const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); const threeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Curve.CurveSwapPool.usdc_3crv.lpToken); + const cvxThreeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools["3pool"].lpToken); + const ausdcInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.usdc.lpToken); + const cusdcInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.usdc.pool); + const usdcAaveV1Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV1.LendingPoolCore.address); + const usdcAaveV2Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const ausdcAaveV2Allowance = await ausdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + + const usdcCompoundAllowance = await usdcInstance.allowance(vaultInstance.address, cusdcInstance.address); const wethSushiswapAllowance = await wethInstance.allowance( vaultInstance.address, EthereumSushiswap.SushiswapRouter.address, @@ -174,12 +187,41 @@ const func: DeployFunction = async ({ vaultInstance.address, EthereumSushiswap.SushiswapRouter.address, ); - const usdcVaultHelperAllowance = await usdcInstance.allowance(vaultInstance.address, vaultHelperInstance.address); + const usdcCurveHelperAllowance = await usdcInstance.allowance(vaultInstance.address, curveHelperInstance.address); + const threeCrvConvexAllowance = await threeCrvInstance.allowance(vaultInstance.address, BOOSTER_DEPOSIT_POOL); + const threeCrvConvexStakingAllowance = await cvxThreeCrvInstance.allowance( + vaultInstance.address, + Convex.pools["3pool"].stakingPool, + ); + const threeCrvCurveAllowance = await threeCrvInstance.allowance( + vaultInstance.address, + Curve.CurveSwapPool.usdc_3crv.pool, + ); const threeCrvCurveGaugeAllowance = await threeCrvInstance.allowance( vaultInstance.address, Curve.CurveSwapPool.usdc_3crv.gauge, ); + if (!usdcAaveV1Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(AaveV1.LendingPoolCore.address); + } + + if (!usdcAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!ausdcAaveV2Allowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(ausdcInstance.address); + approvalSpender.push(AaveV2.LendingPool.address); + } + + if (!usdcCompoundAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(Compound.pools.usdc.pool); + } + if (!wethSushiswapAllowance.gt(parseEther("1000000"))) { approvalTokens.push(wethInstance.address); approvalSpender.push(EthereumSushiswap.SushiswapRouter.address); @@ -190,9 +232,14 @@ const func: DeployFunction = async ({ approvalSpender.push(EthereumSushiswap.SushiswapRouter.address); } - if (!usdcVaultHelperAllowance.gt(parseUnits("1000000", "6"))) { + if (!usdcCurveHelperAllowance.gt(parseUnits("1000000", "6"))) { approvalTokens.push(usdcInstance.address); - approvalSpender.push(vaultHelperInstance.address); + approvalSpender.push(curveHelperInstance.address); + } + + if (!threeCrvCurveAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.pool); } if (!threeCrvCurveGaugeAllowance.gt(parseEther("1000000"))) { @@ -200,6 +247,16 @@ const func: DeployFunction = async ({ approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.gauge); } + if (!threeCrvConvexAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(BOOSTER_DEPOSIT_POOL); + } + + if (!threeCrvConvexStakingAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(cvxThreeCrvInstance.address); + approvalSpender.push(Convex.pools["3pool"].stakingPool); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/helpers/adapters/ethereum/AaveV1Adapter.ts b/helpers/adapters/ethereum/AaveV1Adapter.ts index 2b4868b3..43d55d22 100644 --- a/helpers/adapters/ethereum/AaveV1Adapter.ts +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -115,9 +115,9 @@ export class AaveV1Adapter implements AdapterInterface { getHarvestRewardsPlan( _planner: weirollPlanner, _vaultInstance: Contract, - _vaulltUnderlyingToken: string, + _vaultUnderlyingToken: string, ): weirollPlanner { - throw new Error("not implemented"); + throw new Error(`not implemented for ${_vaultUnderlyingToken}`); } async getOutputTokenBalance( diff --git a/helpers/adapters/ethereum/ConvexAdapter.ts b/helpers/adapters/ethereum/ConvexAdapter.ts index 6e1e3970..f6b27e37 100644 --- a/helpers/adapters/ethereum/ConvexAdapter.ts +++ b/helpers/adapters/ethereum/ConvexAdapter.ts @@ -8,14 +8,12 @@ import { JsonRpcProvider } from "@ethersproject/providers"; export class ConvexAdapter implements AdapterInterface { vaultHelperInstance; - optyFiOracleAddress; readonly boosterDepositPool = "0xF403C135812408BFbE8713b5A23a04b3D48AAE31"; - constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract) { + constructor(vaultHelperContract: Contract) { this.vaultHelperInstance = weirollContract.createContract( new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), ); - this.optyFiOracleAddress = _optyFiOracleAddress; } getDepositPlan( @@ -31,7 +29,7 @@ export class ConvexAdapter implements AdapterInterface { new ethers.Contract(this.boosterDepositPool, IConvexDeposit__factory.abi), ); planner.add( - boosterDepositPoolInstance["deposit(uint256,uint256,bool)"](lpTokenToPoolData[pool], inputTokenAmount, false), + boosterDepositPoolInstance["deposit(uint256,uint256,bool)"](lpTokenToPoolData[pool].pid, inputTokenAmount, false), ); return planner; } @@ -49,7 +47,7 @@ export class ConvexAdapter implements AdapterInterface { new ethers.Contract(this.boosterDepositPool, IConvexDeposit__factory.abi), ); planner.add( - boosterDepositPoolInstance["withdraw(uint256,uint256)"](lpTokenToPoolData[pool], outputTokenAmount, false), + boosterDepositPoolInstance["withdraw(uint256,uint256)"](lpTokenToPoolData[pool].pid, outputTokenAmount), ); return planner; } @@ -108,7 +106,7 @@ export class ConvexAdapter implements AdapterInterface { _vaultInstance: Contract, _vaultUnderlyingToken: string, ): weirollPlanner { - throw new Error("not implemented"); + throw new Error(`not implemented for ${_vaultUnderlyingToken}`); } async getOutputTokenBalance( diff --git a/helpers/adapters/ethereum/ConvexStakingAdapter.ts b/helpers/adapters/ethereum/ConvexStakingAdapter.ts index 8c62808a..957db833 100644 --- a/helpers/adapters/ethereum/ConvexStakingAdapter.ts +++ b/helpers/adapters/ethereum/ConvexStakingAdapter.ts @@ -8,7 +8,7 @@ import { JsonRpcProvider } from "@ethersproject/providers"; import { IConvexStake__factory } from "../../../typechain"; import { getAddress } from "ethers/lib/utils"; -export class CurveSwapPoolAdapter implements AdapterInterface { +export class ConvexStakingAdapter implements AdapterInterface { vaultHelperInstance; optyFiOracleAddress; swapHelperInstance; @@ -46,8 +46,34 @@ export class CurveSwapPoolAdapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): weirollPlanner { - const stakingVault = weirollContract.createContract(new ethers.Contract(pool, IConvexStake__factory.abi)); - planner.add(stakingVault["withdraw(uint256,boll)"](outputTokenAmount, true)); + const abi = [ + { + inputs: [ + { + internalType: "uint256", + name: "amount", + type: "uint256", + }, + { + internalType: "bool", + name: "claim", + type: "bool", + }, + ], + name: "withdraw", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ]; + const stakingVault = weirollContract.createContract(new ethers.Contract(pool, abi)); + planner.add(stakingVault["withdraw(uint256,bool)"](outputTokenAmount, true)); return planner; } @@ -97,8 +123,23 @@ export class CurveSwapPoolAdapter implements AdapterInterface { pool: string, _outputToken: string, ): weirollPlanner { - const stakingVault = weirollContract.createContract(new ethers.Contract(pool, IConvexStake__factory.abi)); - planner.add(stakingVault["getReward"]); + const abi = [ + { + inputs: [], + name: "getReward", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ]; + const stakingVault = weirollContract.createContract(new ethers.Contract(pool, abi)); + planner.add(stakingVault["getReward"]()); return planner; } diff --git a/helpers/adapters/ethereum/CurveAdapter.ts b/helpers/adapters/ethereum/CurveAdapter.ts index aede8a9b..077afd95 100644 --- a/helpers/adapters/ethereum/CurveAdapter.ts +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -7,7 +7,7 @@ import { getAddress } from "ethers/lib/utils"; import { AdapterInterface } from "../AdapterInterface"; import { JsonRpcProvider } from "@ethersproject/providers"; -export class CurveSwapPoolAdapter implements AdapterInterface { +export class CurveAdapter implements AdapterInterface { vaultHelperInstance; curveHelperInstance; @@ -29,7 +29,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { isSwap: boolean, inputTokenAmount: ReturnValue, ): weirollPlanner { - const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens).toNumber(); + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[getAddress(pool)].underlyingTokens).toNumber(); switch (poolRegistry[pool].underlyingTokens.length) { case 2: { switch (tokenIndex) { @@ -42,7 +42,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -60,7 +60,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -85,7 +85,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -103,7 +103,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -121,7 +121,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -146,7 +146,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -164,7 +164,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -182,7 +182,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -200,7 +200,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { ).staticcall(), ); const minimumMintAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](mintAmount, 5).staticcall(), ); planner.add( this.curveHelperInstance[ @@ -235,7 +235,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { poolInstance["calc_withdraw_one_coin(uint256,int128)"](outputTokenAmount, tokenIndex).staticcall(), ); const minimumWithdrawAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](withdrawAmount, 5), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](withdrawAmount, 300).staticcall(), ); planner.add( poolInstance["remove_liquidity_one_coin(uint256,int128,uint256)"]( @@ -256,10 +256,13 @@ export class CurveSwapPoolAdapter implements AdapterInterface { isSwap: boolean, outputTokenAmount: ReturnValue, ): ReturnValue { - const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveSwap__factory.abi)); const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens); const amountUT = planner.add( - poolInstance["calc_withdraw_one_coin(uint256,int128)"](outputTokenAmount, tokenIndex).staticcall(), + this.curveHelperInstance["getCalc_withdraw_one_coin(address,uint256,int128)"]( + pool, + outputTokenAmount, + tokenIndex, + ).staticcall(), ); return amountUT as ReturnValue; } @@ -422,7 +425,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { _vaultInstance: Contract, _vaultUnderlyingToken: string, ): weirollPlanner { - throw new Error("not implemented"); + throw new Error(`not implemented for ${_vaultUnderlyingToken}`); } async getOutputTokenBalance( @@ -451,10 +454,13 @@ export class CurveSwapPoolAdapter implements AdapterInterface { provider: JsonRpcProvider, ): Promise { const poolInstance = new ethers.Contract(pool, ICurveSwap__factory.abi, provider); - return await poolInstance["calc_withdraw_one_coin(uint256,int128)"]( - outputTokenAmount, - this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens), - ); + if (outputTokenAmount.gt(0)) { + return await poolInstance["calc_withdraw_one_coin(uint256,int128)"]( + outputTokenAmount, + this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens), + ); + } + return BigNumber.from("0"); } async getValueInOutputToken( @@ -563,7 +569,7 @@ export class CurveSwapPoolAdapter implements AdapterInterface { } private getTokenIndex(token: string, underlyingTokens: string[]): BigNumber { - for (const [index, underlyingToken] of underlyingTokens) { + for (const [index, underlyingToken] of underlyingTokens.entries()) { if (getAddress(token) === getAddress(underlyingToken)) { return BigNumber.from(index); } diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index a860c176..1837d585 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -6,22 +6,31 @@ import { AdapterInterface } from "./adapters/AdapterInterface"; import { AaveV1Adapter } from "./adapters/ethereum/AaveV1Adapter"; import { Aavev2Adapter } from "./adapters/ethereum/AaveV2Adapter"; import { CompoundAdapter } from "./adapters/ethereum/CompoundAdapter"; +import { CurveAdapter } from "./adapters/ethereum/CurveAdapter"; +import { CurveGaugeAdapter } from "./adapters/ethereum/CurveGaugeAdapter"; +import { ConvexAdapter } from "./adapters/ethereum/ConvexAdapter"; +import { ConvexStakingAdapter } from "./adapters/ethereum/ConvexStakingAdapter"; import { ReturnValue, StrategyStepType, WeirollPlan } from "./type"; export class StrategyManager { + public readonly vaultHelperContract: weirollContract; public readonly compoundAdapterObj: AdapterInterface; public readonly aaveV1AdapterObj: AdapterInterface; public readonly aaveV2AdapterObj: AdapterInterface; - public readonly vaultHelperContract: weirollContract; + public readonly curveAdapterObj: AdapterInterface; + public readonly curveGaugeAdapterObj: AdapterInterface; + public readonly convexAdapterObj: AdapterInterface; + public convexStakingAdapterObj: AdapterInterface; public readonly liquidityPoolToAdapter: { [key: string]: AdapterInterface } = {}; constructor( optyFiOracleAddress: string, vaultHelperInstance: Contract, + swapHelperContract: Contract, compoundHelperContract: Contract, aaveV1HelperContract: Contract, - swapHelperContract: Contract, + curveHelperContract: Contract, ) { this.vaultHelperContract = weirollContract.createContract( new ethers.Contract(vaultHelperInstance.address, vaultHelperInstance.interface), @@ -29,6 +38,19 @@ export class StrategyManager { this.compoundAdapterObj = new CompoundAdapter(optyFiOracleAddress, vaultHelperInstance, compoundHelperContract); this.aaveV1AdapterObj = new AaveV1Adapter(vaultHelperInstance, aaveV1HelperContract); this.aaveV2AdapterObj = new Aavev2Adapter(optyFiOracleAddress, vaultHelperInstance, swapHelperContract); + this.curveAdapterObj = new CurveAdapter(vaultHelperInstance, curveHelperContract); + this.curveGaugeAdapterObj = new CurveGaugeAdapter( + optyFiOracleAddress, + vaultHelperInstance, + curveHelperContract, + swapHelperContract, + ); + this.convexAdapterObj = new ConvexAdapter(vaultHelperInstance); + this.convexStakingAdapterObj = new ConvexStakingAdapter( + optyFiOracleAddress, + vaultHelperInstance, + swapHelperContract, + ); this.liquidityPoolToAdapter["0x52D306e36E3B6B02c153d0266ff0f85d18BCD413"] = this.aaveV2AdapterObj; this.liquidityPoolToAdapter["0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"] = this.compoundAdapterObj; // cDAI this.liquidityPoolToAdapter["0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"] = this.compoundAdapterObj; // cUSDT @@ -38,6 +60,9 @@ export class StrategyManager { this.liquidityPoolToAdapter["0x24a42fD28C976A61Df5D00D0599C34c4f90748c8"] = this.aaveV1AdapterObj; this.liquidityPoolToAdapter["0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4"] = this.compoundAdapterObj; // cCOMP this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; // cYFI + this.liquidityPoolToAdapter["0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7"] = this.curveAdapterObj; // 3Crv pool + this.liquidityPoolToAdapter["0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C"] = this.convexAdapterObj; // cvx3Crv + this.liquidityPoolToAdapter["0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8"] = this.convexStakingAdapterObj; // cvx3Crv staking pool } getDepositPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { diff --git a/helpers/utils.ts b/helpers/utils.ts index 458ec2c5..2578da7a 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -36,6 +36,7 @@ import { AaveV1Helper, CompoundHelper, SwapHelper, + CurveHelper, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -112,9 +113,10 @@ declare module "mocha" { vault: Vault; vaultProxy: InitializableImmutableAdminUpgradeabilityProxy; vaultHelper: VaultHelper; + swapHelper: SwapHelper; aaveV1Helper: AaveV1Helper; compoundHelper: CompoundHelper; - swapHelper: SwapHelper; + curveHelper: CurveHelper; opUSDCearn: Vault; opUSDCearnProxy: InitializableImmutableAdminUpgradeabilityProxy; opWETHearn: Vault; @@ -480,9 +482,11 @@ export async function assertPostVaultDepositState( expect(vaultBalanceAfterUT).to.eq(expectedVaultBalanceAfterUT); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); - expect(vaultBalanceAfterLP).to.closeTo( - expectedVaultBalanceAfterLP, - parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + expect(vaultBalanceAfterLP).gte( + expectedVaultBalanceAfterLP.sub(parseUnits("1", await outputTokenInstance.decimals())), + ); + expect(vaultBalanceAfterLP).lte( + expectedVaultBalanceAfterLP.add(parseUnits("1", await outputTokenInstance.decimals())), ); expect(userBalanceBeforeVT).to.eq(userBalanceAfterVT); expect(userBalanceAfterUT).to.eq(userBalanceBeforeUT); @@ -566,7 +570,7 @@ export async function assertPostUserWithdrawState( expect(userBalanceAfterVT).to.eq(expectedUserBalanceVT); expect(userBalanceAfterUT).to.closeTo( expectedUserBalanceUT, - parseUnits("9", (await underlyingTokenInstance.decimals()) / 2).toNumber(), + parseUnits("9", Math.floor(((await underlyingTokenInstance.decimals()) * 4) / 5)).toNumber(), ); expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); diff --git a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts index 57ffabe8..145a7923 100644 --- a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -116,7 +116,7 @@ async function main() { ); const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - const ldoUnclaimedForopWETHEarn = parseEther("11.280000000000000000"); + const ldoUnclaimedForopWETHEarn = parseEther("21.460000000000000000"); const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); @@ -133,14 +133,14 @@ async function main() { BigNumber.from("100000000000000000000000000"), ); - const oneCRVToUSDC = parseUnits("0.4", "6"); //0.53 crv - usdc - const oneCVXToUSDC = parseUnits("3", "6"); // 3.3 - const oneLDOToWETH = parseEther("0.0008"); //0.0008 + const oneCRVToUSDC = parseUnits("0.6", "6"); //0.53 crv - usdc + const oneCVXToUSDC = parseUnits("3.2", "6"); // 3.3 + const oneLDOToWETH = parseEther("0.001"); //0.0008 const oneSUSHIToUSDC = parseUnits("0.96", "6"); const oneSUSHIToWETH = parseEther("0.0007"); // sushi-dai-weth const oneCRVToWBTC = parseUnits("0", "8"); - const oneCRVToUSD3 = parseUnits("0.5", "18"); - const oneCVXToUSD3 = parseUnits("3", "18"); + const oneCRVToUSD3 = parseUnits("0.6", "18"); + const oneCVXToUSD3 = parseUnits("3.2", "18"); const expectedMinUSD3FromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) .mul(oneCRVToUSD3) @@ -379,12 +379,12 @@ async function main() { // value: "0", // data: opWBTCEarn.interface.encodeFunctionData("vaultDepositAllToStrategy"), // }, - // vaultDepositAllToStrategy for opWETHInvst - { - to: opWETHInvst.address, - value: "0", - data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), - }, + // // vaultDepositAllToStrategy for opWETHInvst + // { + // to: opWETHInvst.address, + // value: "0", + // data: opWETHInvst.interface.encodeFunctionData("vaultDepositAllToStrategy"), + // }, // // vaultDepositAllToStrategy for opUSDCInvst // { // to: opUSDCInvst.address, From fc3a03be8c5ef288e0ad04f5790ec4a79a4290bb Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Thu, 12 Jan 2023 03:08:07 -0500 Subject: [PATCH 40/44] feat(test): convex strategies --- deploy_mainnet/021_deploy_opUSD3-Earn.ts | 13 ++++ .../adapters/ethereum/CurveGaugeAdapter.ts | 2 +- helpers/strategy-manager.ts | 1 + helpers/utils.ts | 73 ++++++++++++------- tasks/actions/print-strategy-hash.ts | 2 +- test/test-opty/3_vault_strategies.spec.ts | 24 +++--- 6 files changed, 76 insertions(+), 39 deletions(-) diff --git a/deploy_mainnet/021_deploy_opUSD3-Earn.ts b/deploy_mainnet/021_deploy_opUSD3-Earn.ts index 7a014908..65942128 100644 --- a/deploy_mainnet/021_deploy_opUSD3-Earn.ts +++ b/deploy_mainnet/021_deploy_opUSD3-Earn.ts @@ -157,12 +157,19 @@ const func: DeployFunction = async ({ const approvalTokens: string[] = []; const approvalSpender: string[] = []; + const threeCrvInstance = ( + await ethers.getContractAt(ERC20__factory.abi, CurveExports.CurveSwapPool.usdc_3crv.lpToken) + ); const tokenInstanceUT = ( await ethers.getContractAt( ERC20__factory.abi, CurveExports.CurveCryptoPool.pools["LUSD3CRV-f_bLUSDLUSD3-f"].tokens[0], ) ); + const threeCrvCurveGaugeAllowance = await threeCrvInstance.allowance( + vaultInstance.address, + CurveExports.CurveSwapPool.usdc_3crv.gauge, + ); const allowanceUT = await tokenInstanceUT.allowance( vaultInstance.address, CurveExports.CurveCryptoPool.pools["LUSD3CRV-f_bLUSDLUSD3-f"].pool, @@ -181,6 +188,12 @@ const func: DeployFunction = async ({ vaultInstance.address, CurveExports.CurveCryptoPool.pools["LUSD3CRV-f_bLUSDLUSD3-f"].pool, ); + + if (!threeCrvCurveGaugeAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(CurveExports.CurveSwapPool.usdc_3crv.gauge); + } + if (!allowanceLP.gt(parseEther("1000000"))) { approvalTokens.push(tokenInstanceLP.address); approvalSpender.push(CurveExports.CurveCryptoPool.pools["LUSD3CRV-f_bLUSDLUSD3-f"].pool); diff --git a/helpers/adapters/ethereum/CurveGaugeAdapter.ts b/helpers/adapters/ethereum/CurveGaugeAdapter.ts index 6d73ee64..1bb52999 100644 --- a/helpers/adapters/ethereum/CurveGaugeAdapter.ts +++ b/helpers/adapters/ethereum/CurveGaugeAdapter.ts @@ -130,7 +130,7 @@ export class CurveGaugeAdapter implements AdapterInterface { ).staticcall(), ); const minimumOutputAmount = planner.add( - this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100), + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100).staticcall(), ); let univ3Path; switch (getAddress(vaultUnderlyingToken)) { diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index 1837d585..f13b8c8e 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -61,6 +61,7 @@ export class StrategyManager { this.liquidityPoolToAdapter["0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4"] = this.compoundAdapterObj; // cCOMP this.liquidityPoolToAdapter["0x80a2AE356fc9ef4305676f7a3E2Ed04e12C33946"] = this.compoundAdapterObj; // cYFI this.liquidityPoolToAdapter["0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7"] = this.curveAdapterObj; // 3Crv pool + this.liquidityPoolToAdapter["0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A"] = this.curveGaugeAdapterObj; // 3Crv gauge this.liquidityPoolToAdapter["0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C"] = this.convexAdapterObj; // cvx3Crv this.liquidityPoolToAdapter["0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8"] = this.convexStakingAdapterObj; // cvx3Crv staking pool } diff --git a/helpers/utils.ts b/helpers/utils.ts index 2578da7a..a710d628 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -7,6 +7,8 @@ import { Artifact } from "hardhat/types"; import { MerkleTree } from "merkletreejs"; import keccak256 from "keccak256"; import { ethers } from "ethers"; +import Curve from "@optyfi/defi-legos/ethereum/curve/index"; +import Convex from "@optyfi/defi-legos/ethereum/convex/index"; import { AaveV1Adapter, AaveV1ETHGateway, @@ -50,7 +52,7 @@ import { RiskManagerV2 } from "./types/riskManagerv2"; import { StrategyManager } from "./strategy-manager"; import { StrategyStepType } from "./type"; import { JsonRpcProvider } from "@ethersproject/providers"; -import { parseEther, parseUnits } from "ethers/lib/utils"; +import { getAddress, parseEther, parseUnits } from "ethers/lib/utils"; // function to get the equivalent hash (as generated by the solidity) of data passed in args export function getSoliditySHA3Hash(argTypes: string[], args: any[]): string { @@ -452,17 +454,14 @@ export async function assertPostVaultDepositState( const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); const expectedVaultBalanceAfterLP = ( - await strategyManager.liquidityPoolToAdapter[steps[steps.length - 1].pool].getValueInOutputToken( + await strategyManager.getValueInOutputToken( + underlyingTokenInstance.address, + steps, vaultInstance, - steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, vaultDepositUT, - steps[steps.length - 1].isSwap, provider, ) ).add(vaultBalanceBeforeLP); - const expectedVaultBalanceAfterUT = vaultBalanceBeforeUT.sub(vaultDepositUT); const expectedPPS = vaultTotalSupplyBeforeVT.eq("0") @@ -482,12 +481,18 @@ export async function assertPostVaultDepositState( expect(vaultBalanceAfterUT).to.eq(expectedVaultBalanceAfterUT); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); - expect(vaultBalanceAfterLP).gte( - expectedVaultBalanceAfterLP.sub(parseUnits("1", await outputTokenInstance.decimals())), - ); - expect(vaultBalanceAfterLP).lte( - expectedVaultBalanceAfterLP.add(parseUnits("1", await outputTokenInstance.decimals())), - ); + let outputTokenDecimals; + if ( + [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( + getAddress(steps[steps.length - 1].pool), + ) + ) { + outputTokenDecimals = 18; + } else { + outputTokenDecimals = await outputTokenInstance.decimals(); + } + expect(vaultBalanceAfterLP).gte(expectedVaultBalanceAfterLP.sub(parseUnits("1", outputTokenDecimals))); + expect(vaultBalanceAfterLP).lte(expectedVaultBalanceAfterLP.add(parseUnits("1", outputTokenDecimals))); expect(userBalanceBeforeVT).to.eq(userBalanceAfterVT); expect(userBalanceAfterUT).to.eq(userBalanceBeforeUT); expect(vaultTotalSupplyAfterVT).to.eq(vaultTotalSupplyBeforeVT); @@ -546,22 +551,28 @@ export async function assertPostUserWithdrawState( .div(expectedTotalSupplyVT); if (!vaultBalanceBeforeUT.gte(expectedUserBalanceUT)) { const expectedStratWithdrawUT = expectedWithdrawAmountUT.sub(vaultBalanceBeforeUT); - const expectedAmountWithdrawLP = await strategyManager.liquidityPoolToAdapter[ - steps[steps.length - 1].pool - ].getValueInOutputToken( + const expectedAmountWithdrawLP = await strategyManager.getValueInOutputToken( + underlyingTokenInstance.address, + steps, vaultInstance, - steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, expectedStratWithdrawUT, - steps[steps.length - 1].isSwap, provider, ); const expectedVaultBalanceLP = vaultBalanceBeforeLP.sub(expectedAmountWithdrawLP); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + let outputTokenDecimals; + if ( + [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( + getAddress(steps[steps.length - 1].pool), + ) + ) { + outputTokenDecimals = 18; + } else { + outputTokenDecimals = await outputTokenInstance.decimals(); + } expect(vaultBalanceAfterLP).to.closeTo( expectedVaultBalanceLP, - parseUnits("9", Math.floor(((await outputTokenInstance.decimals()) * 3) / 4)).toNumber(), + parseUnits("9", Math.floor((outputTokenDecimals * 3) / 4)).toNumber(), ); expect(vaultBalanceBeforeLP).gt(vaultBalanceAfterLP); } @@ -607,13 +618,11 @@ export async function assertPostVaultWithdrawState( const ppsAfter = await vaultInstance.getPricePerFullShare(); const vaultTotalSupplyAfterVT = await vaultInstance.totalSupply(); const expectedVaultBalanceAfterUT = ( - await strategyManager.liquidityPoolToAdapter[steps[steps.length - 1].pool].getValueInInputToken( + await strategyManager.getValueInInputToken( + underlyingTokenInstance.address, + steps, vaultInstance, - steps.length === 1 ? underlyingTokenInstance.address : steps[steps.length - 2].outputToken, - steps[steps.length - 1].pool, - steps[steps.length - 1].outputToken, vaultWithdrawLP, - steps[steps.length - 1].isSwap, provider, ) ).add(vaultBalanceBeforeUT); @@ -643,9 +652,19 @@ export async function assertPostVaultWithdrawState( parseUnits("9", (await underlyingTokenInstance.decimals()) / 2).toNumber(), ); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + let outputTokenDecimals; + if ( + [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( + getAddress(steps[steps.length - 1].pool), + ) + ) { + outputTokenDecimals = 18; + } else { + outputTokenDecimals = await outputTokenInstance.decimals(); + } expect(vaultBalanceAfterLP).to.closeTo( expectedVaultBalanceAfterLP, - parseUnits("9", (await outputTokenInstance.decimals()) / 2).toNumber(), + parseUnits("9", outputTokenDecimals / 2).toNumber(), ); expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); diff --git a/tasks/actions/print-strategy-hash.ts b/tasks/actions/print-strategy-hash.ts index 534e8687..84a098fb 100644 --- a/tasks/actions/print-strategy-hash.ts +++ b/tasks/actions/print-strategy-hash.ts @@ -21,7 +21,7 @@ task(TASKS.ACTION_TASKS.PRINT_STRATEGY_HASH.NAME, TASKS.ACTION_TASKS.PRINT_STRAT const tokenInstance = await hre.ethers.getContractAt(ERC20__factory.abi, token); let tokenSymbol = (await tokenInstance.symbol()).toUpperCase(); - tokenSymbol = tokenSymbol === "3Crv" ? "USD3" : tokenSymbol; + tokenSymbol = tokenSymbol === "3CRV" ? "USD3" : tokenSymbol; const tokensHash = MULTI_CHAIN_VAULT_TOKENS[chainId][tokenSymbol].hash; for (const riskProfile of Object.keys(StrategiesByTokenByChain[chainId])) { if ( diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index e45a0db2..aa03c93d 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -34,6 +34,7 @@ import { CompoundHelper__factory, SwapHelper__factory, VaultHelper__factory, + CurveHelper__factory, } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; @@ -59,6 +60,12 @@ describe(`${fork}-Vault-rev7`, () => { this.vaultHelper = ( await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) ); + this.swapHelper = await ethers.getContractAt( + SwapHelper__factory.abi, + ( + await deployments.get("SwapHelper") + ).address, + ); this.aaveV1Helper = await ethers.getContractAt( AaveV1Helper__factory.abi, ( @@ -71,19 +78,20 @@ describe(`${fork}-Vault-rev7`, () => { await deployments.get("CompoundHelper") ).address, ); - this.swapHelper = await ethers.getContractAt( - SwapHelper__factory.abi, + this.curveHelper = await ethers.getContractAt( + CurveHelper__factory.abi, ( - await deployments.get("SwapHelper") + await deployments.get("CurveHelper") ).address, ); this.strategyManager = new StrategyManager( (await deployments.get("OptyFiOracle")).address, this.vaultHelper, + this.swapHelper, this.compoundHelper, this.aaveV1Helper, - this.swapHelper, + this.curveHelper, ); this.registry = ( await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) @@ -146,13 +154,7 @@ describe(`${fork}-Vault-rev7`, () => { }); describe(`${fork}-Vault-rev7 strategies`, () => { for (const riskProfile of Object.keys(StrategiesByTokenByChain[fork])) { - if (riskProfile !== "Save") { - continue; - } for (const token of Object.keys(StrategiesByTokenByChain[fork][riskProfile])) { - if (!["WETH"].includes(token)) { - continue; - } if (IGNORE_VAULTS?.split(",").includes(MultiChainVaults[fork][riskProfile][token].symbol)) { continue; } @@ -286,6 +288,8 @@ describe(`${fork}-Vault-rev7`, () => { const decimals = await this.tokens[token].decimals(); if (_userDepositInDecimals.eq(BigNumber.from("0"))) { _userDepositInDecimals = BigNumber.from("3").mul(parseUnits("1", decimals)); + } else { + _userDepositInDecimals = _userDepositInDecimals.mul(3); } const _userDeposit = new BN(_userDepositInDecimals.toString()).div( new BN(to_10powNumber_BN(await this.tokens[token].decimals()).toString()), From bff302593b0d204e766b3536b07b872b6baac707 Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 16 Jan 2023 14:47:17 -0500 Subject: [PATCH 41/44] feat(strategy): test frax --- .../adapters/ethereum/CurveHelper.sol | 1 + contracts/protocol/tokenization/VM.sol | 26 +- .../protocol/tokenization/VaultWeiroll.sol | 262 ------------ deploy_mainnet/002_deploy_curvehelper.ts | 13 + deploy_mainnet/023_deploy_opUSDC-Earn.ts | 40 ++ hardhat.config.ts | 2 +- helpers/adapters/ethereum/ConvexAdapter.ts | 3 + helpers/adapters/ethereum/CurveAdapter.ts | 9 +- helpers/data/adapter-with-strategies.ts | 390 ++++++++++++++++-- helpers/strategy-manager.ts | 7 +- helpers/utils.ts | 30 +- ...9_usdc-mim-sushi_weth-ldo-sushi_harvest.ts | 8 +- test/test-opty/vaultweiroll.spec.ts | 225 ---------- 13 files changed, 445 insertions(+), 571 deletions(-) delete mode 100644 contracts/protocol/tokenization/VaultWeiroll.sol delete mode 100644 test/test-opty/vaultweiroll.spec.ts diff --git a/contracts/protocol/adapters/ethereum/CurveHelper.sol b/contracts/protocol/adapters/ethereum/CurveHelper.sol index 13646adc..1341b92a 100644 --- a/contracts/protocol/adapters/ethereum/CurveHelper.sol +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -265,6 +265,7 @@ contract CurveHelper is Modifiers { return ICurveSwap(_pool).calc_token_amount([0, 0, 0, _amount], _isDeposit); } + // solhint-disable-next-line func-name-mixedcase function getCalc_withdraw_one_coin( address _pool, uint256 _lpTokenAmount, diff --git a/contracts/protocol/tokenization/VM.sol b/contracts/protocol/tokenization/VM.sol index acbabd29..48a0e1fc 100644 --- a/contracts/protocol/tokenization/VM.sol +++ b/contracts/protocol/tokenization/VM.sol @@ -98,18 +98,7 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert( - string( - abi.encodePacked( - "Execution Failed : command_index=", - string("0"), - ", target=", - address(uint160(uint256(command))), - ", message=", - outdata.length > 0 ? string(outdata) : "Unknown" - ) - ) - ); + revert("_writeExecute"); } if (flags & FLAG_TUPLE_RETURN != 0) { @@ -160,18 +149,7 @@ abstract contract VM { outdata := add(outdata, 68) } } - revert( - string( - abi.encodePacked( - "Execution Failed : command_index=", - string("0"), - ", target=", - address(uint160(uint256(command))), - ", message=", - outdata.length > 0 ? string(outdata) : "Unknown" - ) - ) - ); + revert("_readExecute"); } if (flags & FLAG_TUPLE_RETURN != 0) { diff --git a/contracts/protocol/tokenization/VaultWeiroll.sol b/contracts/protocol/tokenization/VaultWeiroll.sol deleted file mode 100644 index 4e4be362..00000000 --- a/contracts/protocol/tokenization/VaultWeiroll.sol +++ /dev/null @@ -1,262 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.12; -pragma experimental ABIEncoderV2; - -import "./VM.sol"; -import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol"; -import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; -import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import { Constants } from "../../utils/Constants.sol"; -import { EnumerableSet } from "@openzeppelin/contracts/utils/EnumerableSet.sol"; - -contract VaultWeiroll is VM, ERC20 { - using SafeMath for uint256; - using SafeERC20 for IERC20; - // command to - struct Inputs { - bytes32[] commands; - bytes[] state; - uint256 outputIndex; - } - - struct Strategy { - bool enable; - bytes32 hash; - Inputs oraValueUT; - Inputs oraValueLP; - Inputs lastStepBalanceLP; - Inputs depositToStrategy; - Inputs withdrawSomeFromStrategy; - Inputs withdrawAllFromStrategy; - } - - mapping(uint256 => Strategy) public strategies; - - EnumerableSet.Bytes32Set internal strategiesArr; - - Inputs internal _oraValueUT; - Inputs internal _oraValueLP; - Inputs internal _lastStepBalanceLP; - Inputs internal _depositToStrategy; - Inputs internal _withdrawSomeFromStrategy; - Inputs internal _withdrawAllFromStrategy; - - uint256 internal _cacheOraAmountLP; - uint256 internal _cacheExpectedStratWithdrawUT; - - IERC20 public underlyingToken; - - constructor( - IERC20 _underlyingToken, - uint8 _decimals, - string memory _name, - string memory _symbol - ) public ERC20(_name, _symbol) { - underlyingToken = _underlyingToken; - _setupDecimals(_decimals); - } - - function setTemp(uint256 _index, Strategy memory _strategy) external { - strategies[_index] = _strategy; - } - - function setOraValueUT( - bytes32[] calldata commands, - bytes[] memory state, - uint256 _outputIndex - ) external { - delete _oraValueUT.commands; - delete _oraValueUT.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _oraValueUT.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _oraValueUT.commands[_i] = commands[_i]; - } - _oraValueUT.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _oraValueUT.state[_i] = state[_i]; - } - _oraValueUT.outputIndex = _outputIndex; - } - - function setOraValueLP(bytes32[] calldata commands, bytes[] memory state) external { - delete _oraValueLP.commands; - delete _oraValueLP.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _oraValueLP.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _oraValueLP.commands[_i] = commands[_i]; - } - _oraValueLP.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _oraValueLP.state[_i] = state[_i]; - } - } - - function setLastStepBalanceLP(bytes32[] calldata commands, bytes[] memory state) external { - delete _lastStepBalanceLP.commands; - delete _lastStepBalanceLP.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _lastStepBalanceLP.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _lastStepBalanceLP.commands[_i] = commands[_i]; - } - _lastStepBalanceLP.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _lastStepBalanceLP.state[_i] = state[_i]; - } - } - - function setDepositToStrategy(bytes32[] calldata commands, bytes[] memory state) external { - delete _depositToStrategy.commands; - delete _depositToStrategy.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _depositToStrategy.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _depositToStrategy.commands[_i] = commands[_i]; - } - _depositToStrategy.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _depositToStrategy.state[_i] = state[_i]; - } - } - - function setWithdrawSomeFromStrategy(bytes32[] calldata commands, bytes[] memory state) external { - delete _withdrawSomeFromStrategy.commands; - delete _withdrawSomeFromStrategy.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _withdrawSomeFromStrategy.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _withdrawSomeFromStrategy.commands[_i] = commands[_i]; - } - _withdrawSomeFromStrategy.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _withdrawSomeFromStrategy.state[_i] = state[_i]; - } - } - - function setWithdrawAllFromStrategy(bytes32[] calldata commands, bytes[] memory state) external { - delete _withdrawAllFromStrategy.commands; - delete _withdrawAllFromStrategy.state; - uint256 _cLen = commands.length; - uint256 _sLen = state.length; - _withdrawAllFromStrategy.commands = new bytes32[](_cLen); - for (uint256 _i; _i < _cLen; _i++) { - _withdrawAllFromStrategy.commands[_i] = commands[_i]; - } - _withdrawAllFromStrategy.state = new bytes[](_sLen); - for (uint256 _i; _i < _sLen; _i++) { - _withdrawAllFromStrategy.state[_i] = state[_i]; - } - } - - function userDepositVault(uint256 _amount) external { - uint256 _oraVaultAndStratValuePreDepositUT = _oraVaultAndStratValueUT(); - underlyingToken.safeTransferFrom(msg.sender, address(this), _amount); - uint256 _mintAmount; - if (_oraVaultAndStratValuePreDepositUT == 0 || totalSupply() == 0) { - _mintAmount = _amount; - } else { - _mintAmount = (_amount.mul(totalSupply())).div(_oraVaultAndStratValuePreDepositUT); - } - _mint(msg.sender, _mintAmount); - } - - function userWithdrawVault(uint256 _amount) external { - uint256 _oraUserWithdrawUT = _amount.mul(_oraVaultAndStratValueUT()).div(totalSupply()); - _burn(msg.sender, _amount); - uint256 _vaultValuePreStratWithdrawUT = balanceUT(); - // if vault does not have sufficient UT, we need to withdraw from strategy - if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { - // withdraw UT shortage from strategy - _cacheExpectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - _cacheOraAmountLP = _getOraSomeValueLP(); - _writeExecute(_withdrawSomeFromStrategy.commands, _withdrawSomeFromStrategy.state); - uint256 _vaultValuePostStratWithdrawUT = balanceUT(); - uint256 _receivedStratWithdrawUT = _vaultValuePostStratWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - - // If slippage occurs, reduce _oraUserWithdrawUT by slippage amount - if (_receivedStratWithdrawUT < _cacheExpectedStratWithdrawUT) { - _oraUserWithdrawUT = _oraUserWithdrawUT.sub( - _cacheExpectedStratWithdrawUT.sub(_receivedStratWithdrawUT) - ); - } - } - underlyingToken.safeTransfer(msg.sender, _oraUserWithdrawUT); - } - - function vaultWithdrawAllFromStrategy() external { - _writeExecute(_withdrawAllFromStrategy.commands, _withdrawAllFromStrategy.state); - } - - function getPPS() external view returns (uint256) { - // if the last strategt step's balance > 0 then execute lastStepBalanceLP - if (getLastStrategyStepBalance() > 0) { - return _oraVaultAndStratValueUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); - } else { - return balanceUT().mul(Constants.WEI_DECIMAL).div(totalSupply()); - } - } - - function getLastStrategyStepBalance() public view returns (uint256) { - bytes[] memory _bal = _readExecute(_lastStepBalanceLP.commands, _lastStepBalanceLP.state); - return abi.decode(_bal[_bal.length - 1], (uint256)); - } - - function balanceUT() public view returns (uint256) { - return underlyingToken.balanceOf(address(this)); - } - - function _oraVaultAndStratValueUT() internal view returns (uint256) { - return _getOraValueUT().add(balanceUT()); - } - - function _oraStratValueUT() internal view returns (uint256) { - return getLastStrategyStepBalance() > 0 ? _getOraValueUT() : 0; - } - - function _getOraValueUT() internal view returns (uint256) { - bytes[] memory _ut = _readExecute(_oraValueUT.commands, _oraValueUT.state); - return abi.decode(_ut[0], (uint256)); - } - - function _getOraSomeValueLP() internal view returns (uint256) { - bytes[] memory _lp = _readExecute(_oraValueLP.commands, _oraValueLP.state); - return abi.decode(_lp[_lp.length - 1], (uint256)); - } - - function vaultDepositAllToStrategy() external { - _writeExecute(_depositToStrategy.commands, _depositToStrategy.state); - } - - function giveAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external { - uint256 _tokensLen = _tokens.length; - require(_tokensLen == _spenders.length, "E1"); - for (uint256 _i; _i < _tokens.length; _i++) { - _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); - } - } - - function removeAllowances(IERC20[] calldata _tokens, address[] calldata _spenders) external { - uint256 _tokensLen = _tokens.length; - require(_tokensLen == _spenders.length, "E2"); - for (uint256 _i; _i < _tokens.length; _i++) { - _tokens[_i].safeApprove(_spenders[_i], 0); - } - } - - function getCacheOraAmountLP() external view returns (uint256) { - return _cacheOraAmountLP; - } - - function getCacheExpectedStratWithdrawUT() external view returns (uint256) { - return _cacheExpectedStratWithdrawUT; - } -} diff --git a/deploy_mainnet/002_deploy_curvehelper.ts b/deploy_mainnet/002_deploy_curvehelper.ts index d3f23c0f..8383564c 100644 --- a/deploy_mainnet/002_deploy_curvehelper.ts +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -74,17 +74,30 @@ const func: DeployFunction = async ({ ); const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const threeCrvInstance = ( + await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.THREE_CRV) + ); const usdcThreeCrvAllowance = await usdcInstance.allowance( curveHelperInstance.address, Curve.CurveSwapPool.usdc_3crv.pool, ); + const threeCrvCurveFraxAllowance = await threeCrvInstance.allowance( + curveHelperInstance.address, + Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].pool, + ); + if (!usdcThreeCrvAllowance.gt(parseEther("1000000"))) { approvalTokens.push(usdcInstance.address); approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.pool); } + if (!threeCrvCurveFraxAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].pool); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/deploy_mainnet/023_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts index 123c69a9..d4c16ad1 100644 --- a/deploy_mainnet/023_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/023_deploy_opUSDC-Earn.ts @@ -173,6 +173,10 @@ const func: DeployFunction = async ({ const cvxThreeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools["3pool"].lpToken); const ausdcInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.usdc.lpToken); const cusdcInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.usdc.pool); + const fraxThreeCrvInstance = ( + await ethers.getContractAt(ERC20__factory.abi, Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].lpToken) + ); + const cvxFraxThreeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools.frax.lpToken); const usdcAaveV1Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV1.LendingPoolCore.address); const usdcAaveV2Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); @@ -188,6 +192,11 @@ const func: DeployFunction = async ({ EthereumSushiswap.SushiswapRouter.address, ); const usdcCurveHelperAllowance = await usdcInstance.allowance(vaultInstance.address, curveHelperInstance.address); + const threeCrvCurveHelperAllowance = await threeCrvInstance.allowance( + vaultInstance.address, + curveHelperInstance.address, + ); + const threeCrvConvexAllowance = await threeCrvInstance.allowance(vaultInstance.address, BOOSTER_DEPOSIT_POOL); const threeCrvConvexStakingAllowance = await cvxThreeCrvInstance.allowance( vaultInstance.address, @@ -202,6 +211,17 @@ const func: DeployFunction = async ({ Curve.CurveSwapPool.usdc_3crv.gauge, ); + const fraxThreeConvexAllowance = await fraxThreeCrvInstance.allowance(vaultInstance.address, BOOSTER_DEPOSIT_POOL); + const fraxThreeCrvCurveHelperAllowance = await fraxThreeCrvInstance.allowance( + vaultInstance.address, + curveHelperInstance.address, + ); + + const cvxFraxThreeCrvConvexStakingAllowance = await cvxFraxThreeCrvInstance.allowance( + vaultInstance.address, + Convex.pools.frax.stakingPool, + ); + if (!usdcAaveV1Allowance.gt(parseUnits("1000000", "6"))) { approvalTokens.push(usdcInstance.address); approvalSpender.push(AaveV1.LendingPoolCore.address); @@ -237,6 +257,11 @@ const func: DeployFunction = async ({ approvalSpender.push(curveHelperInstance.address); } + if (!threeCrvCurveHelperAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(curveHelperInstance.address); + } + if (!threeCrvCurveAllowance.gt(parseEther("1000000"))) { approvalTokens.push(threeCrvInstance.address); approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.pool); @@ -257,6 +282,21 @@ const func: DeployFunction = async ({ approvalSpender.push(Convex.pools["3pool"].stakingPool); } + if (!fraxThreeCrvCurveHelperAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(fraxThreeCrvInstance.address); + approvalSpender.push(curveHelperInstance.address); + } + + if (!fraxThreeConvexAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(fraxThreeCrvInstance.address); + approvalSpender.push(BOOSTER_DEPOSIT_POOL); + } + + if (!cvxFraxThreeCrvConvexStakingAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(cvxFraxThreeCrvInstance.address); + approvalSpender.push(Convex.pools.frax.stakingPool); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/hardhat.config.ts b/hardhat.config.ts index 0489bd86..713ee6a8 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -86,7 +86,7 @@ const config: HardhatUserConfig = { // https://hardhat.org/hardhat-network/#solidity-optimizer-support optimizer: { enabled: true, - runs: 40, + runs: 200, }, }, }, diff --git a/helpers/adapters/ethereum/ConvexAdapter.ts b/helpers/adapters/ethereum/ConvexAdapter.ts index f6b27e37..419a146a 100644 --- a/helpers/adapters/ethereum/ConvexAdapter.ts +++ b/helpers/adapters/ethereum/ConvexAdapter.ts @@ -155,4 +155,7 @@ const lpTokenToPoolData: { [key: string]: { pid: number } } = { "0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C": { pid: 9, }, + "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA": { + pid: 32, + }, }; diff --git a/helpers/adapters/ethereum/CurveAdapter.ts b/helpers/adapters/ethereum/CurveAdapter.ts index 077afd95..ac24e0d1 100644 --- a/helpers/adapters/ethereum/CurveAdapter.ts +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -232,7 +232,11 @@ export class CurveAdapter implements AdapterInterface { const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICurveSwap__factory.abi)); const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens); const withdrawAmount = planner.add( - poolInstance["calc_withdraw_one_coin(uint256,int128)"](outputTokenAmount, tokenIndex).staticcall(), + this.curveHelperInstance["getCalc_withdraw_one_coin(address,uint256,int128)"]( + pool, + outputTokenAmount, + tokenIndex, + ).staticcall(), ); const minimumWithdrawAmount = planner.add( this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](withdrawAmount, 300).staticcall(), @@ -586,4 +590,7 @@ const poolRegistry: { [key: string]: { underlyingTokens: string[] } } = { "0xdAC17F958D2ee523a2206206994597C13D831ec7", ], }, + "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B": { + underlyingTokens: ["0x853d955aCEf822Db058eb8505911ED77F175b99e", "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"], + }, }; diff --git a/helpers/data/adapter-with-strategies.ts b/helpers/data/adapter-with-strategies.ts index e15aa87c..d3f98b6c 100644 --- a/helpers/data/adapter-with-strategies.ts +++ b/helpers/data/adapter-with-strategies.ts @@ -261,8 +261,8 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { description: "The OptyFi vault supplies USDC to the lending pool on Compound Protocol to earn interest in USDC and, potentially, additional rewards in COMP tokens. The earned USDC tokens and any harvested COMP rewards are reinvested into the vault.", }, - "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-Convex-cvx3Crv": { - strategyName: "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-Convex-cvx3Crv", + "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-Convex-cvx3Crv-DEPOSIT-Convex-cvx3crv-gauge": { + strategyName: "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-Convex-cvx3Crv-DEPOSIT-Convex-cvx3crv-gauge", token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", strategy: [ { @@ -281,47 +281,64 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { adapterName: "ConvexFinanceAdapter", protocol: "Convex", }, - ], - riskProfileCode: 1, - name: "3CRV LP Staking on Convex", - description: - "The OptyFi vault supplies USDC to the 3CRV liquidity pool on Curve Finance and obtains the 3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the 3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", - }, - "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f": { - strategyName: - "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", - outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", - isSwap: false, - outputTokenSymbol: "3Crv", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, { - contract: "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B", - outputToken: "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B", + contract: "0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8", + outputToken: "0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8", isSwap: false, - outputTokenSymbol: "FRAX3CRV-f", - adapterName: "CurveMetapoolSwapAdapter", - protocol: "Curve", - }, - { - contract: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", - outputToken: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", - isSwap: false, - outputTokenSymbol: "cvxFRAX3CRV-f", + outputTokenSymbol: "cvx3Crv-gauge", adapterName: "ConvexFinanceAdapter", protocol: "Convex", }, ], riskProfileCode: 1, - name: "FRAX3CRV LP Staking on Convex", + name: "3CRV LP Staking on Convex", description: - "The OptyFi vault supplies USDC to the FRAX3CRV liquidity pool on Curve Finance and obtains the FRAX3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the FRAX3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", + "The OptyFi vault supplies USDC to the 3CRV liquidity pool on Curve Finance and obtains the 3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the 3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", }, + "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f-DEPOSIT-Convex-stake": + { + strategyName: + "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f-DEPOSIT-Convex-stake", + token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + strategy: [ + { + contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", + outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", + isSwap: false, + outputTokenSymbol: "3Crv", + adapterName: "CurveSwapPoolAdapter", + protocol: "Curve", + }, + { + contract: "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B", + outputToken: "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B", + isSwap: false, + outputTokenSymbol: "FRAX3CRV-f", + adapterName: "CurveMetapoolSwapAdapter", + protocol: "Curve", + }, + { + contract: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", + outputToken: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", + isSwap: false, + outputTokenSymbol: "cvxFRAX3CRV-f", + adapterName: "ConvexFinanceAdapter", + protocol: "Convex", + }, + { + contract: "0xB900EF131301B307dB5eFcbed9DBb50A3e209B2e", + outputToken: "0xB900EF131301B307dB5eFcbed9DBb50A3e209B2e", + isSwap: false, + outputTokenSymbol: "cvxFRAX3CRV-f-gauge", + adapterName: "ConvexStakingAdapter", + protocol: "Convex", + }, + ], + riskProfileCode: 1, + name: "FRAX3CRV LP Staking on Convex", + description: + "The OptyFi vault supplies USDC to the FRAX3CRV liquidity pool on Curve Finance and obtains the FRAX3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the FRAX3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", + }, "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f": { strategyName: "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f", @@ -3009,3 +3026,308 @@ export const MultiChainVaults: MultiChainVaultsType = { [NETWORKS_CHAIN_ID[eEVMNetwork.avalanche]]: avalancheVaults, [NETWORKS_CHAIN_ID_HEX[eEVMNetwork.avalanche]]: avalancheVaults, }; + +export const strategyHashReadIndexes: { + [key: string]: { oraValueUTIndex: number; oraValueLPIndex: number; lastStepLPBalanceIndex: number }; +} = { + "0x9d5b0ec470b7cc0292aa6f12b02080fab6963a074f01f19bf163819cb6e38cb6": { + // dai-DEPOSIT-AaveV2-aDAI + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0x209d8398fa428a480aa63498a065daaa46d3d7ef77e2d367194e8a6a4d3ebf9a": { + // dai-DEPOSIT-Compound-cDAI + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x74ceffc4d239683893dd31f6c4bed33f65aafb1bdad2c47d61bae7db81a42e4d": { + // usdt-DEPOSIT-AaveV2-aUSDT + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0x4103355b18f7a7ea81aebc211548510bb077426632fbe4899f4cf162c70ba396": { + // usdt-DEPOSIT-Compound-cUSDT + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x46b9c41c6ff6c82958774b97fc426f046011e4177b8f64ded0d1a704d083b3c6": { + // wbtc-DEPOSIT-AaveV2-aWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0x35f4123193f545465801d2cd7418a60c7cfe6ade80b8be4f941124705fb7a39c": { + // wbtc-DEPOSIT-Compound-cWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xdc405bd6462f69e015108f9c274278cf552f891ed2015820827a672abafd48fc": { + // usdc-DEPOSIT-AaveV2-aUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0x58404c3f191270f62e374653c7aa923e5487ed261523b0aef0a432a01a8ea088": { + // usdc-DEPOSIT-Compound-cUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x514e845e4f1401cfe30f214a1386dfd06a98e15316dde7f669889a82ddffdeb8": { + // weth-DEPOSIT-AaveV2-aWETH + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0x87ed056054f13b934a9864226aa8c1f52ad63a7ad25bb2c74c1f920c9635c04c": { + // weth-DEPOSIT-Compound-cETH + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x33e72ee91a5fcfa11c583c3be5fd45eddbef67a615865be872a394c8305806b4": { + // USDC-usdc-DEPOSIT-AaveV1-aUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0xa1b19fa34fb71db306003f9ba72dc16b2c107535a0ebb3975f094908b8d5f9c4": { + // usdc-3Crv + oraValueUTIndex: 1, + oraValueLPIndex: 2, + lastStepLPBalanceIndex: 1, + }, + "0x962f5a2c0adec65d44b6c7de93d8ad396729b769d4f5d94c7747456859863153": { + // usdc-3crv-stake + oraValueUTIndex: 1, + oraValueLPIndex: 2, + lastStepLPBalanceIndex: 1, + }, + "0xcc1280fc747f012724200b50ba7e674c6d26dccca7b339b2118e4187e70f10ae": { + // usd3-3crv-gauge + oraValueUTIndex: 1, + oraValueLPIndex: 0, + lastStepLPBalanceIndex: 1, + }, + "0xd99e7dc1561021e2f6a6526f082a16432cd44110600e7706a3590f3cb20053cb": { + // usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-Convex-cvx3Crv-DEPOSIT-Convex-cvx3crv-gauge + oraValueUTIndex: 1, + oraValueLPIndex: 2, + lastStepLPBalanceIndex: 1, + }, + "0x58121072e9862448a198a175b3a42d2e83c50ebd19857d5b20efe565862f101f": { + // usdc-DEPOSIT-Curve-3Crv-Convex-cvx3Crv + oraValueUTIndex: 1, + oraValueLPIndex: 2, + lastStepLPBalanceIndex: 1, + }, + "0xd9ea0597f2f2f5a4918a68a60ef6a4abc8219adbe430c8233415a32b4658b1aa": { + // USDC-usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f-stake + oraValueUTIndex: 1, + oraValueLPIndex: 3, + lastStepLPBalanceIndex: 1, + }, + "0x1a51589f4b4d7e8c0b180cc03a180bec364072acb3d473e74148e0a8645fa720": { + // USDC-usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f + oraValueUTIndex: 1, + oraValueLPIndex: 3, + lastStepLPBalanceIndex: 1, + }, + "0xe730dd3e3fb79dbbc0e3309ea466ed5456d2442a9d727a5304d91526f1b99115": { + // USDC-usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x7f4a606bdb57acf36a7b3885df335d058d516e820fb9b9c3ec601b28dac881ac": { + // USDC-usdc-DEPOSIT-Curve_3Crv-DEPOSIT-Curve_USDN-3Crv-DEPOSIT-Convex_CurveUsdn-3Crv + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xc592b22e23c6bd72c7b3ed34396ed41051f7f7be478b4d2b11f657b588b7b99b": { + // USDC-usdc-DEPOSIT-Compound-cUSDC-dAMM-dCUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x6bc8319b2a8fe26c05d78a307ad47d29cfc49563c73a9670addd3463a39c82d4": { + // USDC-usdc-DEPOSIT-dAMM-dUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xf04ccd8132ade155ababcc7558707e687ba9de09a298863eedb8fb6d5f093e2d": { + // USDC-usdc-DEPOSIT-Sushiswap-WETH-DEPOSIT-Compound-cETH + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xa527d077e8da7fa29812630176c559b4d26243c3e365bf08ac3190d5f7620dd8": { + // WETH-weth-DEPOSIT-CurveSwapPool-eCRV-DEPOSIT-Convex-cvxeCRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x9558552b170165e5318b71ed27d1a323d3d27958b3ee8ed1e6552b70df4ecb2b": { + // WETH-weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xc2a070b74e17df73591dd75d00255d928bd989bd1a2bf32ddcba0d467c9da368": { + // WETH-weth-DEPOSIT-CurveSwapPool-eCRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x78adc751570d29a87ace5b66a47fa7d7bfd675885263d91c62d8cb51a7e9e2fb": { + // WETH-weth-DEPOSIT-CurveSwapPool-steCRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xc51fafbe25400cba7415597d7bb033ee9e5765e678b90d557583fa15d612d4ff": { + // WETH-weth-DEPOSIT-Compound-cETH-DEPOSIT-dAMM-dCWETH + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x3d1029e6895ff5617a91aa3a754a4d837e85495f8e5c5076a82f391aefb4870f": { + // WETH-weth-DEPOSIT-dAMM-dWETH + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xd23479645ebe943a9682b6d47c682941edd01b3efd048c976ede8e7c8d124f9d": { + // WETH-weth-DEPOSIT-UniswapV2-USDC-DEPOSIT-Compound-cUSDC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x169858bb623971d8e95c014b2eb7eabd74b61054f218a9118bb3a199b5473b6e": { + // USD3-3Crv-DEPOSIT-Convex-cvx3Crv + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x28e7d2a9e4f8fdb4af62b83502048bd2f8ed90479d08d59129edef563514ddf7": { + // USD3-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f-DEPOSIT-Convex-cvxFRAX3CRV-f + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x5c1aabba365f3cec8d5fab3dfd3870cfcb8d8519863d257bcd45d571eea8b41b": { + // USD3-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x34af0fe7f4f4402eb8cc7741574cb56fb61b429dc6c513dd711a6c12438e312c": { + // USD3-3Crv-DEPOSIT-CurveMetapoolSwapPool-LUSD3CRV-f-CurveCryptoPool-bLUSDLUSD3-f-CurveCryptoGauge-bLUSDLUSD3-f-gauge + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x565d78b41d6405bf914ced726961c011be482e204b9a9c727bc6a30f02f2ead4": { + // USD3-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xfa8e9be61f6087993f79d18ee05ab6de67354a4a11207e7933b875663a25a0ad": { + // USD3-3Crv-DEPOSIT-CurveMetapoolSwapPool-MIM-3LP3CRV + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x6f7fa816dfb53eb6c69f45942e1687728a9ec8764c16cd1e404987d97c0ddc77": { + // USD3-3Crv-DEPOSIT-CurveSwapPool-USDC-DEPOSIT-CurveSwapPool-FRAX-DEPOSIT-dAMM-dFRAX + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xb7cb51e4a9ee742b890d6015f2b3db006aa5fc33eb6b7f6eb4d41b64238be549": { + // USD3-3Crv-DEPOSIT-CurveSwapPool-USDT-DEPOSIT-CurveSwapPool-USDT-DEPOSIT-dAMM-dUSDT + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xe50750078cfbef2576b5f2ca5b81f701e899e9138f24ee93769cdb70ddf48ce6": { + // WBTC-wbtc-DEPOSIT-Curve-crvRenWSBTC-DEPOSIT-Convex-cvxcrvRenWSBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x5c93a95e1ca1b7c5edd4f2ed8eb017661d1d8cc961c0dec93d37ce4cdc169073": { + // WBTC-wbtc-DEPOSIT-Curve-crvRenWBTC-DEPOSIT-Convex-cvxcrvRenWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x34fc4a8fff5422894f940ac0db60dc3e3aaf3f9d3c15c62d7f3479d193b0a2fc": { + // WBTC-wbtc-DEPOSIT-Curve-crvRenWSBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x256dbd9be6cd307045aba664b0cae4a18341d2f4d0e9e51f9b658686260017a7": { + // WBTC-wbtc-DEPOSIT-Curve-crvRenWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xdf3f8ef63f05db6e4b04c3b5a8198d128b61faee8075aed893d76832a0deed6f": { + // WBTC-wbtc-DEPOSIT-dAMM-dWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x44216c2a6ff5f35d0b24f54cfddb2f39f8ab9a7a998bfa4683aa6083dceb9a9a": { + // WBTC-wbtc-DEPOSIT-AaveV2-aWBTC-DEPOSIT-dAMM-dAWBTC + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x9088797ca169aa71efbef61dbada999c1508855dfea7e62fde222933ffdb190b": { + // USDC-usdc-DEPOSIT-SushiswapPool-USDC-WETH-SLP + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x1761fc82fc48c50dd013e8b21e8eddfd963c7edc6473d7806771c3692692668a": { + // USDC-usdc-DEPOSIT-SushiswapPool-USDC-WETH-SLP-DEPOSIT-SushiswapMasterChef + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xf7a93716c5628af5a4bd1a5c743b02e699b6a9810ee2f0baec0fe197f931856c": { + // WETH-weth-DEPOSIT-SushiswapPool-USDC-WETH-SLP + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x3c5b9d8948861c5be71f8d95cfd4f45ef382db65ef1b8da7a3fe54bf3b53a862": { + // WETH-weth-DEPOSIT-SushiswapPool-USDC-WETH-SLP-DEPOSIT-SushiswapMasterChef + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0x7de3006bfe10fe1130dcfd6715c8bfdc2f456b55ea1a61c110d83fce5a428aec": { + // WETH-weth-DEPOSIT-SushiswapPool-WBTC-WETH-SLP + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, + "0xe4616c097c874aeabc748db2dc5055118c040b17cbf86862b4cec849f196d63c": { + // WETH-weth-DEPOSIT-SushiswapPool-WBTC-WETH-SLP-DEPOSIT-SushiswapMasterChef + oraValueUTIndex: 1, + oraValueLPIndex: 1, + lastStepLPBalanceIndex: 1, + }, +}; diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index f13b8c8e..1c36ee79 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -64,6 +64,9 @@ export class StrategyManager { this.liquidityPoolToAdapter["0xbFcF63294aD7105dEa65aA58F8AE5BE2D9d0952A"] = this.curveGaugeAdapterObj; // 3Crv gauge this.liquidityPoolToAdapter["0x30D9410ED1D5DA1F6C8391af5338C93ab8d4035C"] = this.convexAdapterObj; // cvx3Crv this.liquidityPoolToAdapter["0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8"] = this.convexStakingAdapterObj; // cvx3Crv staking pool + this.liquidityPoolToAdapter["0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B"] = this.curveAdapterObj; // frax3crv-f + this.liquidityPoolToAdapter["0xbE0F6478E0E4894CFb14f32855603A083A57c7dA"] = this.convexAdapterObj; // cvxFRAX3Crv-f + this.liquidityPoolToAdapter["0xB900EF131301B307dB5eFcbed9DBb50A3e209B2e"] = this.convexStakingAdapterObj; // cvxFRAX3Crv-f gauge } getDepositPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { @@ -123,7 +126,7 @@ export class StrategyManager { adapterObj.getWithdrawPlan( planner, vaultInstance, - underlyingToken, + iteratorIndex > 0 ? strategySteps[iteratorIndex - 1].outputToken : underlyingToken, strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, @@ -141,7 +144,7 @@ export class StrategyManager { adapterObj.getWithdrawPlan( planner, vaultInstance, - underlyingToken, + iteratorIndex > 0 ? strategySteps[iteratorIndex - 1].outputToken : underlyingToken, strategySteps[iteratorIndex].pool, strategySteps[iteratorIndex].outputToken, strategySteps[iteratorIndex].isSwap, diff --git a/helpers/utils.ts b/helpers/utils.ts index a710d628..c70c1c09 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -54,6 +54,12 @@ import { StrategyStepType } from "./type"; import { JsonRpcProvider } from "@ethersproject/providers"; import { getAddress, parseEther, parseUnits } from "ethers/lib/utils"; +const noDecimalPools = [ + getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), + getAddress(Convex.pools["3pool"].stakingPool), + getAddress(Convex.pools.frax.stakingPool), +]; + // function to get the equivalent hash (as generated by the solidity) of data passed in args export function getSoliditySHA3Hash(argTypes: string[], args: any[]): string { const soliditySHA3Hash = "0x" + abi.soliditySHA3(argTypes, args).toString("hex"); @@ -482,11 +488,7 @@ export async function assertPostVaultDepositState( expect(vaultBalanceAfterUT).to.eq(expectedVaultBalanceAfterUT); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); let outputTokenDecimals; - if ( - [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( - getAddress(steps[steps.length - 1].pool), - ) - ) { + if (noDecimalPools.includes(getAddress(steps[steps.length - 1].pool))) { outputTokenDecimals = 18; } else { outputTokenDecimals = await outputTokenInstance.decimals(); @@ -496,7 +498,7 @@ export async function assertPostVaultDepositState( expect(userBalanceBeforeVT).to.eq(userBalanceAfterVT); expect(userBalanceAfterUT).to.eq(userBalanceBeforeUT); expect(vaultTotalSupplyAfterVT).to.eq(vaultTotalSupplyBeforeVT); - expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 13).toNumber()); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 14).toNumber()); } export async function assertPostUserWithdrawState( @@ -561,11 +563,7 @@ export async function assertPostUserWithdrawState( const expectedVaultBalanceLP = vaultBalanceBeforeLP.sub(expectedAmountWithdrawLP); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); let outputTokenDecimals; - if ( - [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( - getAddress(steps[steps.length - 1].pool), - ) - ) { + if (noDecimalPools.includes(getAddress(steps[steps.length - 1].pool))) { outputTokenDecimals = 18; } else { outputTokenDecimals = await outputTokenInstance.decimals(); @@ -581,7 +579,7 @@ export async function assertPostUserWithdrawState( expect(userBalanceAfterVT).to.eq(expectedUserBalanceVT); expect(userBalanceAfterUT).to.closeTo( expectedUserBalanceUT, - parseUnits("9", Math.floor(((await underlyingTokenInstance.decimals()) * 4) / 5)).toNumber(), + parseUnits("9", Math.ceil(((await underlyingTokenInstance.decimals()) * 4) / 5)).toNumber(), ); expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); @@ -653,11 +651,7 @@ export async function assertPostVaultWithdrawState( ); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); let outputTokenDecimals; - if ( - [getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool)].includes( - getAddress(steps[steps.length - 1].pool), - ) - ) { + if (noDecimalPools.includes(getAddress(steps[steps.length - 1].pool))) { outputTokenDecimals = 18; } else { outputTokenDecimals = await outputTokenInstance.decimals(); @@ -667,7 +661,7 @@ export async function assertPostVaultWithdrawState( parseUnits("9", outputTokenDecimals / 2).toNumber(), ); expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); - expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 9).toNumber()); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 14).toNumber()); } export function getDepositFeeUT(vaultConfigurationV2: BigNumber | string): BigNumber { diff --git a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts index 145a7923..fe4c190b 100644 --- a/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -133,14 +133,14 @@ async function main() { BigNumber.from("100000000000000000000000000"), ); - const oneCRVToUSDC = parseUnits("0.6", "6"); //0.53 crv - usdc - const oneCVXToUSDC = parseUnits("3.2", "6"); // 3.3 + const oneCRVToUSDC = parseUnits("0.7", "6"); //0.53 crv - usdc + const oneCVXToUSDC = parseUnits("3.6", "6"); // 3.3 const oneLDOToWETH = parseEther("0.001"); //0.0008 const oneSUSHIToUSDC = parseUnits("0.96", "6"); const oneSUSHIToWETH = parseEther("0.0007"); // sushi-dai-weth const oneCRVToWBTC = parseUnits("0", "8"); - const oneCRVToUSD3 = parseUnits("0.6", "18"); - const oneCVXToUSD3 = parseUnits("3.2", "18"); + const oneCRVToUSD3 = parseUnits("0.7", "18"); + const oneCVXToUSD3 = parseUnits("3.4", "18"); const expectedMinUSD3FromCRVForopUSD3Earn = BigNumber.from(crvBalanceForOPUSD3Earn.add(crvUnclaimedForopUSD3Earn)) .mul(oneCRVToUSD3) diff --git a/test/test-opty/vaultweiroll.spec.ts b/test/test-opty/vaultweiroll.spec.ts deleted file mode 100644 index e7b87be8..00000000 --- a/test/test-opty/vaultweiroll.spec.ts +++ /dev/null @@ -1,225 +0,0 @@ -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; -import { deployContract, solidity } from "ethereum-waffle"; -import { artifacts, ethers } from "hardhat"; -import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; -import { Contract as weirollContract, Planner as weirollPlanner } from "@weiroll/weiroll.js"; -import { Signers } from "../../helpers/utils"; -import { - CommandBuilder, - CommandBuilder__factory, - CompoundAdapter, - CompoundAdapter__factory, - ERC20, - ERC20__factory, - ICompound, - ICompound__factory, - TestVault, - VaultWeiroll, - VaultWeiroll__factory, -} from "../../typechain"; -import { setTokenBalanceInStorage } from "./utils"; -import { parseUnits } from "ethers/lib/utils"; -import { Contract } from "ethers"; -import chai, { expect } from "chai"; - -chai.use(solidity); - -describe("VaultWeiroll", async function () { - let vaultWeirollUSDC: VaultWeiroll; - let usdc: ERC20; - let cUSDC: ICompound; - let cToken: ERC20; - let compoundAdapter: CompoundAdapter; - let testVault: TestVault; - let compoundContract: Contract; - let cTokenContract: Contract; - let vaultWeirollContract: Contract; - let compoundAdapterContract: Contract; - let testVaultContract: Contract; - let usdcContract: Contract; - before(async function () { - this.signers = {} as Signers; - const signers: SignerWithAddress[] = await ethers.getSigners(); - this.signers.deployer = signers[0]; - this.signers.alice = signers[3]; - usdc = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDT); - const commandBuilderFactory = await ethers.getContractFactory( - CommandBuilder__factory.abi, - CommandBuilder__factory.bytecode, - ); - const commandBuilder = await commandBuilderFactory.deploy(); - - const vaultWeirollUSDCFactory = await ethers.getContractFactory( - VaultWeiroll__factory.abi, - linkLibrary( - VaultWeiroll__factory.bytecode, - "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", - commandBuilder.address, - ), - ); - vaultWeirollUSDC = await vaultWeirollUSDCFactory.deploy( - usdc.address, - await usdc.decimals(), - await usdc.name(), - await usdc.symbol(), - ); - - testVault = await deployContract(this.signers.deployer, await artifacts.readArtifact("TestVault")); - cUSDC = await ethers.getContractAt(ICompound__factory.abi, "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"); - cToken = await ethers.getContractAt(ERC20__factory.abi, "0xf650C3d88D12dB855b8bf7D11Be6C55A4e07dCC9"); - compoundAdapter = ( - await ethers.getContractAt(CompoundAdapter__factory.abi, "0x9680624ad6bf5a34ce496a483400585136c575a4") - ); - await setTokenBalanceInStorage(usdc, this.signers.alice.address, "20"); - await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("20", await usdc.decimals())); - await vaultWeirollUSDC.connect(this.signers.alice).giveAllowances([usdc.address], [cUSDC.address]); - compoundContract = weirollContract.createContract(cUSDC); - cTokenContract = weirollContract.createContract(cToken); - vaultWeirollContract = weirollContract.createContract(vaultWeirollUSDC); - compoundAdapterContract = weirollContract.createContract(compoundAdapter); - testVaultContract = weirollContract.createContract(testVault); - usdcContract = weirollContract.createContract(usdc); - }); - - it("userDepositVault", async function () { - // oraValueUT planner - const oraValueUTPlanner = new weirollPlanner(); - const lpTokenBalance = oraValueUTPlanner.add( - // cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall() - testVaultContract.getERC20Balance(vaultWeirollUSDC.address, cTokenContract.address).staticcall(), - ); - const amountInToken = oraValueUTPlanner.add( - compoundAdapterContract.getSomeAmountInToken(usdc.address, cUSDC.address, lpTokenBalance).staticcall(), - ); - oraValueUTPlanner.add(testVaultContract.pureFunctionUint256(amountInToken).staticcall()); - await vaultWeirollUSDC.setOraValueUT(oraValueUTPlanner.plan().commands, oraValueUTPlanner.plan().state, 0); - - // lastStepBalanceLP planner - const lastStepBalanceLPPlanner = new weirollPlanner(); - const lastStepBalance = lastStepBalanceLPPlanner.add( - // cTokenContract.balanceOf(vaultWeirollUSDC.address).staticcall(), - testVaultContract.getERC20Balance(vaultWeirollUSDC.address, cTokenContract.address).staticcall(), - ); - lastStepBalanceLPPlanner.add(testVaultContract.pureFunctionUint256(lastStepBalance).staticcall()); - await vaultWeirollUSDC.setLastStepBalanceLP( - lastStepBalanceLPPlanner.plan().commands, - lastStepBalanceLPPlanner.plan().state, - ); - - // depositToStrategy planner - const depositToStrategyPlanner = new weirollPlanner(); - const usdcBalance = depositToStrategyPlanner.add(usdcContract.balanceOf(vaultWeirollUSDC.address).staticcall()); - depositToStrategyPlanner.add(compoundContract["mint(uint256)"](usdcBalance)); - await vaultWeirollUSDC.setDepositToStrategy( - depositToStrategyPlanner.plan().commands, - depositToStrategyPlanner.plan().state, - ); - - // oraValueLP planner - const oraValueLPPlanner = new weirollPlanner(); - const expectedStratWithdrawUT = oraValueLPPlanner.add( - vaultWeirollContract.getCacheExpectedStratWithdrawUT().staticcall(), - ); - const amountInLP = oraValueLPPlanner.add( - compoundAdapterContract - .calculateAmountInLPToken(usdc.address, cUSDC.address, expectedStratWithdrawUT) - .staticcall(), - ); - oraValueLPPlanner.add(testVaultContract.pureFunctionUint256(amountInLP).staticcall()); - await vaultWeirollUSDC.setOraValueLP(oraValueLPPlanner.plan().commands, oraValueLPPlanner.plan().state); - - // withdrawSomeFromStrategy planner - const withdrawSomeFromStrategyPlanner = new weirollPlanner(); - const oraAmountLP = withdrawSomeFromStrategyPlanner.add(vaultWeirollContract.getCacheOraAmountLP().staticcall()); - withdrawSomeFromStrategyPlanner.add(compoundContract["redeem(uint256)"](oraAmountLP)); - await vaultWeirollUSDC.setWithdrawSomeFromStrategy( - withdrawSomeFromStrategyPlanner.plan().commands, - withdrawSomeFromStrategyPlanner.plan().state, - ); - - // withdrawAllFromStrategy planner - const withdrawAllFromStrategyPlanner = new weirollPlanner(); - const lpTokenBalanceAll = withdrawAllFromStrategyPlanner.add( - vaultWeirollContract.getLastStrategyStepBalance().staticcall(), - ); - withdrawAllFromStrategyPlanner.add(compoundContract["redeem(uint256)"](lpTokenBalanceAll)); - await vaultWeirollUSDC.setWithdrawAllFromStrategy( - withdrawAllFromStrategyPlanner.plan().commands, - withdrawAllFromStrategyPlanner.plan().state, - ); - - // user deposit no strategy - await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("20", await usdc.decimals()))) - .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs(ethers.constants.AddressZero, this.signers.alice.address, parseUnits("20", await usdc.decimals())); - - // user withdraw no strategy - await expect( - vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("10", await usdc.decimals())), - ) - .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs(this.signers.alice.address, ethers.constants.AddressZero, parseUnits("10", await usdc.decimals())); - expect(await await usdc.balanceOf(this.signers.alice.address)).to.eq(parseUnits("10", await usdc.decimals())); - - // deposit all to strategy - await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); - - // last strategy step balance - console.log("last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); - - // price per full share - console.log("price per full share ", (await vaultWeirollUSDC.getPPS()).toString()); - - // 2. user deposit - await usdc.connect(this.signers.alice).approve(vaultWeirollUSDC.address, parseUnits("10", await usdc.decimals())); - await expect(vaultWeirollUSDC.connect(this.signers.alice).userDepositVault(parseUnits("10", await usdc.decimals()))) - .to.emit(vaultWeirollUSDC, "Transfer") - .withArgs( - ethers.constants.AddressZero, - this.signers.alice.address, - parseUnits("10.000001", await usdc.decimals()), - ); - - // 2. deposit all to strategy - await vaultWeirollUSDC.connect(this.signers.alice).vaultDepositAllToStrategy(); - - // 2. last strategy step balance - console.log("last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); - - // 2. price per full share - console.log("price per full share ", (await vaultWeirollUSDC.getPPS()).toString()); - - // 2. user withdraw - console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); - await expect( - await vaultWeirollUSDC.connect(this.signers.alice).userWithdrawVault(parseUnits("5", await usdc.decimals())), - ) - .to.emit(usdc, "Transfer") - .withArgs(vaultWeirollUSDC.address, this.signers.alice.address, parseUnits("4.999999", await usdc.decimals())); - console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); - console.log("PPS ", (await vaultWeirollUSDC.getPPS()).toString()); - console.log("Total Supply ", (await vaultWeirollUSDC.totalSupply()).toString()); - console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); - console.log("withdraw all from strategy"); - await vaultWeirollUSDC.vaultWithdrawAllFromStrategy(); - console.log("balance USDC ", await usdc.balanceOf(this.signers.alice.address)); - console.log("PPS ", (await vaultWeirollUSDC.getPPS()).toString()); - console.log("Total Supply ", (await vaultWeirollUSDC.totalSupply()).toString()); - console.log("Last strategy step balance ", (await vaultWeirollUSDC.getLastStrategyStepBalance()).toString()); - }); -}); - -// reference : https://github.com/ethers-io/ethers.js/issues/195#issuecomment-1212815642 -function linkLibrary(bytecode: string, name: string, address: string): string { - let linkedBytecode = bytecode; - // eslint-disable-next-line no-useless-escape - const placeholder = `__\$${ethers.utils.solidityKeccak256(["string"], [name]).slice(2, 36)}\$__`; - const formattedAddress = ethers.utils.getAddress(address).toLowerCase().replace("0x", ""); - if (linkedBytecode.indexOf(placeholder) === -1) { - throw new Error(`Unable to find placeholder for library ${name}`); - } - while (linkedBytecode.indexOf(placeholder) !== -1) { - linkedBytecode = linkedBytecode.replace(placeholder, formattedAddress); - } - return linkedBytecode; -} From d4e0612a56297781716dee23c21ab1658878b1ec Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Fri, 20 Jan 2023 16:40:37 -0500 Subject: [PATCH 42/44] test(strategy): mim and steth strat --- .../adapters/ethereum/CurveHelper.sol | 38 +- deploy_mainnet/002_deploy_curvehelper.ts | 40 +- deploy_mainnet/004_deploy_opWETH-Earn.ts | 41 ++ deploy_mainnet/023_deploy_opUSDC-Earn.ts | 21 + helpers/adapters/ethereum/ConvexAdapter.ts | 6 + .../adapters/ethereum/ConvexStakingAdapter.ts | 11 + helpers/adapters/ethereum/CurveAdapter.ts | 33 +- helpers/data/adapter-with-strategies.ts | 619 +++++++++--------- helpers/strategy-manager.ts | 6 + helpers/utils.ts | 4 +- 10 files changed, 506 insertions(+), 313 deletions(-) diff --git a/contracts/protocol/adapters/ethereum/CurveHelper.sol b/contracts/protocol/adapters/ethereum/CurveHelper.sol index 1341b92a..739330c8 100644 --- a/contracts/protocol/adapters/ethereum/CurveHelper.sol +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -7,6 +7,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { IWETH } from "@optyfi/defi-legos/interfaces/misc/contracts/IWETH.sol"; import { ICurveSwap } from "@optyfi/defi-legos/ethereum/curve/contracts/interfacesV0/ICurveSwap.sol"; +import { ICurveETHSwapV1 as ICurveETHSwap } from "@optyfi/defi-legos/ethereum/curve/contracts/ICurveETHSwapV1.sol"; // helper contracts import { Modifiers } from "../../earn-protocol-configuration/contracts/Modifiers.sol"; @@ -17,10 +18,11 @@ import { Errors } from "../../../utils/Errors.sol"; contract CurveHelper is Modifiers { using SafeERC20 for IERC20; - /* solhint-disable no-empty-blocks*/ - constructor(address _registry) public Modifiers(_registry) {} + IWETH public immutable WETH; - /* solhint-enable no-empty-blocks*/ + constructor(address _registry, IWETH _weth) public Modifiers(_registry) { + WETH = _weth; + } // solhint-disable-next-line func-name-mixedcase function addLiquidity_two_coin_zero_index_Curve( @@ -35,7 +37,12 @@ contract CurveHelper is Modifiers { uint256 _underlyingTokenBalanceAfter = IERC20(_underlyingToken).balanceOf(address(this)); uint256 _actualAmount = _underlyingTokenBalanceAfter - _underlyingTokenBalanceBefore; uint256 _balanceBeforeLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); - ICurveSwap(_pool).add_liquidity([_actualAmount, 0], _minMintAmount); + if (_underlyingToken == address(WETH)) { + WETH.withdraw(_actualAmount); + ICurveETHSwap(_pool).add_liquidity{ value: _actualAmount }([_actualAmount, 0], _minMintAmount); + } else { + ICurveSwap(_pool).add_liquidity([_actualAmount, 0], _minMintAmount); + } uint256 _balanceAfterLP = IERC20(_liquidityPoolToken).balanceOf(address(this)); IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } @@ -184,6 +191,25 @@ contract CurveHelper is Modifiers { IERC20(_liquidityPoolToken).safeTransfer(msg.sender, _balanceAfterLP - _balanceBeforeLP); } + function withdrawETH( + address _pool, + address _liquidityPoolToken, + uint256 _amount, + uint256 _minMintAmount, + int128 _tokenIndex + ) external { + uint256 _lpTokenBalanceBefore = IERC20(_liquidityPoolToken).balanceOf(address(this)); + IERC20(_liquidityPoolToken).transferFrom(msg.sender, address(this), _amount); + uint256 _lpTokenBalanceAfter = IERC20(_liquidityPoolToken).balanceOf(address(this)); + uint256 _actualLpTokenAmount = _lpTokenBalanceAfter - _lpTokenBalanceBefore; + uint256 _ethBalanceBefore = address(this).balance; + ICurveETHSwap(_pool).remove_liquidity_one_coin(_actualLpTokenAmount, _tokenIndex, _minMintAmount); + uint256 _ethBalanceAfter = address(this).balance; + uint256 _actualEthReceived = _ethBalanceAfter - _ethBalanceBefore; + WETH.deposit{ value: _actualEthReceived }(); + IERC20(address(WETH)).transfer(msg.sender, _actualEthReceived); + } + // solhint-disable-next-line func-name-mixedcase function getMintAmount_two_coin_zero_index_Curve( address _pool, @@ -292,4 +318,8 @@ contract CurveHelper is Modifiers { _tokens[_i].safeApprove(_spenders[_i], 0); } } + + /* solhint-disable no-empty-blocks*/ + receive() external payable {} + /* solhint-enable no-empty-blocks*/ } diff --git a/deploy_mainnet/002_deploy_curvehelper.ts b/deploy_mainnet/002_deploy_curvehelper.ts index 8383564c..a2c5303d 100644 --- a/deploy_mainnet/002_deploy_curvehelper.ts +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -36,7 +36,7 @@ const func: DeployFunction = async ({ bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress], + args: [registryProxyAddress, EthereumTokens.WRAPPED_TOKENS.WETH], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -50,7 +50,7 @@ const func: DeployFunction = async ({ await tenderly.verify({ name: "CurveHelper", address: curvehelper.address, - constructorArguments: [registryProxyAddress], + constructorArguments: [registryProxyAddress, EthereumTokens.WRAPPED_TOKENS.WETH], contract: "CurveHelper", }); } else if (!["31337"].includes(chainId)) { @@ -59,7 +59,7 @@ const func: DeployFunction = async ({ await run("verify:verify", { name: "CurveHelper", address: curvehelper.address, - constructorArguments: [registryProxyAddress], + constructorArguments: [registryProxyAddress, EthereumTokens.WRAPPED_TOKENS.WETH], contract: "CurveHelper", }); } @@ -74,9 +74,13 @@ const func: DeployFunction = async ({ ); const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.WETH); const threeCrvInstance = ( await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.THREE_CRV) ); + const steCRVInstance = ( + await ethers.getContractAt(ERC20__factory.abi, Curve.CurveSwapPool["eth_eth+steth"].lpToken) + ); const usdcThreeCrvAllowance = await usdcInstance.allowance( curveHelperInstance.address, @@ -88,6 +92,21 @@ const func: DeployFunction = async ({ Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].pool, ); + const threeCrvMimAllowance = await threeCrvInstance.allowance( + curveHelperInstance.address, + Curve.CurveMetapoolSwap.pools["3crv_mim+3crv"].pool, + ); + + const wethsteCRVAllowance = await wethInstance.allowance( + curveHelperInstance.address, + Curve.CurveSwapPool["eth_eth+steth"].pool, + ); + + const steCRVCurveAllowance = await steCRVInstance.allowance( + curveHelperInstance.address, + Curve.CurveSwapPool["eth_eth+steth"].pool, + ); + if (!usdcThreeCrvAllowance.gt(parseEther("1000000"))) { approvalTokens.push(usdcInstance.address); approvalSpender.push(Curve.CurveSwapPool.usdc_3crv.pool); @@ -98,6 +117,21 @@ const func: DeployFunction = async ({ approvalSpender.push(Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].pool); } + if (!threeCrvMimAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + approvalSpender.push(Curve.CurveMetapoolSwap.pools["3crv_mim+3crv"].pool); + } + + if (!wethsteCRVAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(wethInstance.address); + approvalSpender.push(Curve.CurveSwapPool["eth_eth+steth"].pool); + } + + if (!steCRVCurveAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(steCRVInstance.address); + approvalSpender.push(Curve.CurveSwapPool["eth_eth+steth"].pool); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/deploy_mainnet/004_deploy_opWETH-Earn.ts b/deploy_mainnet/004_deploy_opWETH-Earn.ts index 755e4edf..af9d5286 100644 --- a/deploy_mainnet/004_deploy_opWETH-Earn.ts +++ b/deploy_mainnet/004_deploy_opWETH-Earn.ts @@ -5,6 +5,8 @@ import { getAddress, parseEther, parseUnits } from "ethers/lib/utils"; import { BigNumber } from "ethers"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import EthereumUniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; +import Curve from "@optyfi/defi-legos/ethereum/curve/index"; +import Convex from "@optyfi/defi-legos/ethereum/convex/index"; import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; import { waitforme } from "../helpers/utils"; import { ESSENTIAL_CONTRACTS } from "../helpers/constants/essential-contracts-name"; @@ -156,11 +158,20 @@ const func: DeployFunction = async ({ await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) ); + const curveHelperInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("CurveHelper")).address) + ); + const approvalTokens = []; const approvalSpender = []; + const BOOSTER_DEPOSIT_POOL = "0xF403C135812408BFbE8713b5A23a04b3D48AAE31"; const wethInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.WRAPPED_TOKENS.WETH); const usdcInstance = await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.PLAIN_TOKENS.USDC); + const steCRVInstance = ( + await ethers.getContractAt(ERC20__factory.abi, Curve.CurveSwapPool["eth_eth+steth"].lpToken) + ); + const cvxsteCRVInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools.steth.lpToken); const wethSushiswapAllowance = await wethInstance.allowance( vaultInstance.address, @@ -171,6 +182,16 @@ const func: DeployFunction = async ({ EthereumUniswapV2.router02.address, ); + const wethCurveHelperAllowance = await wethInstance.allowance(vaultInstance.address, curveHelperInstance.address); + + const steCRVConvexAllowance = await steCRVInstance.allowance(vaultInstance.address, BOOSTER_DEPOSIT_POOL); + const steCRVCurveHelperAllowance = await steCRVInstance.allowance(vaultInstance.address, curveHelperInstance.address); + + const cvxsteCRVConvexStakingAllownce = await cvxsteCRVInstance.allowance( + vaultInstance.address, + Convex.pools.steth.stakingPool, + ); + if (!wethSushiswapAllowance.gt(parseEther("1000000"))) { approvalTokens.push(wethInstance.address); approvalSpender.push(EthereumUniswapV2.router02.address); @@ -181,6 +202,26 @@ const func: DeployFunction = async ({ approvalSpender.push(EthereumUniswapV2.router02.address); } + if (!wethCurveHelperAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(wethInstance.address); + approvalSpender.push(curveHelperInstance.address); + } + + if (!steCRVConvexAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(steCRVInstance.address); + approvalSpender.push(BOOSTER_DEPOSIT_POOL); + } + + if (!cvxsteCRVConvexStakingAllownce.gt(parseEther("1000000"))) { + approvalTokens.push(cvxsteCRVInstance.address); + approvalSpender.push(Convex.pools.steth.stakingPool); + } + + if (!steCRVCurveHelperAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(steCRVInstance.address); + approvalSpender.push(curveHelperInstance.address); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/deploy_mainnet/023_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts index d4c16ad1..f7de44dc 100644 --- a/deploy_mainnet/023_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/023_deploy_opUSDC-Earn.ts @@ -177,6 +177,10 @@ const func: DeployFunction = async ({ await ethers.getContractAt(ERC20__factory.abi, Curve.CurveMetapoolSwap.pools["3crv_frax+3crv"].lpToken) ); const cvxFraxThreeCrvInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools.frax.lpToken); + const mimThreeCrvLpInstance = ( + await ethers.getContractAt(ERC20__factory.abi, Curve.CurveMetapoolSwap.pools["3crv_mim+3crv"].lpToken) + ); + const cvxmimThreeCrvLpInstance = await ethers.getContractAt(ERC20__factory.abi, Convex.pools.mim.lpToken); const usdcAaveV1Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV1.LendingPoolCore.address); const usdcAaveV2Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); @@ -222,6 +226,13 @@ const func: DeployFunction = async ({ Convex.pools.frax.stakingPool, ); + const cvxMimConvexAllowance = await mimThreeCrvLpInstance.allowance(vaultInstance.address, BOOSTER_DEPOSIT_POOL); + + const mimThreeCrvConvexStakingAllowance = await cvxmimThreeCrvLpInstance.allowance( + vaultInstance.address, + Convex.pools.mim.stakingPool, + ); + if (!usdcAaveV1Allowance.gt(parseUnits("1000000", "6"))) { approvalTokens.push(usdcInstance.address); approvalSpender.push(AaveV1.LendingPoolCore.address); @@ -297,6 +308,16 @@ const func: DeployFunction = async ({ approvalSpender.push(Convex.pools.frax.stakingPool); } + if (!cvxMimConvexAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(mimThreeCrvLpInstance.address); + approvalSpender.push(BOOSTER_DEPOSIT_POOL); + } + + if (!mimThreeCrvConvexStakingAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(cvxmimThreeCrvLpInstance.address); + approvalSpender.push(Convex.pools.mim.stakingPool); + } + if (approvalTokens.length > 0) { console.log(`${approvalTokens.length} tokens to approve ...`, approvalTokens); console.log(`${approvalSpender.length} spender to spend ...`, approvalSpender); diff --git a/helpers/adapters/ethereum/ConvexAdapter.ts b/helpers/adapters/ethereum/ConvexAdapter.ts index 419a146a..be464f80 100644 --- a/helpers/adapters/ethereum/ConvexAdapter.ts +++ b/helpers/adapters/ethereum/ConvexAdapter.ts @@ -158,4 +158,10 @@ const lpTokenToPoolData: { [key: string]: { pid: number } } = { "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA": { pid: 32, }, + "0xabB54222c2b77158CC975a2b715a3d703c256F05": { + pid: 40, + }, + "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0": { + pid: 25, + }, }; diff --git a/helpers/adapters/ethereum/ConvexStakingAdapter.ts b/helpers/adapters/ethereum/ConvexStakingAdapter.ts index 957db833..94d43807 100644 --- a/helpers/adapters/ethereum/ConvexStakingAdapter.ts +++ b/helpers/adapters/ethereum/ConvexStakingAdapter.ts @@ -264,6 +264,17 @@ export class ConvexStakingAdapter implements AdapterInterface { ); break; } + case getAddress(EthereumTokens.WRAPPED_TOKENS.WETH): { + univ3PathCRV = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CRV, 3000, EthereumTokens.WRAPPED_TOKENS.WETH], + ); + univ3PathCVX = ethers.utils.solidityPack( + ["address", "uint24", "address"], + [EthereumTokens.REWARD_TOKENS.CVX, 10000, EthereumTokens.WRAPPED_TOKENS.WETH], + ); + break; + } } planner.add( diff --git a/helpers/adapters/ethereum/CurveAdapter.ts b/helpers/adapters/ethereum/CurveAdapter.ts index ac24e0d1..ed2c1351 100644 --- a/helpers/adapters/ethereum/CurveAdapter.ts +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -1,6 +1,7 @@ import { BigNumber, Contract } from "ethers"; import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { ReturnValue } from "../../type"; import { ICurveSwap__factory } from "../../../typechain"; import { getAddress } from "ethers/lib/utils"; @@ -241,13 +242,25 @@ export class CurveAdapter implements AdapterInterface { const minimumWithdrawAmount = planner.add( this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](withdrawAmount, 300).staticcall(), ); - planner.add( - poolInstance["remove_liquidity_one_coin(uint256,int128,uint256)"]( - outputTokenAmount, - tokenIndex, - minimumWithdrawAmount, - ), - ); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add( + this.curveHelperInstance["withdrawETH(address,address,uint256,uint256,int128)"]( + pool, + outputToken, + outputTokenAmount, + minimumWithdrawAmount, + tokenIndex, + ), + ); + } else { + planner.add( + poolInstance["remove_liquidity_one_coin(uint256,int128,uint256)"]( + outputTokenAmount, + tokenIndex, + minimumWithdrawAmount, + ), + ); + } return planner; } @@ -593,4 +606,10 @@ const poolRegistry: { [key: string]: { underlyingTokens: string[] } } = { "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B": { underlyingTokens: ["0x853d955aCEf822Db058eb8505911ED77F175b99e", "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"], }, + "0x5a6A4D54456819380173272A5E8E9B9904BdF41B": { + underlyingTokens: ["0x99D8a9C45b2ecA8864373A26D1459e3Dff1e17F3", "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"], + }, + "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022": { + underlyingTokens: ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"], + }, }; diff --git a/helpers/data/adapter-with-strategies.ts b/helpers/data/adapter-with-strategies.ts index d3f98b6c..4df01a2b 100644 --- a/helpers/data/adapter-with-strategies.ts +++ b/helpers/data/adapter-with-strategies.ts @@ -339,145 +339,154 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { description: "The OptyFi vault supplies USDC to the FRAX3CRV liquidity pool on Curve Finance and obtains the FRAX3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the FRAX3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", }, - "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f": { - strategyName: - "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", - outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", - isSwap: false, - outputTokenSymbol: "3Crv", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, - { - contract: "0x5a6A4D54456819380173272A5E8E9B9904BdF41B", - outputToken: "0x5a6A4D54456819380173272A5E8E9B9904BdF41B", - isSwap: false, - outputTokenSymbol: "MIM-3LP3CRV-f", - adapterName: "CurveMetapoolSwapAdapter", - protocol: "Curve", - }, - { - contract: "0xabB54222c2b77158CC975a2b715a3d703c256F05", - outputToken: "0xabB54222c2b77158CC975a2b715a3d703c256F05", - isSwap: false, - outputTokenSymbol: "cvxMIM-3LP3CRV-f", - adapterName: "ConvexFinanceAdapter", - protocol: "Convex", - }, - ], - riskProfileCode: 1, - name: "MIM3CRV LP Staking on Convex", - description: - "The OptyFi vault supplies USDC to the MIM3CRV liquidity pool on Curve Finance and obtains the MIM3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the MIM3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", - }, - "usdc-DEPOSIT-Curve_3Crv-DEPOSIT-Curve_USDN-3Crv-DEPOSIT-Convex_CurveUsdn-3Crv": { - strategyName: "usdc-DEPOSIT-Curve_3Crv-DEPOSIT-Curve_USDN-3Crv-DEPOSIT-Convex_CurveUsdn-3Crv", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", - outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", - isSwap: false, - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - outputTokenSymbol: "3Crv", - }, - { - contract: "0x0f9cb53Ebe405d49A0bbdBD291A65Ff571bC83e1", - outputToken: "0x4f3E8F405CF5aFC05D68142F3783bDfE13811522", - isSwap: false, - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - outputTokenSymbol: "usdn3Crv", - }, - { - contract: "0x3689f325E88c2363274E5F3d44b6DaB8f9e1f524", - outputToken: "0x3689f325E88c2363274E5F3d44b6DaB8f9e1f524", - isSwap: false, - adapterName: "ConvexFinanceAdapter", - protocol: "Convex", - outputTokenSymbol: "cvxusdn3CRV", - }, - ], - riskProfileCode: 1, - name: "cvxusdn3CRV staking on Convex", - description: - "The OptyFi vault supplies USDC to the 3Crv liquidity pool on Curve Finance and obtains the 3Crv LP token then it is supplied inturn to its usdn3Crv liquidity pool which accrues yield from the pool’s trading fees and rewards The vault then stakes the usdn3Crv LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", - }, - "usdc-DEPOSIT-Compound-cUSDC-dAMM-dCUSDC": { - strategyName: "usdc-DEPOSIT-Compound-cUSDC-dAMM-dcUSDC", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", - outputToken: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", - isSwap: false, - outputTokenSymbol: "cUSDC", - adapterName: "CompoundAdapter", - protocol: "Compound", - }, - { - contract: "0x5714EB15A226059202CdfA1bF304167e36752862", - outputToken: "0x5714EB15A226059202CdfA1bF304167e36752862", - isSwap: false, - outputTokenSymbol: "dCUSDC", - adapterName: "CompoundAdapter", - protocol: "dAMM", - }, - ], - riskProfileCode: 1, - name: "cUSDC Lending on dAMM", - description: - "The OptyFi vault supplies USDC to the lending pool on Compound finance. Then it lends cUSDC to cUSDC pool on dAMM finance. The earned USDC tokens and any harvested BDAMM rewards are reinvested into the vault.", - }, - "usdc-DEPOSIT-dAMM-dUSDC": { - strategyName: "usdc-DEPOSIT-dAMM-cUSDC", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5", - outputToken: "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5", - isSwap: false, - outputTokenSymbol: "dUSDC", - adapterName: "CompoundAdapter", - protocol: "dAMM", - }, - ], - riskProfileCode: 1, - name: "USDC Lending on dAMM", - description: - "The OptyFi vault supplies USDC to the lending pool on dAMM finance which accrues yield and earns Bonded DAMM rewards. The vault harvests and re-invests the rewards.", - }, - "usdc-DEPOSIT-Sushiswap-WETH-DEPOSIT-Compound-cETH": { - strategyName: "usdc-DEPOSIT-Sushiswap-WETH-DEPOSIT-Compound-cETH", - token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - strategy: [ - { - contract: "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0", - outputToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - isSwap: true, - outputTokenSymbol: "WETH", - adapterName: "SushiswapExchangeAdapterEthereum", - protocol: "Sushiswap", - }, - { - contract: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", - outputToken: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", - isSwap: false, - outputTokenSymbol: "cETH", - adapterName: "CompoundAdapter", - protocol: "Compound", - }, - ], - riskProfileCode: 1, - name: "ETH lending on Compound", - description: - "The OptyFi vault swaps USDC for ETH on Sushiswap then lends ETH to Compound. Any COMP rewards earned are harvest and re-invested into the vault.", - }, + "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f-Convex-stake": + { + strategyName: + "usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f-Convex-stake", + token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + strategy: [ + { + contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", + outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", + isSwap: false, + outputTokenSymbol: "3Crv", + adapterName: "CurveSwapPoolAdapter", + protocol: "Curve", + }, + { + contract: "0x5a6A4D54456819380173272A5E8E9B9904BdF41B", + outputToken: "0x5a6A4D54456819380173272A5E8E9B9904BdF41B", + isSwap: false, + outputTokenSymbol: "MIM-3LP3CRV-f", + adapterName: "CurveMetapoolSwapAdapter", + protocol: "Curve", + }, + { + contract: "0xabB54222c2b77158CC975a2b715a3d703c256F05", + outputToken: "0xabB54222c2b77158CC975a2b715a3d703c256F05", + isSwap: false, + outputTokenSymbol: "cvxMIM-3LP3CRV-f", + adapterName: "ConvexFinanceAdapter", + protocol: "Convex", + }, + { + contract: "0xFd5AbF66b003881b88567EB9Ed9c651F14Dc4771", + outputToken: "0xFd5AbF66b003881b88567EB9Ed9c651F14Dc4771", + isSwap: false, + outputTokenSymbol: "cvxMIM-3LP3CRV-f-stake", + adapterName: "ConvexStakingAdapter", + protocol: "Convex", + }, + ], + riskProfileCode: 1, + name: "MIM3CRV LP Staking on Convex", + description: + "The OptyFi vault supplies USDC to the MIM3CRV liquidity pool on Curve Finance and obtains the MIM3CRV LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the MIM3CRV LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", + }, + // "usdc-DEPOSIT-Curve_3Crv-DEPOSIT-Curve_USDN-3Crv-DEPOSIT-Convex_CurveUsdn-3Crv": { + // strategyName: "usdc-DEPOSIT-Curve_3Crv-DEPOSIT-Curve_USDN-3Crv-DEPOSIT-Convex_CurveUsdn-3Crv", + // token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + // strategy: [ + // { + // contract: "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7", + // outputToken: "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490", + // isSwap: false, + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // outputTokenSymbol: "3Crv", + // }, + // { + // contract: "0x0f9cb53Ebe405d49A0bbdBD291A65Ff571bC83e1", + // outputToken: "0x4f3E8F405CF5aFC05D68142F3783bDfE13811522", + // isSwap: false, + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // outputTokenSymbol: "usdn3Crv", + // }, + // { + // contract: "0x3689f325E88c2363274E5F3d44b6DaB8f9e1f524", + // outputToken: "0x3689f325E88c2363274E5F3d44b6DaB8f9e1f524", + // isSwap: false, + // adapterName: "ConvexFinanceAdapter", + // protocol: "Convex", + // outputTokenSymbol: "cvxusdn3CRV", + // }, + // ], + // riskProfileCode: 1, + // name: "cvxusdn3CRV staking on Convex", + // description: + // "The OptyFi vault supplies USDC to the 3Crv liquidity pool on Curve Finance and obtains the 3Crv LP token then it is supplied inturn to its usdn3Crv liquidity pool which accrues yield from the pool’s trading fees and rewards The vault then stakes the usdn3Crv LP token on Convex Finance to earn additional rewards which are harvested to USDC and reinvested into the vault.", + // }, + // "usdc-DEPOSIT-Compound-cUSDC-dAMM-dCUSDC": { + // strategyName: "usdc-DEPOSIT-Compound-cUSDC-dAMM-dcUSDC", + // token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + // strategy: [ + // { + // contract: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", + // outputToken: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", + // isSwap: false, + // outputTokenSymbol: "cUSDC", + // adapterName: "CompoundAdapter", + // protocol: "Compound", + // }, + // { + // contract: "0x5714EB15A226059202CdfA1bF304167e36752862", + // outputToken: "0x5714EB15A226059202CdfA1bF304167e36752862", + // isSwap: false, + // outputTokenSymbol: "dCUSDC", + // adapterName: "CompoundAdapter", + // protocol: "dAMM", + // }, + // ], + // riskProfileCode: 1, + // name: "cUSDC Lending on dAMM", + // description: + // "The OptyFi vault supplies USDC to the lending pool on Compound finance. Then it lends cUSDC to cUSDC pool on dAMM finance. The earned USDC tokens and any harvested BDAMM rewards are reinvested into the vault.", + // }, + // "usdc-DEPOSIT-dAMM-dUSDC": { + // strategyName: "usdc-DEPOSIT-dAMM-cUSDC", + // token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + // strategy: [ + // { + // contract: "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5", + // outputToken: "0xa3006250a22E1Ca3C3f19fd1FB080C5dc65992c5", + // isSwap: false, + // outputTokenSymbol: "dUSDC", + // adapterName: "CompoundAdapter", + // protocol: "dAMM", + // }, + // ], + // riskProfileCode: 1, + // name: "USDC Lending on dAMM", + // description: + // "The OptyFi vault supplies USDC to the lending pool on dAMM finance which accrues yield and earns Bonded DAMM rewards. The vault harvests and re-invests the rewards.", + // }, + // "usdc-DEPOSIT-Sushiswap-WETH-DEPOSIT-Compound-cETH": { + // strategyName: "usdc-DEPOSIT-Sushiswap-WETH-DEPOSIT-Compound-cETH", + // token: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + // strategy: [ + // { + // contract: "0x397FF1542f962076d0BFE58eA045FfA2d347ACa0", + // outputToken: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // isSwap: true, + // outputTokenSymbol: "WETH", + // adapterName: "SushiswapExchangeAdapterEthereum", + // protocol: "Sushiswap", + // }, + // { + // contract: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + // outputToken: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + // isSwap: false, + // outputTokenSymbol: "cETH", + // adapterName: "CompoundAdapter", + // protocol: "Compound", + // }, + // ], + // riskProfileCode: 1, + // name: "ETH lending on Compound", + // description: + // "The OptyFi vault swaps USDC for ETH on Sushiswap then lends ETH to Compound. Any COMP rewards earned are harvest and re-invested into the vault.", + // }, }, WETH: { "weth-DEPOSIT-AaveV2-aWETH": { @@ -516,34 +525,96 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { description: "The OptyFi vault converts WETH to ETH and supplies ETH to the lending pool on Compound Protocol to earn interest in ETH and, potentially, additional rewards in COMP tokens. The earned COMP tokens are harvested to ETH and reinvested into the vault.", }, - "weth-DEPOSIT-CurveSwapPool-eCRV-DEPOSIT-Convex-cvxeCRV": { - strategyName: "weth-DEPOSIT-CurveSwapPool-eCRV-DEPOSIT-Convex-cvxeCRV", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0xc5424B857f758E906013F3555Dad202e4bdB4567", - outputToken: "0xA3D87FffcE63B53E0d54fAa1cc983B7eB0b74A9c", - isSwap: false, - outputTokenSymbol: "eCRV", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, - { - contract: "0xAF1d4C576bF55f6aE493AEebAcC3a227675e5B98", - outputToken: "0xAF1d4C576bF55f6aE493AEebAcC3a227675e5B98", - isSwap: false, - outputTokenSymbol: "cvxeCRV", - adapterName: "ConvexFinanceAdapter", - protocol: "Convex", - }, - ], - riskProfileCode: 1, - name: "sETH LP Staking on Convex", - description: - "The OptyFi vault supplies WETH to the sETH-ETH liquidity pool on Curve Finance and obtains the sETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the sETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV": { - strategyName: "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV", + // "weth-DEPOSIT-CurveSwapPool-eCRV-DEPOSIT-Convex-cvxeCRV": { + // strategyName: "weth-DEPOSIT-CurveSwapPool-eCRV-DEPOSIT-Convex-cvxeCRV", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0xc5424B857f758E906013F3555Dad202e4bdB4567", + // outputToken: "0xA3D87FffcE63B53E0d54fAa1cc983B7eB0b74A9c", + // isSwap: false, + // outputTokenSymbol: "eCRV", + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // }, + // { + // contract: "0xAF1d4C576bF55f6aE493AEebAcC3a227675e5B98", + // outputToken: "0xAF1d4C576bF55f6aE493AEebAcC3a227675e5B98", + // isSwap: false, + // outputTokenSymbol: "cvxeCRV", + // adapterName: "ConvexFinanceAdapter", + // protocol: "Convex", + // }, + // ], + // riskProfileCode: 1, + // name: "sETH LP Staking on Convex", + // description: + // "The OptyFi vault supplies WETH to the sETH-ETH liquidity pool on Curve Finance and obtains the sETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the sETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", + // }, + // "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV": { + // strategyName: "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022", + // outputToken: "0x06325440D014e39736583c165C2963BA99fAf14E", + // isSwap: false, + // outputTokenSymbol: "steCRV", + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // }, + // { + // contract: "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0", + // outputToken: "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0", + // isSwap: false, + // outputTokenSymbol: "cvxsteCRV", + // adapterName: "ConvexFinanceAdapter", + // protocol: "Convex", + // }, + // ], + // riskProfileCode: 1, + // name: "stETH LP Staking on Convex", + // description: + // "The OptyFi vault supplies WETH to the stETH-ETH liquidity pool on Curve Finance and obtains the stETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the stETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", + // }, + // "weth-DEPOSIT-CurveSwapPool-eCRV": { + // strategyName: "weth-DEPOSIT-CurveSwapPool-eCRV", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0xc5424B857f758E906013F3555Dad202e4bdB4567", + // outputToken: "0xA3D87FffcE63B53E0d54fAa1cc983B7eB0b74A9c", + // isSwap: false, + // outputTokenSymbol: "eCRV", + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // }, + // ], + // riskProfileCode: 1, + // name: "sETH LP Staking on Convex", + // description: + // "The OptyFi vault supplies WETH to the sETH-ETH liquidity pool on Curve Finance and obtains the sETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the sETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", + // }, + // "weth-DEPOSIT-CurveSwapPool-steCRV": { + // strategyName: "weth-DEPOSIT-CurveSwapPool-steCRV", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022", + // outputToken: "0x06325440D014e39736583c165C2963BA99fAf14E", + // isSwap: false, + // outputTokenSymbol: "steCRV", + // adapterName: "CurveSwapPoolAdapter", + // protocol: "Curve", + // }, + // ], + // riskProfileCode: 1, + // name: "stETH LP Staking on Curve", + // description: + // "The OptyFi vault supplies WETH to the stETH-ETH liquidity pool on Curve Finance and obtains the stETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the stETH LP token on Curve Gauge to earn additional rewards which are harvested to WETH and reinvested into the vault.", + // }, + "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV-DEPOSIT-Convex-stake": { + strategyName: "weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV-DEPOSIT-Convex-stake", token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", strategy: [ { @@ -562,66 +633,12 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { adapterName: "ConvexFinanceAdapter", protocol: "Convex", }, - ], - riskProfileCode: 1, - name: "stETH LP Staking on Convex", - description: - "The OptyFi vault supplies WETH to the stETH-ETH liquidity pool on Curve Finance and obtains the stETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the stETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-CurveSwapPool-eCRV": { - strategyName: "weth-DEPOSIT-CurveSwapPool-eCRV", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0xc5424B857f758E906013F3555Dad202e4bdB4567", - outputToken: "0xA3D87FffcE63B53E0d54fAa1cc983B7eB0b74A9c", - isSwap: false, - outputTokenSymbol: "eCRV", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, - ], - riskProfileCode: 1, - name: "sETH LP Staking on Convex", - description: - "The OptyFi vault supplies WETH to the sETH-ETH liquidity pool on Curve Finance and obtains the sETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the sETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-CurveSwapPool-steCRV": { - strategyName: "weth-DEPOSIT-CurveSwapPool-steCRV", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022", - outputToken: "0x06325440D014e39736583c165C2963BA99fAf14E", - isSwap: false, - outputTokenSymbol: "steCRV", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, - ], - riskProfileCode: 1, - name: "stETH LP Staking on Curve", - description: - "The OptyFi vault supplies WETH to the stETH-ETH liquidity pool on Curve Finance and obtains the stETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the stETH LP token on Curve Gauge to earn additional rewards which are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV": { - strategyName: "weth-DEPOSIT-Lido-stETH-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022", - outputToken: "0x06325440D014e39736583c165C2963BA99fAf14E", - isSwap: false, - outputTokenSymbol: "steCRV", - adapterName: "CurveSwapPoolAdapter", - protocol: "Curve", - }, { - contract: "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0", - outputToken: "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0", + contract: "0x0A760466E1B4621579a82a39CB56Dda2F4E70f03", + outputToken: "0x0A760466E1B4621579a82a39CB56Dda2F4E70f03", isSwap: false, - outputTokenSymbol: "cvxsteCRV", - adapterName: "ConvexFinanceAdapter", + outputTokenSymbol: "cvxsteCRV-stake", + adapterName: "ConvexStakingAdapter", protocol: "Convex", }, ], @@ -630,76 +647,76 @@ const mainnetStrategiesByToken: StrategiesByTokenByChainType = { description: "The OptyFi vault supplies WETH to the stETH-ETH liquidity pool on Curve Finance and obtains the stETH LP token which accrues yield from the pool’s trading fees and rewards. The vault then stakes the stETH LP token on Convex Finance to earn additional rewards which are harvested to WETH and reinvested into the vault.", }, - "weth-DEPOSIT-Compound-cETH-DEPOSIT-dAMM-dCWETH": { - strategyName: "weth-DEPOSIT-Compound-cETH-DEPOSIT-dAMM-dCWETH", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", - outputToken: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", - isSwap: false, - outputTokenSymbol: "cETH", - adapterName: "CompoundAdapter", - protocol: "Compound", - }, - { - contract: "0x3Be69a1D7B8821cDcCE90509aBB62D250A5AeFcc", - outputToken: "0x3Be69a1D7B8821cDcCE90509aBB62D250A5AeFcc", - isSwap: false, - outputTokenSymbol: "dCWETH", - adapterName: "CompoundAdapter", - protocol: "dAMM", - }, - ], - riskProfileCode: 1, - name: "cETH Lending on dAMM", - description: - "The OptyFi vault supplies WETH to the lending pool on Compound finance the lends cETH to cETH lending pool on dAMM finance to earn interest in WETH and, potentially, additional rewards in BDAMM tokens. The earned BDAMM tokens are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-dAMM-dWETH": { - strategyName: "weth-DEPOSIT-dAMM-dWETH", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0x118823514681353634FF95837939E783D85B18AF", - outputToken: "0x118823514681353634FF95837939E783D85B18AF", - isSwap: false, - outputTokenSymbol: "dWETH", - adapterName: "CompoundAdapter", - protocol: "dAMM", - }, - ], - riskProfileCode: 1, - name: "WETH Lending on dAMM", - description: - "The OptyFi vault WETH to the lending pool on dAMM finance Protocol to earn interest in WETH and, potentially, additional rewards in BDAMM tokens. The earned BDAMM tokens are harvested to WETH and reinvested into the vault.", - }, - "weth-DEPOSIT-UniswapV2-USDC-DEPOSIT-Compound-cUSDC": { - strategyName: "weth-DEPOSIT-Uniswap-USDC-DEPOSIT-Compound-cUSDC", - token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", - strategy: [ - { - contract: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc", - outputToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", - isSwap: true, - outputTokenSymbol: "USDC", - adapterName: "UniswapV2ExchangeAdapterEthereum", - protocol: "Uniswap", - }, - { - contract: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", - outputToken: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", - isSwap: false, - outputTokenSymbol: "cUSDC", - adapterName: "CompoundAdapter", - protocol: "Compound", - }, - ], - riskProfileCode: 1, - name: "USDC Lending on Compound", - description: - "The OptyFi vault swaps WETH for USDC and lends to Compound. The COMP rewards earned are harvested and re-invested into the vault.", - }, + // "weth-DEPOSIT-Compound-cETH-DEPOSIT-dAMM-dCWETH": { + // strategyName: "weth-DEPOSIT-Compound-cETH-DEPOSIT-dAMM-dCWETH", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + // outputToken: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + // isSwap: false, + // outputTokenSymbol: "cETH", + // adapterName: "CompoundAdapter", + // protocol: "Compound", + // }, + // { + // contract: "0x3Be69a1D7B8821cDcCE90509aBB62D250A5AeFcc", + // outputToken: "0x3Be69a1D7B8821cDcCE90509aBB62D250A5AeFcc", + // isSwap: false, + // outputTokenSymbol: "dCWETH", + // adapterName: "CompoundAdapter", + // protocol: "dAMM", + // }, + // ], + // riskProfileCode: 1, + // name: "cETH Lending on dAMM", + // description: + // "The OptyFi vault supplies WETH to the lending pool on Compound finance the lends cETH to cETH lending pool on dAMM finance to earn interest in WETH and, potentially, additional rewards in BDAMM tokens. The earned BDAMM tokens are harvested to WETH and reinvested into the vault.", + // }, + // "weth-DEPOSIT-dAMM-dWETH": { + // strategyName: "weth-DEPOSIT-dAMM-dWETH", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0x118823514681353634FF95837939E783D85B18AF", + // outputToken: "0x118823514681353634FF95837939E783D85B18AF", + // isSwap: false, + // outputTokenSymbol: "dWETH", + // adapterName: "CompoundAdapter", + // protocol: "dAMM", + // }, + // ], + // riskProfileCode: 1, + // name: "WETH Lending on dAMM", + // description: + // "The OptyFi vault WETH to the lending pool on dAMM finance Protocol to earn interest in WETH and, potentially, additional rewards in BDAMM tokens. The earned BDAMM tokens are harvested to WETH and reinvested into the vault.", + // }, + // "weth-DEPOSIT-UniswapV2-USDC-DEPOSIT-Compound-cUSDC": { + // strategyName: "weth-DEPOSIT-Uniswap-USDC-DEPOSIT-Compound-cUSDC", + // token: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + // strategy: [ + // { + // contract: "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc", + // outputToken: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + // isSwap: true, + // outputTokenSymbol: "USDC", + // adapterName: "UniswapV2ExchangeAdapterEthereum", + // protocol: "Uniswap", + // }, + // { + // contract: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", + // outputToken: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", + // isSwap: false, + // outputTokenSymbol: "cUSDC", + // adapterName: "CompoundAdapter", + // protocol: "Compound", + // }, + // ], + // riskProfileCode: 1, + // name: "USDC Lending on Compound", + // description: + // "The OptyFi vault swaps WETH for USDC and lends to Compound. The COMP rewards earned are harvested and re-invested into the vault.", + // }, }, USD3: { "3Crv-DEPOSIT-Convex-cvx3Crv": { @@ -3132,6 +3149,12 @@ export const strategyHashReadIndexes: { oraValueLPIndex: 3, lastStepLPBalanceIndex: 1, }, + "0x38ad341dcd655e738a7a7f2073e63c54dcecc8620a13005a5c4c0c6581b8dd69": { + // usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveSwapPool-MIM-3LP3CRV-f-DEPOSIT-Convex-cvxMIM-3LP3CRV-f-Convex-stake + oraValueUTIndex: 1, + oraValueLPIndex: 3, + lastStepLPBalanceIndex: 1, + }, "0x1a51589f4b4d7e8c0b180cc03a180bec364072acb3d473e74148e0a8645fa720": { // USDC-usdc-DEPOSIT-CurveSwapPool-3Crv-DEPOSIT-CurveMetapoolSwapPool-FRAX3CRV-f oraValueUTIndex: 1, @@ -3174,10 +3197,10 @@ export const strategyHashReadIndexes: { oraValueLPIndex: 1, lastStepLPBalanceIndex: 1, }, - "0x9558552b170165e5318b71ed27d1a323d3d27958b3ee8ed1e6552b70df4ecb2b": { - // WETH-weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV + "0x9be5e54cb32b066ac47ab45dd0f04ee8aa5835e389478c5eb00050d23a10578e": { + // weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV-DEPOSIT-Convex-stake oraValueUTIndex: 1, - oraValueLPIndex: 1, + oraValueLPIndex: 2, lastStepLPBalanceIndex: 1, }, "0xc2a070b74e17df73591dd75d00255d928bd989bd1a2bf32ddcba0d467c9da368": { diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index 1c36ee79..8a732620 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -67,6 +67,12 @@ export class StrategyManager { this.liquidityPoolToAdapter["0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B"] = this.curveAdapterObj; // frax3crv-f this.liquidityPoolToAdapter["0xbE0F6478E0E4894CFb14f32855603A083A57c7dA"] = this.convexAdapterObj; // cvxFRAX3Crv-f this.liquidityPoolToAdapter["0xB900EF131301B307dB5eFcbed9DBb50A3e209B2e"] = this.convexStakingAdapterObj; // cvxFRAX3Crv-f gauge + this.liquidityPoolToAdapter["0x5a6A4D54456819380173272A5E8E9B9904BdF41B"] = this.curveAdapterObj; // curve mim-3lp3crv-f + this.liquidityPoolToAdapter["0xabB54222c2b77158CC975a2b715a3d703c256F05"] = this.convexAdapterObj; // convex cvxMIM-3LP3CRV-f + this.liquidityPoolToAdapter["0xFd5AbF66b003881b88567EB9Ed9c651F14Dc4771"] = this.convexStakingAdapterObj; // convex cvxMIM-3LP3CRV-f-stake + this.liquidityPoolToAdapter["0xDC24316b9AE028F1497c275EB9192a3Ea0f67022"] = this.curveAdapterObj; // curve steCRV + this.liquidityPoolToAdapter["0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0"] = this.convexAdapterObj; // convex cvxsteCRV + this.liquidityPoolToAdapter["0x0A760466E1B4621579a82a39CB56Dda2F4E70f03"] = this.convexStakingAdapterObj; // convex cvssteCRV-stake } getDepositPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { diff --git a/helpers/utils.ts b/helpers/utils.ts index c70c1c09..9db96dc8 100644 --- a/helpers/utils.ts +++ b/helpers/utils.ts @@ -58,6 +58,8 @@ const noDecimalPools = [ getAddress(Curve.CurveSwapPool.usdc_3crv.gauge), getAddress(Convex.pools["3pool"].stakingPool), getAddress(Convex.pools.frax.stakingPool), + getAddress(Convex.pools.mim.stakingPool), + getAddress(Convex.pools.steth.stakingPool), ]; // function to get the equivalent hash (as generated by the solidity) of data passed in args @@ -647,7 +649,7 @@ export async function assertPostVaultWithdrawState( expect(userBalanceAfterUT).to.eq(expectedUserBalanceUT); expect(vaultBalanceAfterUT).to.closeTo( expectedVaultBalanceAfterUT, - parseUnits("9", (await underlyingTokenInstance.decimals()) / 2).toNumber(), + parseUnits("9", Math.ceil(((await underlyingTokenInstance.decimals()) * 3) / 4)).toNumber(), ); const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); let outputTokenDecimals; From 6389a73402f4e40920eae63b133409e4eb62e60a Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Mon, 30 Jan 2023 15:23:22 -0500 Subject: [PATCH 43/44] test(vault_spec): fix some test --- test/test-opty/2_vault.spec.ts | 499 ++++++++++++++++++++------------- 1 file changed, 302 insertions(+), 197 deletions(-) diff --git a/test/test-opty/2_vault.spec.ts b/test/test-opty/2_vault.spec.ts index 0ba71ce9..5dfa7717 100644 --- a/test/test-opty/2_vault.spec.ts +++ b/test/test-opty/2_vault.spec.ts @@ -2,10 +2,13 @@ import { ethers, deployments, artifacts, getChainId } from "hardhat"; import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signers"; import chai, { expect } from "chai"; import { deployContract, solidity } from "ethereum-waffle"; -import { BigNumber, ContractReceipt, Event } from "ethers"; +import { BigNumber, ContractReceipt, Event, Contract } from "ethers"; import BN from "bignumber.js"; import { getAddress, parseUnits } from "ethers/lib/utils"; import ethereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import { ethers as weirollEthers } from "@weiroll/weiroll.js/node_modules/ethers"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { assertVaultConfiguration, getAccountsMerkleProof, @@ -23,103 +26,73 @@ import { ERC20__factory, Registry, Registry__factory, - RiskManager, - RiskManager__factory, - StrategyProvider, - StrategyProvider__factory, Vault, Vault__factory, TestVault, ERC20Permit, ERC20Permit__factory, IAdapterFull, + StrategyRegistry__factory, + StrategyRegistry, + SwapHelper__factory, + AaveV1Helper__factory, + CompoundHelper__factory, + CurveHelper__factory, + VaultHelper, + VaultHelper__factory, } from "../../typechain"; import { getPermitSignature, getPermitLegacySignature, setTokenBalanceInStorage } from "./utils"; -import { TypedDefiPools } from "../../helpers/data/defiPools"; -import { generateStrategyHashV2 } from "../../helpers/helpers"; +import { generateStrategyHashV2, generateTokenHashV2 } from "../../helpers/helpers"; import { MULTI_CHAIN_VAULT_TOKENS } from "../../helpers/constants/tokens"; -import { eEVMNetwork, NETWORKS_CHAIN_ID } from "../../helper-hardhat-config"; +import { eEVMNetwork, NETWORKS_CHAIN_ID_HEX } from "../../helper-hardhat-config"; import { Artifact } from "hardhat/types"; +import { StrategyManager } from "../../helpers/strategy-manager"; +import { StrategiesByTokenByChain, strategyHashReadIndexes } from "../../helpers/data/adapter-with-strategies"; chai.use(solidity); const fork = process.env.FORK as eEVMNetwork; const UniswapV3RouterAddress = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; //mainnet -const testStrategy: { - [key: string]: { - [name: string]: { steps: { pool: string; outputToken: string; isSwap: boolean }[]; hash: string }; - }; -} = { - [eEVMNetwork.mainnet || NETWORKS_CHAIN_ID[eEVMNetwork.mainnet]]: { - USDC_COMPOUND: { - steps: [ - { - pool: TypedDefiPools.CompoundAdapter.usdc.pool, - outputToken: TypedDefiPools.CompoundAdapter.usdc.lpToken, - isSwap: false, - }, - ], - hash: generateStrategyHashV2( - [ - { - contract: TypedDefiPools.CompoundAdapter.usdc.pool, - outputToken: TypedDefiPools.CompoundAdapter.usdc.lpToken, - isSwap: false, - }, - ], - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.hash, - ), - }, - USDC_AAVEV1: { - steps: [ - { - pool: TypedDefiPools.AaveV1Adapter.usdc.pool, - outputToken: TypedDefiPools.AaveV1Adapter.usdc.lpToken, - isSwap: false, - }, - ], - hash: generateStrategyHashV2( - [ - { - contract: TypedDefiPools.AaveV2Adapter.usdc.pool, - outputToken: TypedDefiPools.AaveV2Adapter.usdc.lpToken, - isSwap: false, - }, - ], - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.hash, - ), - }, - USDC_AAVEV2: { - steps: [ - { - pool: TypedDefiPools.AaveV2Adapter.usdc.pool, - outputToken: TypedDefiPools.AaveV2Adapter.usdc.lpToken, - isSwap: false, - }, - ], - hash: generateStrategyHashV2( - [ - { - contract: TypedDefiPools.CompoundAdapter.usdc.pool, - outputToken: TypedDefiPools.CompoundAdapter.usdc.lpToken, - isSwap: false, - }, - ], - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.hash, - ), - }, - }, - [eEVMNetwork.polygon || NETWORKS_CHAIN_ID[eEVMNetwork.polygon]]: {}, -}; - -const strategyKeys = Object.keys(testStrategy[fork]); - const EIP712_DOMAIN = ethers.utils.id( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", ); const EIP712_REVISION = ethers.utils.id("1"); +const usdcTokenHash = generateTokenHashV2([EthereumTokens.PLAIN_TOKENS.USDC], NETWORKS_CHAIN_ID_HEX[fork]); +const cUSDCStrategySteps = StrategiesByTokenByChain[fork]["Save"]["USDC"]["usdc-DEPOSIT-Compound-cUSDC"].strategy.map( + item => ({ + pool: item.contract, + outputToken: item.outputToken, + isSwap: item.isSwap, + }), +); +const cUSDCStrategyHash = generateStrategyHashV2( + StrategiesByTokenByChain[fork]["Save"]["USDC"]["usdc-DEPOSIT-Compound-cUSDC"].strategy, + usdcTokenHash, +); +const aUSDCV1StrategySteps = StrategiesByTokenByChain[fork]["Earn"]["USDC"]["usdc-DEPOSIT-AaveV1-aUSDC"].strategy.map( + item => ({ + pool: item.contract, + outputToken: item.outputToken, + isSwap: item.isSwap, + }), +); +const aUSDCV1StrategyHash = generateStrategyHashV2( + StrategiesByTokenByChain[fork]["Earn"]["USDC"]["usdc-DEPOSIT-AaveV1-aUSDC"].strategy, + usdcTokenHash, +); +const aUSDCV2StrategySteps = StrategiesByTokenByChain[fork]["Earn"]["USDC"]["usdc-DEPOSIT-AaveV2-aUSDC"].strategy.map( + item => ({ + pool: item.contract, + outputToken: item.outputToken, + isSwap: item.isSwap, + }), +); +const aUSDCV2StrategyHash = generateStrategyHashV2( + StrategiesByTokenByChain[fork]["Earn"]["USDC"]["usdc-DEPOSIT-AaveV2-aUSDC"].strategy, + usdcTokenHash, +); describe(`::${fork}-Vault-rev4`, function () { before(async function () { await deployments.fixture(); @@ -132,10 +105,8 @@ describe(`::${fork}-Vault-rev4`, function () { this.signers.bob = signers[4]; this.signers.eve = signers[10]; const REGISTRY_PROXY_ADDRESS = (await deployments.get("RegistryProxy")).address; - const RISKMANAGER_ADDRESS = (await deployments.get("RiskManager")).address; - const STRATEGYPROVIDER_ADDRESS = (await deployments.get("StrategyProvider")).address; - const OPUSDCEARN_VAULT_ADDRESS = (await deployments.get("opUSDC-Earn")).address; - const DAISAVE_VAULT_ADDRESS = (await deployments.get("opDAI-Save")).address; + const OPUSDCEARN_VAULT_ADDRESS = (await deployments.get("opUSDC-Earn_Proxy")).address; + const DAISAVE_VAULT_ADDRESS = (await deployments.get("opDAI-Save_Proxy")).address; this.registry = await ethers.getContractAt(Registry__factory.abi, REGISTRY_PROXY_ADDRESS); const operatorAddress = await this.registry.getOperator(); const financeOperatorAddress = await this.registry.getFinanceOperator(); @@ -146,12 +117,47 @@ describe(`::${fork}-Vault-rev4`, function () { this.signers.governance = await ethers.getSigner(governanceAddress); this.signers.strategyOperator = await ethers.getSigner(strategyOperatorAddress); this.signers.riskOperator = await ethers.getSigner(await this.registry.riskOperator()); - this.riskManager = await ethers.getContractAt(RiskManager__factory.abi, RISKMANAGER_ADDRESS); - this.strategyProvider = ( - await ethers.getContractAt(StrategyProvider__factory.abi, STRATEGYPROVIDER_ADDRESS) + this.strategyRegistry = ( + await ethers.getContractAt(StrategyRegistry__factory.abi, (await deployments.get("StrategyRegistry")).address) ); this.opUSDCearn = await ethers.getContractAt(Vault__factory.abi, OPUSDCEARN_VAULT_ADDRESS); this.opDAIsave = await ethers.getContractAt(Vault__factory.abi, DAISAVE_VAULT_ADDRESS); + this.vaultHelper = ( + await ethers.getContractAt(VaultHelper__factory.abi, (await deployments.get("VaultHelper")).address) + ); + this.swapHelper = await ethers.getContractAt( + SwapHelper__factory.abi, + ( + await deployments.get("SwapHelper") + ).address, + ); + this.aaveV1Helper = await ethers.getContractAt( + AaveV1Helper__factory.abi, + ( + await deployments.get("AaveV1Helper") + ).address, + ); + this.compoundHelper = await ethers.getContractAt( + CompoundHelper__factory.abi, + ( + await deployments.get("CompoundHelper") + ).address, + ); + this.curveHelper = await ethers.getContractAt( + CurveHelper__factory.abi, + ( + await deployments.get("CurveHelper") + ).address, + ); + + this.strategyManager = new StrategyManager( + (await deployments.get("OptyFiOracle")).address, + this.vaultHelper, + this.swapHelper, + this.compoundHelper, + this.aaveV1Helper, + this.curveHelper, + ); this.usdc = ( await ethers.getContractAt(ERC20Permit__factory.abi, MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address) @@ -164,9 +170,154 @@ describe(`::${fork}-Vault-rev4`, function () { await setTokenBalanceInStorage(this.dai, this.signers.admin.address, "20000"); this.testVault = await deployContract(this.signers.deployer, this.testVaultArtifact, []); + + // add cUSDC strategy + const addcUSDCStrategyPlanTx = await this.strategyRegistry + .connect(this.signers.strategyOperator) + .addStrategyPlan(this.opUSDCearn.address, cUSDCStrategyHash, { + oraValueUTPlan: { + ...this.strategyManager.getOraValueUTPlan(this.usdc.address, cUSDCStrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[cUSDCStrategyHash].oraValueUTIndex, + }, + oraValueLPPlan: { + ...this.strategyManager.getOraSomeValueLPPlan(this.usdc.address, cUSDCStrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[cUSDCStrategyHash].oraValueLPIndex, + }, + lastStepBalanceLPPlan: { + ...this.strategyManager.getLastStrategyStepBalancePlan( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ), + outputIndex: strategyHashReadIndexes[cUSDCStrategyHash].lastStepLPBalanceIndex, + }, + depositSomeToStrategyPlan: this.strategyManager.getDepositPlan( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ), + withdrawSomeFromStrategyPlan: this.strategyManager.getWithdrawPlan( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ), + claimRewardsPlan: this.strategyManager.getClaimRewardsPlan( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ), + harvestRewardsPlan: this.strategyManager.getHarvestRewardsPlan( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ), + }); + await addcUSDCStrategyPlanTx.wait(1); + + const addcUSDCStrategyTx = await this.opUSDCearn + .connect(this.signers.strategyOperator) + .addStrategy(cUSDCStrategyHash); + await addcUSDCStrategyTx.wait(1); + + // add aUSDC v1 strategy + + const addaUSDCV1StrategyPlanTx = await this.strategyRegistry + .connect(this.signers.strategyOperator) + .addStrategyPlan(this.opUSDCearn.address, aUSDCV1StrategyHash, { + oraValueUTPlan: { + ...this.strategyManager.getOraValueUTPlan(this.usdc.address, aUSDCV1StrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[aUSDCV1StrategyHash].oraValueUTIndex, + }, + oraValueLPPlan: { + ...this.strategyManager.getOraSomeValueLPPlan(this.usdc.address, aUSDCV1StrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[aUSDCV1StrategyHash].oraValueLPIndex, + }, + lastStepBalanceLPPlan: { + ...this.strategyManager.getLastStrategyStepBalancePlan( + this.usdc.address, + aUSDCV1StrategySteps, + this.opUSDCearn, + ), + outputIndex: strategyHashReadIndexes[aUSDCV1StrategyHash].lastStepLPBalanceIndex, + }, + depositSomeToStrategyPlan: this.strategyManager.getDepositPlan( + this.usdc.address, + aUSDCV1StrategySteps, + this.opUSDCearn, + ), + withdrawSomeFromStrategyPlan: this.strategyManager.getWithdrawPlan( + this.usdc.address, + aUSDCV1StrategySteps, + this.opUSDCearn, + ), + claimRewardsPlan: this.strategyManager.getClaimRewardsPlan( + this.usdc.address, + aUSDCV1StrategySteps, + this.opUSDCearn, + ), + harvestRewardsPlan: this.strategyManager.getHarvestRewardsPlan( + this.usdc.address, + aUSDCV1StrategySteps, + this.opUSDCearn, + ), + }); + await addaUSDCV1StrategyPlanTx.wait(1); + + const addcUSDCV1StrategyTx = await this.opUSDCearn + .connect(this.signers.strategyOperator) + .addStrategy(aUSDCV1StrategyHash); + await addcUSDCV1StrategyTx.wait(1); + + // add aUSDC v2 strategy + + const addaUSDCV2StrategyPlanTx = await this.strategyRegistry + .connect(this.signers.strategyOperator) + .addStrategyPlan(this.opUSDCearn.address, aUSDCV2StrategyHash, { + oraValueUTPlan: { + ...this.strategyManager.getOraValueUTPlan(this.usdc.address, aUSDCV2StrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[aUSDCV2StrategyHash].oraValueUTIndex, + }, + oraValueLPPlan: { + ...this.strategyManager.getOraSomeValueLPPlan(this.usdc.address, aUSDCV2StrategySteps, this.opUSDCearn), + outputIndex: strategyHashReadIndexes[aUSDCV2StrategyHash].oraValueLPIndex, + }, + lastStepBalanceLPPlan: { + ...this.strategyManager.getLastStrategyStepBalancePlan( + this.usdc.address, + aUSDCV2StrategySteps, + this.opUSDCearn, + ), + outputIndex: strategyHashReadIndexes[aUSDCV2StrategyHash].lastStepLPBalanceIndex, + }, + depositSomeToStrategyPlan: this.strategyManager.getDepositPlan( + this.usdc.address, + aUSDCV2StrategySteps, + this.opUSDCearn, + ), + withdrawSomeFromStrategyPlan: this.strategyManager.getWithdrawPlan( + this.usdc.address, + aUSDCV2StrategySteps, + this.opUSDCearn, + ), + claimRewardsPlan: this.strategyManager.getClaimRewardsPlan( + this.usdc.address, + aUSDCV2StrategySteps, + this.opUSDCearn, + ), + harvestRewardsPlan: this.strategyManager.getHarvestRewardsPlan( + this.usdc.address, + aUSDCV2StrategySteps, + this.opUSDCearn, + ), + }); + await addaUSDCV2StrategyPlanTx.wait(1); + const addaUSDCV2StrategyTx = await this.opUSDCearn + .connect(this.signers.strategyOperator) + .addStrategy(aUSDCV2StrategyHash); + await addaUSDCV2StrategyTx.wait(1); }); - describe(`${fork}-#constructor(address,string,string,string,string)`, function () { + describe.only(`${fork}-#constructor(address,string,string,string,string)`, function () { it(`name,symbol,decimals, domainSeparator as expected`, async function () { const expectedName = "OptyFi USDC Earn Vault"; const expectedSymbol = "opUSDC-Earn"; @@ -188,7 +339,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setValueControlParams(uint256,uint256,uint256)`, function () { + describe.only(`${fork}-#setValueControlParams(uint256,uint256,uint256)`, function () { it("fail setValueControlParams() by non Finance operator", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setValueControlParams("10000000000", "1000000000", "1000000000000"), @@ -230,7 +381,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setVaultConfiguration(uint256)`, function () { + describe.only(`${fork}-#setVaultConfiguration(uint256)`, function () { it("fail setVaultConfiguration() by non governance", async function () { const _vaultConfiguration = BigNumber.from( "3533694129556768659166595001485837031654967793751237934691363855473639425", @@ -319,7 +470,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setUserDepositCapUT(uint256)`, function () { + describe.only(`${fork}-#setUserDepositCapUT(uint256)`, function () { it("fails setUserDepositCapUT() call by non finance operator", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setUserDepositCapUT("4000")).to.be.revertedWith( "caller is not the financeOperator", @@ -334,7 +485,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setMinimumDepositValueUT(uint256)`, function () { + describe.only(`${fork}-#setMinimumDepositValueUT(uint256)`, function () { it("fails setMinimumDepositValueUT() call by non finance operator", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setMinimumDepositValueUT("1000")).to.be.revertedWith( "caller is not the financeOperator", @@ -353,7 +504,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setTotalValueLockedLimitUT(uint256)`, function () { + describe.only(`${fork}-#setTotalValueLockedLimitUT(uint256)`, function () { it("fails setTotalValueLockedLimitUT() call by non finance operator", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setTotalValueLockedLimitUT("100000000"), @@ -374,7 +525,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setWhitelistedAccountsRoot(bytes32)`, function () { + describe.only(`${fork}-#setWhitelistedAccountsRoot(bytes32)`, function () { it("fails setWhitelistedAccountsRoot() call by non governance", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setWhitelistedAccountsRoot(ethers.constants.HashZero), @@ -388,7 +539,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#setEmergencyShutdown(bool)`, function () { + describe.only(`${fork}-#setEmergencyShutdown(bool)`, function () { it("fail setEmergencyShutdown() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setEmergencyShutdown(true)).to.be.revertedWith( "caller is not having governance", @@ -412,12 +563,10 @@ describe(`::${fork}-Vault-rev4`, function () { true, false, ); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); }); }); - describe(`${fork}-#setUnpaused(bool)`, function () { + describe.only(`${fork}-#setUnpaused(bool)`, function () { it("fail setUnpaused() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setUnpaused(false)).to.be.revertedWith( "caller is not having governance", @@ -441,12 +590,10 @@ describe(`::${fork}-Vault-rev4`, function () { true, false, ); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); }); }); - describe(`${fork}-#setRiskProfileCode(uint256)`, function () { + describe.only(`${fork}-#setRiskProfileCode(uint256)`, function () { it("fail setRiskProfileCode() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setRiskProfileCode(1)).to.be.revertedWith( "caller is not having governance", @@ -456,7 +603,7 @@ describe(`::${fork}-Vault-rev4`, function () { it("fail setRiskProfileCode(), non-existant code", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).connect(this.signers.operator).setRiskProfileCode(3), - ).to.be.revertedWith("5"); + ).to.be.revertedWith("3"); }); it("setRiskProfileCode() call by governance", async function () { @@ -477,49 +624,35 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#adminCall(bytes[])`, function () { + describe.only(`${fork}-#adminCall(bytes[])`, function () { it("fail adminCall() call by non governance", async function () { - const _codes = []; - const iface = new ethers.utils.Interface(["function approve(address,uint256)"]); - _codes.push( - ethers.utils.defaultAbiCoder.encode( - ["address", "bytes"], - [this.usdc.address, iface.encodeFunctionData("approve", [this.signers.alice.address, "200"])], - ), + const planner = new weirollPlanner(); + const usdcContract = weirollContract.createContract( + new weirollEthers.Contract(this.usdc.address, this.usdc.interface), ); - await expect(this.opUSDCearn.connect(this.signers.bob).adminCall(_codes)).to.be.revertedWith( + planner.add(usdcContract["approve(address,uint256)"](this.signers.alice.address, "200")); + const { commands, state } = planner.plan(); + await expect(this.opUSDCearn.connect(this.signers.bob).adminCall(commands, state)).to.be.revertedWith( "caller is not having governance", ); }); }); - describe(`${fork}-#getNextBestInvestStrategy()`, function () { - it("getNextBestInvestStrategy()", async function () { - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); - await this.strategyProvider - .connect(this.signers.strategyOperator) - .setBestStrategy("1", MULTI_CHAIN_VAULT_TOKENS[fork].USDC.hash, testStrategy[fork][strategyKeys[0]].steps); - expect(await this.opUSDCearn.getNextBestInvestStrategy()).to.deep.eq([ - Object.values(testStrategy[fork][strategyKeys[0]].steps[0]), - ]); - }); - }); - - describe(`${fork}-#getLastStrategyStepBalanceLP(DataTypes.StrategyStep[])`, function () { + describe.only(`${fork}-#getLastStrategyStepBalanceLP(DataTypes.StrategyStep[])`, function () { it("getLastStrategyStepBalanceLP() return 0", async function () { - expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(testStrategy[fork][strategyKeys[0]].steps)).to.eq("0"); + expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(cUSDCStrategyHash)).to.eq("0"); + expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(aUSDCV1StrategyHash)).to.eq("0"); + expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(aUSDCV2StrategyHash)).to.eq("0"); }); }); - describe(`${fork}-#rebalance()`, function () { - it("fail rebalance() call, vault is paused", async function () { + describe.only(`${fork}-#vaultDepositSomeToStrategy()`, function () { + it("fail vaultDepositSomeToStrategy() call, vault is paused", async function () { // (249) unpause = false = 0 await expect(this.opUSDCearn.connect(this.signers.governance).setUnpaused(false)) .to.emit(this.opUSDCearn, "LogUnpause") .withArgs(false, this.signers.governance.address); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); - await expect(this.opUSDCearn.rebalance()).to.be.revertedWith("14"); + await expect(this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, 1)).to.be.revertedWith("8"); assertVaultConfiguration( await this.opUSDCearn.vaultConfiguration(), BigNumber.from("1"), @@ -535,75 +668,49 @@ describe(`::${fork}-Vault-rev4`, function () { ); }); - it("rebalance(), deposit asset into strategy", async function () { + it("vaultDepositSomeToStrategy(), deposit asset into strategy", async function () { await this.opUSDCearn.connect(this.signers.governance).setEmergencyShutdown(false); await this.opUSDCearn.connect(this.signers.governance).setUnpaused(true); const _totalSupply = BigNumber.from("1000").mul(to_10powNumber_BN("6")); - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; - const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); - const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); - await this.opUSDCearn.rebalance(); + await this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, 1); const _expectedVaultUsdcBalance = BigNumber.from("0"); expect(await this.usdc.balanceOf(this.opUSDCearn.address)).to.eq(_expectedVaultUsdcBalance); - const canStake: boolean = await _adapterInstance.canStake(_pool); - let expectedlpTokenBalance = BigNumber.from("0"); - if (canStake) { - expectedlpTokenBalance = await _adapterInstance.getLiquidityPoolTokenBalanceStake( - this.opUSDCearn.address, - _pool, - ); - } - expectedlpTokenBalance = await _adapterInstance["getLiquidityPoolTokenBalance(address,address,address)"]( - this.opUSDCearn.address, - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, - _pool, + const expectedlpTokenBalance = await this.strategyManager.getValueInOutputToken( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + _expectedVaultUsdcBalance, + ethers.provider, ); - const _lpTokenAddress = testStrategy[fork][strategyKeys[0]].steps[0].outputToken; + const _lpTokenAddress = cUSDCStrategySteps[0].outputToken; const _lpTokenInstance: ERC20 = await ethers.getContractAt(ERC20__factory.abi, _lpTokenAddress); const _actuallpTokenBalance = await _lpTokenInstance.balanceOf(this.opUSDCearn.address); expect(_actuallpTokenBalance).to.eq(expectedlpTokenBalance); - let _allAmountInToken = BigNumber.from("0"); - if (canStake) { - _allAmountInToken = await _adapterInstance.getAllAmountInTokenStake( - this.opUSDCearn.address, - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, - _pool, - ); - } else { - _allAmountInToken = await _adapterInstance["getAllAmountInToken(address,address,address)"]( - this.opUSDCearn.address, - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, - _pool, - ); - } + const _allAmountInToken = await this.strategyManager.getValueInInputToken( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + _actuallpTokenBalance, + ethers.provider, + ); const _expectedPricePerFullShare = _allAmountInToken.mul(to_10powNumber_BN("18")).div(_totalSupply); expect(await this.opUSDCearn.getPricePerFullShare()).to.eq(_expectedPricePerFullShare); - - expect(await this.opUSDCearn.investStrategyHash()).to.eq(testStrategy[fork][strategyKeys[0]].hash); - }); - }); - - describe(`${fork}-#getInvestStrategySteps(DataTypes.StrategyStep[])`, function () { - it("getInvestStrategySteps(), return the strategy steps correctly", async function () { - expect(await this.opUSDCearn.getInvestStrategySteps()).to.deep.eq([ - Object.values(testStrategy[fork][strategyKeys[0]].steps[0]), - ]); }); }); - describe(`${fork}-#balanceUT()`, function () { + describe.only(`${fork}-#balanceUT()`, function () { it("balanceUT() return 0", async function () { expect(await this.opUSDCearn.balanceUT()).to.eq("0"); }); }); - describe(`${fork}-#getPricePerFullShare()`, function () { + describe.only(`${fork}-#getPricePerFullShare()`, function () { it("getPricePerFullShare() return 0", async function () { expect(await this.opUSDCearn.getPricePerFullShare()).to.eq("0"); }); }); - describe(`${fork}-#userDepositPermitted(address,bool,uint256,uint256,bytes32[],bytes32[])`, function () { + describe.only(`${fork}-#userDepositPermitted(address,bool,uint256,uint256,bytes32[],bytes32[])`, function () { it("userDepositPermitted() return false,EOA_NOT_WHITELISTED", async function () { const _proof = getAccountsMerkleProof( [this.signers.alice.address, this.signers.bob.address], @@ -628,7 +735,7 @@ describe(`::${fork}-Vault-rev4`, function () { ); expect( await this.opUSDCearn.userDepositPermitted(this.signers.eve.address, true, "1", "0", _proof), - ).to.have.members([false, "8"]); + ).to.have.members([false, "4"]); }); it("userDepositPermitted() return false,MINIMUM_USER_DEPOSIT_VALUE_UT", async function () { @@ -640,7 +747,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "100", "0", _proof), - ).to.have.members([false, "10"]); + ).to.have.members([false, "4"]); }); it("userDepositPermitted() return false,TOTAL_VALUE_LOCKED_LIMIT_UT", async function () { @@ -652,7 +759,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "100000000000", "0", _proof), - ).to.have.members([false, "11"]); + ).to.have.members([false, "5"]); }); it("userDepositPermitted() return false,USER_DEPOSIT_CAP_UT", async function () { @@ -664,7 +771,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "4000000000", "0", _proof), - ).to.have.members([false, "12"]); + ).to.have.members([false, "6"]); }); it('call userDepositPermitted() from EOA return true,""', async function () { @@ -696,9 +803,9 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#vaultDepositPermitted()`, function () { + describe.only(`${fork}-#vaultDepositPermitted()`, function () { it("vaultDepositPermitted() return false,VAULT_PAUSED", async function () { - expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "14"]); + expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "8"]); }); it("vaultDepositPermitted() return false,VAULT_EMERGENCY_SHUTDOWN", async function () { @@ -718,7 +825,7 @@ describe(`::${fork}-Vault-rev4`, function () { true, true, ); - expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "13"]); + expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "7"]); }); it('vaultDepositPermitted() return true,""', async function () { @@ -745,7 +852,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#userDepositVault(address,uint256,uint256,bytes,bytes32[])`, function () { + describe.only(`${fork}-#userDepositVault(address,uint256,uint256,bytes,bytes32[])`, function () { it("userDepositVault() using permit", async function () { const _proofs = getAccountsMerkleProof( [this.signers.alice.address, this.signers.bob.address, this.testVault.address], @@ -898,7 +1005,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.alice) .userDepositVault(this.signers.alice.address, usdcDepositAmount, [], []), - ).to.be.revertedWith("14"); + ).to.be.revertedWith("8"); }); it("first userDepositVault(), mint same shares as deposit", async function () { @@ -981,7 +1088,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proofs), - ).to.revertedWith("8"); + ).to.revertedWith("4"); }); it("fail userDepositVault() good user alice calls on bob's proof,EOA_NOT_WHITELISTED", async function () { @@ -1012,7 +1119,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.alice) .userDepositVault(this.signers.alice.address, _depositAmountUSDC, [], _bobProofs), - ).to.revertedWith("8"); + ).to.revertedWith("4"); }); it("userDepositVault, deposit fees", async function () { @@ -1126,11 +1233,11 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proof), - ).to.revertedWith("13"); + ).to.revertedWith("7"); }); }); - describe(`${fork}-#permit(address,address,uint256,uint256,uint8,bytes32,bytes32`, function () { + describe.only(`${fork}-#permit(address,address,uint256,uint256,uint8,bytes32,bytes32`, function () { it("success, gasless approval and vault token transferfrom", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); expect(vaultTokenBalanceAlice).to.be.gt("0"); @@ -1202,7 +1309,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("25"); + ).to.be.revertedWith("16"); }); it("fail, expired timestamp", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1228,7 +1335,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("26"); + ).to.be.revertedWith("17"); }); it("fail, invalid signature(wrong signer)", async function () { @@ -1255,7 +1362,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("27"); + ).to.be.revertedWith("18"); }); it("fail, invalid signature(reuse nonce)", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1281,7 +1388,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("27"); + ).to.be.revertedWith("18"); }); }); @@ -1495,14 +1602,12 @@ describe(`::${fork}-Vault-rev4`, function () { describe(`${fork}-#computeInvestStrategyHash(DataTypes.StrategyStep[])`, function () { it("computeInvestStrategyHash()", async function () { - expect(await this.opUSDCearn.computeInvestStrategyHash(testStrategy[fork][strategyKeys[0]].steps)).to.eq( - testStrategy[fork][strategyKeys[0]].hash, - ); + expect(await this.opUSDCearn.computeInvestStrategyHash(cUSDCStrategySteps)).to.eq(cUSDCStrategyHash); }); }); describe(`${fork}-#claimRewardToken(address)`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; + const _pool = cUSDCStrategySteps[0].pool; it("fail claimRewardToken() call by non strategyOperator", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).claimRewardToken(_pool)).to.be.revertedWith( @@ -1523,7 +1628,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); describe(`${fork}-#giveAllowances()`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; + const _pool = cUSDCStrategySteps[0].pool; it("fail giveAllowances by non risk operator", async function () { await expect( @@ -1548,7 +1653,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); describe(`${fork}-#removesAllowance()`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; + const _pool = cUSDCStrategySteps[0].pool; it("fail removeAllowances by non risk operator", async function () { await expect( @@ -1574,7 +1679,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); describe(`${fork}-#harvest(address)`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; + const _pool = cUSDCStrategySteps[0].pool; it("fail harvest() call by non strategyOperator", async function () { const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); @@ -1627,7 +1732,7 @@ describe(`::${fork}-Vault-rev4`, function () { ); const _redeemVT = (await this.opUSDCearn.balanceOf(this.signers.alice.address)).div("2"); const _userbalanceBefore = await this.usdc.balanceOf(this.signers.alice.address); - const _pool = testStrategy[fork][strategyKeys[0]].steps[0].pool; + const _pool = cUSDCStrategySteps[0].pool; const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); const canStake: boolean = await _adapterInstance.canStake(_pool); From 6cddfcb60cef0c8a5a6e30bf20d1f2242f20391f Mon Sep 17 00:00:00 2001 From: dhruvinparikh Date: Wed, 1 Feb 2023 13:38:31 -0500 Subject: [PATCH 44/44] feat(2_vault_spec): refactor --- deploy_mainnet/027_set_price_oracle_feed.ts | 7 + helpers/strategy-manager.ts | 17 ++ test/test-opty/2_vault.spec.ts | 263 +++++++++----------- 3 files changed, 148 insertions(+), 139 deletions(-) diff --git a/deploy_mainnet/027_set_price_oracle_feed.ts b/deploy_mainnet/027_set_price_oracle_feed.ts index 8524953b..2af3bd93 100644 --- a/deploy_mainnet/027_set_price_oracle_feed.ts +++ b/deploy_mainnet/027_set_price_oracle_feed.ts @@ -16,6 +16,7 @@ const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvir const WETH_WBTC_FEED = "0xAc559F25B1619171CbC396a50854A3240b6A4e99"; const COMP_ETH_FEED = "0x1B39Ee86Ec5979ba5C322b826B3ECb8C79991699"; const COMP_USD_FEED = "0xdbd020CAeF83eFd542f4De03e3cF0C28A4428bd5"; + const USDC_USD_FEED = "0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6"; const owner = await optyfiOracleInstance.owner(); const feedToTokens = [ @@ -44,6 +45,11 @@ const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvir tokenB: ethereumTokens.PLAIN_TOKENS.USD, priceFeed: COMP_USD_FEED, }, + { + tokenA: ethereumTokens.PLAIN_TOKENS.USDC, + tokenB: ethereumTokens.PLAIN_TOKENS.USD, + priceFeed: USDC_USD_FEED, + }, ]; const pendingFeedToTokens = []; for (const feedToToken of feedToTokens) { @@ -78,6 +84,7 @@ const func: DeployFunction = async ({ deployments, ethers }: HardhatRuntimeEnvir { tokenA: ethereumTokens.PLAIN_TOKENS.USDC, tokenB: ethereumTokens.WRAPPED_TOKENS.WETH, timeAllowance: "86400" }, { tokenA: ethereumTokens.BTC_TOKENS.WBTC, tokenB: ethereumTokens.WRAPPED_TOKENS.WETH, timeAllowance: "48000" }, { tokenA: ethereumTokens.WRAPPED_TOKENS.WETH, tokenB: ethereumTokens.BTC_TOKENS.WBTC, timeAllowance: "48000" }, + { tokenA: ethereumTokens.REWARD_TOKENS.COMP, tokenB: ethereumTokens.PLAIN_TOKENS.USDC, timeAllowance: "48000" }, ]; const pendingChainlinkTimeallowances = []; diff --git a/helpers/strategy-manager.ts b/helpers/strategy-manager.ts index 8a732620..bfc07349 100644 --- a/helpers/strategy-manager.ts +++ b/helpers/strategy-manager.ts @@ -399,4 +399,21 @@ export class StrategyManager { } return amountLP; } + + async getLastStepBalance( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + provider: JsonRpcProvider, + ): Promise { + const adapterObj = this.liquidityPoolToAdapter[strategySteps[strategySteps.length - 1].pool]; + return await adapterObj.getOutputTokenBalance( + vaultInstance, + strategySteps.length === 1 ? underlyingToken : strategySteps[strategySteps.length - 2].outputToken, + strategySteps[strategySteps.length - 1].pool, + strategySteps[strategySteps.length - 1].outputToken, + strategySteps[strategySteps.length - 1].isSwap, + provider, + ); + } } diff --git a/test/test-opty/2_vault.spec.ts b/test/test-opty/2_vault.spec.ts index 5dfa7717..414465f7 100644 --- a/test/test-opty/2_vault.spec.ts +++ b/test/test-opty/2_vault.spec.ts @@ -5,10 +5,10 @@ import { deployContract, solidity } from "ethereum-waffle"; import { BigNumber, ContractReceipt, Event, Contract } from "ethers"; import BN from "bignumber.js"; import { getAddress, parseUnits } from "ethers/lib/utils"; -import ethereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; import { ethers as weirollEthers } from "@weiroll/weiroll.js/node_modules/ethers"; import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; import { assertVaultConfiguration, getAccountsMerkleProof, @@ -31,7 +31,6 @@ import { TestVault, ERC20Permit, ERC20Permit__factory, - IAdapterFull, StrategyRegistry__factory, StrategyRegistry, SwapHelper__factory, @@ -52,13 +51,26 @@ import { StrategiesByTokenByChain, strategyHashReadIndexes } from "../../helpers chai.use(solidity); const fork = process.env.FORK as eEVMNetwork; -const UniswapV3RouterAddress = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; //mainnet const EIP712_DOMAIN = ethers.utils.id( "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", ); const EIP712_REVISION = ethers.utils.id("1"); +const USER_WITHDRAW_INSUFFICIENT_VT = "1"; +const RISK_PROFILE_EXISTS = "3"; +const EOA_NOT_WHITELISTED = "4"; +const TOTAL_VALUE_LOCKED_LIMIT_UT = "5"; +const USER_DEPOSIT_CAP_UT = "6"; +const VAULT_EMERGENCY_SHUTDOWN = "7"; +const VAULT_PAUSED = "8"; +const UNDERLYING_TOKENS_HASH_EXISTS = "10"; +const TRANSFER_TO_THIS_CONTRACT = "11"; +const ZERO_ADDRESS_NOT_VALID = "16"; +const INVALID_EXPIRATION = "17"; +const INVALID_SIGNATURE = "18"; +const LENGTH_MISMATCH = "19"; + const usdcTokenHash = generateTokenHashV2([EthereumTokens.PLAIN_TOKENS.USDC], NETWORKS_CHAIN_ID_HEX[fork]); const cUSDCStrategySteps = StrategiesByTokenByChain[fork]["Save"]["USDC"]["usdc-DEPOSIT-Compound-cUSDC"].strategy.map( item => ({ @@ -93,7 +105,7 @@ const aUSDCV2StrategyHash = generateStrategyHashV2( StrategiesByTokenByChain[fork]["Earn"]["USDC"]["usdc-DEPOSIT-AaveV2-aUSDC"].strategy, usdcTokenHash, ); -describe(`::${fork}-Vault-rev4`, function () { +describe(`::${fork}-Vault-rev7`, function () { before(async function () { await deployments.fixture(); this.testVaultArtifact = await artifacts.readArtifact("TestVault"); @@ -317,7 +329,7 @@ describe(`::${fork}-Vault-rev4`, function () { await addaUSDCV2StrategyTx.wait(1); }); - describe.only(`${fork}-#constructor(address,string,string,string,string)`, function () { + describe(`${fork}-#constructor(address,string,string,string,string)`, function () { it(`name,symbol,decimals, domainSeparator as expected`, async function () { const expectedName = "OptyFi USDC Earn Vault"; const expectedSymbol = "opUSDC-Earn"; @@ -337,9 +349,13 @@ describe(`::${fork}-Vault-rev4`, function () { it(`registry as expected`, async function () { expect(await this.opUSDCearn.registryContract()).to.eq((await deployments.get("RegistryProxy")).address); }); + + it(`opTOKEN_REVISION as expected`, async function () { + expect(await this.opUSDCearn.opTOKEN_REVISION()).to.eq("7"); + }); }); - describe.only(`${fork}-#setValueControlParams(uint256,uint256,uint256)`, function () { + describe(`${fork}-#setValueControlParams(uint256,uint256,uint256)`, function () { it("fail setValueControlParams() by non Finance operator", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setValueControlParams("10000000000", "1000000000", "1000000000000"), @@ -381,7 +397,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setVaultConfiguration(uint256)`, function () { + describe(`${fork}-#setVaultConfiguration(uint256)`, function () { it("fail setVaultConfiguration() by non governance", async function () { const _vaultConfiguration = BigNumber.from( "3533694129556768659166595001485837031654967793751237934691363855473639425", @@ -470,7 +486,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setUserDepositCapUT(uint256)`, function () { + describe(`${fork}-#setUserDepositCapUT(uint256)`, function () { it("fails setUserDepositCapUT() call by non finance operator", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setUserDepositCapUT("4000")).to.be.revertedWith( "caller is not the financeOperator", @@ -485,7 +501,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setMinimumDepositValueUT(uint256)`, function () { + describe(`${fork}-#setMinimumDepositValueUT(uint256)`, function () { it("fails setMinimumDepositValueUT() call by non finance operator", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setMinimumDepositValueUT("1000")).to.be.revertedWith( "caller is not the financeOperator", @@ -504,7 +520,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setTotalValueLockedLimitUT(uint256)`, function () { + describe(`${fork}-#setTotalValueLockedLimitUT(uint256)`, function () { it("fails setTotalValueLockedLimitUT() call by non finance operator", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setTotalValueLockedLimitUT("100000000"), @@ -525,7 +541,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setWhitelistedAccountsRoot(bytes32)`, function () { + describe(`${fork}-#setWhitelistedAccountsRoot(bytes32)`, function () { it("fails setWhitelistedAccountsRoot() call by non governance", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).setWhitelistedAccountsRoot(ethers.constants.HashZero), @@ -539,7 +555,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setEmergencyShutdown(bool)`, function () { + describe(`${fork}-#setEmergencyShutdown(bool)`, function () { it("fail setEmergencyShutdown() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setEmergencyShutdown(true)).to.be.revertedWith( "caller is not having governance", @@ -566,7 +582,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setUnpaused(bool)`, function () { + describe(`${fork}-#setUnpaused(bool)`, function () { it("fail setUnpaused() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setUnpaused(false)).to.be.revertedWith( "caller is not having governance", @@ -593,7 +609,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#setRiskProfileCode(uint256)`, function () { + describe(`${fork}-#setRiskProfileCode(uint256)`, function () { it("fail setRiskProfileCode() call by non governance", async function () { await expect(this.opUSDCearn.connect(this.signers.bob).setRiskProfileCode(1)).to.be.revertedWith( "caller is not having governance", @@ -603,7 +619,7 @@ describe(`::${fork}-Vault-rev4`, function () { it("fail setRiskProfileCode(), non-existant code", async function () { await expect( this.opUSDCearn.connect(this.signers.bob).connect(this.signers.operator).setRiskProfileCode(3), - ).to.be.revertedWith("3"); + ).to.be.revertedWith(RISK_PROFILE_EXISTS); }); it("setRiskProfileCode() call by governance", async function () { @@ -624,7 +640,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#adminCall(bytes[])`, function () { + describe(`${fork}-#adminCall(bytes32[],bytes[])`, function () { it("fail adminCall() call by non governance", async function () { const planner = new weirollPlanner(); const usdcContract = weirollContract.createContract( @@ -638,7 +654,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#getLastStrategyStepBalanceLP(DataTypes.StrategyStep[])`, function () { + describe(`${fork}-#getLastStrategyStepBalanceLP(DataTypes.StrategyStep[])`, function () { it("getLastStrategyStepBalanceLP() return 0", async function () { expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(cUSDCStrategyHash)).to.eq("0"); expect(await this.opUSDCearn.getLastStrategyStepBalanceLP(aUSDCV1StrategyHash)).to.eq("0"); @@ -646,13 +662,13 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#vaultDepositSomeToStrategy()`, function () { - it("fail vaultDepositSomeToStrategy() call, vault is paused", async function () { + describe(`${fork}-#vaultDepositSomeToStrategy(bytes32,uint256)`, function () { + it("fail vaultDepositSomeToStrategy(bytes32,uint256) call, vault is paused", async function () { // (249) unpause = false = 0 await expect(this.opUSDCearn.connect(this.signers.governance).setUnpaused(false)) .to.emit(this.opUSDCearn, "LogUnpause") .withArgs(false, this.signers.governance.address); - await expect(this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, 1)).to.be.revertedWith("8"); + await expect(this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, 1)).to.be.revertedWith(VAULT_PAUSED); assertVaultConfiguration( await this.opUSDCearn.vaultConfiguration(), BigNumber.from("1"), @@ -668,7 +684,7 @@ describe(`::${fork}-Vault-rev4`, function () { ); }); - it("vaultDepositSomeToStrategy(), deposit asset into strategy", async function () { + it("vaultDepositSomeToStrategy(bytes32,uint256), deposit asset into strategy", async function () { await this.opUSDCearn.connect(this.signers.governance).setEmergencyShutdown(false); await this.opUSDCearn.connect(this.signers.governance).setUnpaused(true); const _totalSupply = BigNumber.from("1000").mul(to_10powNumber_BN("6")); @@ -698,19 +714,19 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#balanceUT()`, function () { + describe(`${fork}-#balanceUT()`, function () { it("balanceUT() return 0", async function () { expect(await this.opUSDCearn.balanceUT()).to.eq("0"); }); }); - describe.only(`${fork}-#getPricePerFullShare()`, function () { + describe(`${fork}-#getPricePerFullShare()`, function () { it("getPricePerFullShare() return 0", async function () { expect(await this.opUSDCearn.getPricePerFullShare()).to.eq("0"); }); }); - describe.only(`${fork}-#userDepositPermitted(address,bool,uint256,uint256,bytes32[],bytes32[])`, function () { + describe(`${fork}-#userDepositPermitted(address,bool,uint256,uint256,bytes32[])`, function () { it("userDepositPermitted() return false,EOA_NOT_WHITELISTED", async function () { const _proof = getAccountsMerkleProof( [this.signers.alice.address, this.signers.bob.address], @@ -735,7 +751,7 @@ describe(`::${fork}-Vault-rev4`, function () { ); expect( await this.opUSDCearn.userDepositPermitted(this.signers.eve.address, true, "1", "0", _proof), - ).to.have.members([false, "4"]); + ).to.have.members([false, EOA_NOT_WHITELISTED]); }); it("userDepositPermitted() return false,MINIMUM_USER_DEPOSIT_VALUE_UT", async function () { @@ -747,7 +763,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "100", "0", _proof), - ).to.have.members([false, "4"]); + ).to.have.members([false, EOA_NOT_WHITELISTED]); }); it("userDepositPermitted() return false,TOTAL_VALUE_LOCKED_LIMIT_UT", async function () { @@ -759,7 +775,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "100000000000", "0", _proof), - ).to.have.members([false, "5"]); + ).to.have.members([false, TOTAL_VALUE_LOCKED_LIMIT_UT]); }); it("userDepositPermitted() return false,USER_DEPOSIT_CAP_UT", async function () { @@ -771,7 +787,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userDepositPermitted(this.signers.alice.address, true, "4000000000", "0", _proof), - ).to.have.members([false, "6"]); + ).to.have.members([false, USER_DEPOSIT_CAP_UT]); }); it('call userDepositPermitted() from EOA return true,""', async function () { @@ -803,9 +819,9 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#vaultDepositPermitted()`, function () { + describe(`${fork}-#vaultDepositPermitted()`, function () { it("vaultDepositPermitted() return false,VAULT_PAUSED", async function () { - expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "8"]); + expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, VAULT_PAUSED]); }); it("vaultDepositPermitted() return false,VAULT_EMERGENCY_SHUTDOWN", async function () { @@ -825,7 +841,7 @@ describe(`::${fork}-Vault-rev4`, function () { true, true, ); - expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, "7"]); + expect(await this.opUSDCearn.vaultDepositPermitted()).to.have.members([false, VAULT_EMERGENCY_SHUTDOWN]); }); it('vaultDepositPermitted() return true,""', async function () { @@ -852,7 +868,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe.only(`${fork}-#userDepositVault(address,uint256,uint256,bytes,bytes32[])`, function () { + describe(`${fork}-#userDepositVault(address,uint256,bytes,bytes32[])`, function () { it("userDepositVault() using permit", async function () { const _proofs = getAccountsMerkleProof( [this.signers.alice.address, this.signers.bob.address, this.testVault.address], @@ -1005,7 +1021,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.alice) .userDepositVault(this.signers.alice.address, usdcDepositAmount, [], []), - ).to.be.revertedWith("8"); + ).to.be.revertedWith(VAULT_PAUSED); }); it("first userDepositVault(), mint same shares as deposit", async function () { @@ -1088,7 +1104,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proofs), - ).to.revertedWith("4"); + ).to.revertedWith(EOA_NOT_WHITELISTED); }); it("fail userDepositVault() good user alice calls on bob's proof,EOA_NOT_WHITELISTED", async function () { @@ -1119,7 +1135,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.alice) .userDepositVault(this.signers.alice.address, _depositAmountUSDC, [], _bobProofs), - ).to.revertedWith("4"); + ).to.revertedWith(EOA_NOT_WHITELISTED); }); it("userDepositVault, deposit fees", async function () { @@ -1233,11 +1249,11 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proof), - ).to.revertedWith("7"); + ).to.revertedWith(VAULT_EMERGENCY_SHUTDOWN); }); }); - describe.only(`${fork}-#permit(address,address,uint256,uint256,uint8,bytes32,bytes32`, function () { + describe(`${fork}-#permit(address,address,uint256,uint256,uint8,bytes32,bytes32`, function () { it("success, gasless approval and vault token transferfrom", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); expect(vaultTokenBalanceAlice).to.be.gt("0"); @@ -1309,7 +1325,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("16"); + ).to.be.revertedWith(ZERO_ADDRESS_NOT_VALID); }); it("fail, expired timestamp", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1335,7 +1351,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("17"); + ).to.be.revertedWith(INVALID_EXPIRATION); }); it("fail, invalid signature(wrong signer)", async function () { @@ -1362,7 +1378,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("18"); + ).to.be.revertedWith(INVALID_SIGNATURE); }); it("fail, invalid signature(reuse nonce)", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1388,7 +1404,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("18"); + ).to.be.revertedWith(INVALID_SIGNATURE); }); }); @@ -1398,8 +1414,6 @@ describe(`::${fork}-Vault-rev4`, function () { [this.signers.alice.address, this.signers.bob.address, this.testVault.address], this.signers.alice.address, ); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); await this.opUSDCearn .connect(this.signers.governance) .setVaultConfiguration("906570639739453258250749540332912351914733262152258629340941055050750689281"); @@ -1421,7 +1435,7 @@ describe(`::${fork}-Vault-rev4`, function () { await this.opUSDCearn .connect(this.signers.alice) .userWithdrawPermitted(this.signers.alice.address, _userBalance.add(1), _accountProof), - ).to.have.members([false, "1"]); + ).to.have.members([false, USER_WITHDRAW_INSUFFICIENT_VT]); }); it('userWithdrawPermitted() return true,""', async function () { @@ -1443,8 +1457,6 @@ describe(`::${fork}-Vault-rev4`, function () { await expect(this.opUSDCearn.connect(this.signers.governance).setUnpaused(false)) .to.emit(this.opUSDCearn, "LogUnpause") .withArgs(false, this.signers.governance.address); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); assertVaultConfiguration( await this.opUSDCearn.vaultConfiguration(), BigNumber.from("1"), @@ -1458,7 +1470,7 @@ describe(`::${fork}-Vault-rev4`, function () { false, false, ); - expect(await this.opUSDCearn.vaultWithdrawPermitted()).to.have.members([false, "14"]); + expect(await this.opUSDCearn.vaultWithdrawPermitted()).to.have.members([false, VAULT_PAUSED]); }); it('vaultWithdrawPermitted() return true,""', async function () { @@ -1482,13 +1494,15 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#vaultDepositAllToStrategy()`, function () { - it("fail vaultDepositAllToStrategy() call, vault is paused", async function () { + describe(`${fork}-#vaultDepositSomeToStrategy(bytes32,uint256)`, function () { + it("fail vaultDepositSomeToStrategy() call, vault is paused", async function () { await this.opUSDCearn.connect(this.signers.governance).setUnpaused(false); - await expect(this.opUSDCearn.vaultDepositAllToStrategy()).to.be.revertedWith("14"); + await expect( + this.opUSDCearn.connect(this.signers.strategyOperator).vaultDepositSomeToStrategy(cUSDCStrategyHash, 1), + ).to.be.revertedWith(VAULT_PAUSED); }); - it("vaultDepositAllToStrategy() by any user", async function () { + it("vaultDepositSomeToStrategy() by any user", async function () { await expect(this.opUSDCearn.connect(this.signers.governance).setUnpaused(true)) .to.emit(this.opUSDCearn, "LogUnpause") .withArgs(true, this.signers.governance.address); @@ -1505,34 +1519,25 @@ describe(`::${fork}-Vault-rev4`, function () { true, false, ); - const _balanceUTBeforeRebalance = await this.usdc.balanceOf(this.opUSDCearn.address); - await this.opUSDCearn.rebalance(); - expect(await this.opUSDCearn.getInvestStrategySteps()).to.deep.eq([ - Object.values(testStrategy[fork][strategyKeys[0]].steps[0]), - ]); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(testStrategy[fork][strategyKeys[0]].hash); - const _balanceUTAfterRebalance = await this.usdc.balanceOf(this.opUSDCearn.address); - expect(_balanceUTBeforeRebalance).to.gt(_balanceUTAfterRebalance); + const _balanceUTBeforeDepositSomeToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); + await this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, _balanceUTBeforeDepositSomeToStrategy); + const _balanceUTAfterDepositSomeToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); + expect(_balanceUTBeforeDepositSomeToStrategy).to.gt(_balanceUTAfterDepositSomeToStrategy); await this.usdc.connect(this.signers.eve).transfer(this.opUSDCearn.address, "1000000000"); - const _balanceUTBeforeDepositToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); - await this.opUSDCearn.vaultDepositAllToStrategy(); - expect(await this.opUSDCearn.getInvestStrategySteps()).to.deep.eq([ - Object.values(testStrategy[fork][strategyKeys[0]].steps[0]), - ]); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(testStrategy[fork][strategyKeys[0]].hash); - const _balanceUTAfterDepositToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); - expect(_balanceUTBeforeDepositToStrategy).to.gt(_balanceUTAfterDepositToStrategy); + const _balanceUTBeforeDepositAllToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); + await this.opUSDCearn.vaultDepositSomeToStrategy(cUSDCStrategyHash, _balanceUTBeforeDepositAllToStrategy); + const _balanceUTAfterDepositAllToStrategy = await this.usdc.balanceOf(this.opUSDCearn.address); + expect(_balanceUTBeforeDepositAllToStrategy).to.gt(_balanceUTAfterDepositAllToStrategy); }); - it("fail vaultDepositAllToStrategy(), VAULT_EMERGENCY_SHUTDOWN", async function () { + it("fail vaultDepositSomeToStrategy(), VAULT_EMERGENCY_SHUTDOWN", async function () { const _balanceUTBefore = await this.usdc.balanceOf(this.opUSDCearn.address); await expect(this.opUSDCearn.connect(this.signers.governance).setEmergencyShutdown(true)) .to.emit(this.opUSDCearn, "LogEmergencyShutdown") .withArgs(true, this.signers.governance.address); const _balanceUTAfter = await this.usdc.balanceOf(this.opUSDCearn.address); expect(_balanceUTAfter).to.gt(_balanceUTBefore); - expect(await this.opUSDCearn.investStrategyHash()).to.eq(ethers.constants.HashZero); - expect((await this.opUSDCearn.getInvestStrategySteps()).length).to.eq(0); + assertVaultConfiguration( await this.opUSDCearn.vaultConfiguration(), BigNumber.from("1"), @@ -1546,7 +1551,9 @@ describe(`::${fork}-Vault-rev4`, function () { true, false, ); - await expect(this.opUSDCearn.vaultDepositAllToStrategy()).to.revertedWith("13"); + await expect( + this.opUSDCearn.connect(this.signers.strategyOperator).vaultDepositSomeToStrategy(cUSDCStrategyHash, 1), + ).to.revertedWith(VAULT_EMERGENCY_SHUTDOWN); }); }); @@ -1564,7 +1571,7 @@ describe(`::${fork}-Vault-rev4`, function () { .setUnderlyingTokensHash( getSoliditySHA3Hash(["address", "uint256"], [MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, "a"]), ), - ).to.be.revertedWith("17"); + ).to.be.revertedWith(UNDERLYING_TOKENS_HASH_EXISTS); }); it("setUnderlyingTokensHash() call by operator", async function () { @@ -1600,28 +1607,19 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#computeInvestStrategyHash(DataTypes.StrategyStep[])`, function () { - it("computeInvestStrategyHash()", async function () { - expect(await this.opUSDCearn.computeInvestStrategyHash(cUSDCStrategySteps)).to.eq(cUSDCStrategyHash); - }); - }); - - describe(`${fork}-#claimRewardToken(address)`, function () { - const _pool = cUSDCStrategySteps[0].pool; - + describe(`${fork}-#claimRewardToken(bytes32)`, function () { it("fail claimRewardToken() call by non strategyOperator", async function () { - await expect(this.opUSDCearn.connect(this.signers.bob).claimRewardToken(_pool)).to.be.revertedWith( + await expect(this.opUSDCearn.connect(this.signers.bob).claimRewardToken(cUSDCStrategyHash)).to.be.revertedWith( "caller is not the strategyOperator", ); }); it("claimRewardToken(), fails nothing to claim", async function () { - const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); - const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); - const _rewardToken = await _adapterInstance.getRewardToken(_pool); - const _rewardTokenInstance: ERC20 = await ethers.getContractAt(ERC20__factory.abi, _rewardToken); + const _rewardTokenInstance: ERC20 = ( + await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.COMP) + ); const _balanceRTBefore = await _rewardTokenInstance.balanceOf(this.opUSDCearn.address); - await this.opUSDCearn.claimRewardToken(_pool); + await this.opUSDCearn.claimRewardToken(cUSDCStrategyHash); const _balanceRTAfter = await _rewardTokenInstance.balanceOf(this.opUSDCearn.address); expect(_balanceRTAfter).to.gt(_balanceRTBefore); }); @@ -1640,9 +1638,16 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.riskOperator) .giveAllowances([await this.opUSDCearn.underlyingToken()], []), - ).to.be.revertedWith("28"); + ).to.be.revertedWith(LENGTH_MISMATCH); }); it("success giveAllowances", async function () { + await expect( + this.opUSDCearn + .connect(this.signers.riskOperator) + .removeAllowances([await this.opUSDCearn.underlyingToken()], [_pool]), + ) + .to.emit(this.usdc, "Approval") + .withArgs(this.opUSDCearn.address, getAddress(_pool), "0"); await expect( this.opUSDCearn .connect(this.signers.riskOperator) @@ -1665,7 +1670,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.riskOperator) .removeAllowances([await this.opUSDCearn.underlyingToken()], []), - ).to.be.revertedWith("28"); + ).to.be.revertedWith(LENGTH_MISMATCH); }); it("success removeAllowances", async function () { await expect( @@ -1678,53 +1683,37 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#harvest(address)`, function () { - const _pool = cUSDCStrategySteps[0].pool; - - it("fail harvest() call by non strategyOperator", async function () { - const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); - const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); - const _rewardToken = await _adapterInstance.getRewardToken(_pool); - await expect( - this.opUSDCearn.connect(this.signers.bob).harvest(_rewardToken, UniswapV3RouterAddress, true, 0, 0, [], "0x"), - ).to.be.revertedWith("caller is not the strategyOperator"); + describe(`${fork}-#harvestRewards(strategyHash)`, function () { + it("fail harvestRewards() call by non strategyOperator", async function () { + await expect(this.opUSDCearn.connect(this.signers.bob).harvestRewards(cUSDCStrategyHash)).to.be.revertedWith( + "caller is not the strategyOperator", + ); }); - it("harvest()", async function () { - const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); - const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); - const _rewardToken = await _adapterInstance.getRewardToken(_pool); + it("harvestRewards()", async function () { const _balanceBeforeUT = await this.opUSDCearn.balanceUT(); - const _rewardTokenInstance = await ethers.getContractAt(ERC20__factory.abi, _rewardToken); + const _rewardTokenInstance = ( + await ethers.getContractAt(ERC20__factory.abi, EthereumTokens.REWARD_TOKENS.COMP) + ); await expect( - this.opUSDCearn.connect(this.signers.governance).giveAllowances([_rewardToken], [UniswapV3RouterAddress]), + this.opUSDCearn + .connect(this.signers.governance) + .giveAllowances([EthereumTokens.REWARD_TOKENS.COMP], [UniswapV2.router02.address]), ) .to.emit(_rewardTokenInstance, "Approval") .withArgs( this.opUSDCearn.address, - getAddress(UniswapV3RouterAddress), + getAddress(UniswapV2.router02.address), BigNumber.from("0xffffffffffffffffffffffff"), ); // in COMP uint96 is used - const uniV3SwapPath = ethers.utils.solidityPack( - ["address", "uint24", "address", "uint24", "address"], - [_rewardToken, 3000, ethereumTokens.WRAPPED_TOKENS.WETH, 500, await this.opUSDCearn.underlyingToken()], - ); - const tx = await this.opUSDCearn.harvest( - _rewardToken, - UniswapV3RouterAddress, - true, - 0, - 9999999999, - [], - uniV3SwapPath, - ); + const tx = await this.opUSDCearn.harvestRewards(cUSDCStrategyHash); await tx.wait(1); const _balanceAfterUT = await this.opUSDCearn.balanceUT(); expect(_balanceAfterUT).gt(_balanceBeforeUT); }); }); - describe(`${fork}-#userWithdrawVault(uint256,bytes32[],bytes32[])`, function () { + describe(`${fork}-#userWithdrawVault(address,uint256,bytes32[])`, function () { it("userWithdrawVault()", async function () { const _proofs = getAccountsMerkleProof( [this.signers.alice.address, this.signers.bob.address, this.testVault.address], @@ -1732,24 +1721,18 @@ describe(`::${fork}-Vault-rev4`, function () { ); const _redeemVT = (await this.opUSDCearn.balanceOf(this.signers.alice.address)).div("2"); const _userbalanceBefore = await this.usdc.balanceOf(this.signers.alice.address); - const _pool = cUSDCStrategySteps[0].pool; - const _adapterAddress = await this.registry.liquidityPoolToAdapter(_pool); - const _adapterInstance = await ethers.getContractAt("IAdapterFull", _adapterAddress); - const canStake: boolean = await _adapterInstance.canStake(_pool); - let _allAmountInToken = BigNumber.from("0"); - if (canStake) { - _allAmountInToken = await _adapterInstance.getAllAmountInTokenStake( - this.opUSDCearn.address, - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, - _pool, - ); - } else { - _allAmountInToken = await _adapterInstance["getAllAmountInToken(address,address,address)"]( - this.opUSDCearn.address, - MULTI_CHAIN_VAULT_TOKENS[fork].USDC.address, - _pool, - ); - } + const _allAmountInToken = await this.strategyManager.getValueInInputToken( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + await this.strategyManager.getLastStepBalance( + this.usdc.address, + cUSDCStrategySteps, + this.opUSDCearn, + ethers.provider, + ), + ethers.provider, + ); const _vaultBalanceUT = await this.usdc.balanceOf(this.opUSDCearn.address); const _totalSupply = await this.opUSDCearn.totalSupply(); const _calculatedReceivableUT = _redeemVT.mul(_allAmountInToken.add(_vaultBalanceUT)).div(_totalSupply); @@ -1784,7 +1767,9 @@ describe(`::${fork}-Vault-rev4`, function () { describe(`${fork}-_beforeTokenTransfer()`, function () { it("fail _beforeTokenTransfer() TRANSFER_TO_THIS_CONTRACT", async function () { const _redeemVT = await this.opUSDCearn.balanceOf(this.signers.alice.address); - await expect(this.opUSDCearn.transfer(this.opUSDCearn.address, _redeemVT)).to.revertedWith("18"); + await expect(this.opUSDCearn.transfer(this.opUSDCearn.address, _redeemVT)).to.revertedWith( + TRANSFER_TO_THIS_CONTRACT, + ); }); });