diff --git a/.eslintignore b/.eslintignore index 375261e4a..514236ae4 100644 --- a/.eslintignore +++ b/.eslintignore @@ -10,8 +10,22 @@ 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/ # files .solcover.js diff --git a/.gitmodules b/.gitmodules index 312acfe82..f522f8a4f 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,9 +37,13 @@ [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 +[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 f57a8ae8e..64d913fda 100644 --- a/.prettierignore +++ b/.prettierignore @@ -12,8 +12,22 @@ **/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/ +contracts/utils/optyfi-oracle # files *.env diff --git a/.solhint.json b/.solhint.json index ad5e6d35c..5d7b650dc 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 9e4b82f92..4f3d7939d 100644 --- a/.solhintignore +++ b/.solhintignore @@ -7,4 +7,16 @@ contracts/utils/ contracts/dependencies/ contracts/mocks/ contracts/protocol/earn-protocol-configuration -contracts/protocol/adapters \ No newline at end of file +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/interfaces/opty/IVault.sol b/contracts/interfaces/opty/IVault.sol index 8772892b2..8ef0194ff 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,16 +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; - - /** - * @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; + function setUnpaused(bool _unpaused) external payable; /** * @notice Deposit underlying tokens to the vault @@ -159,43 +150,40 @@ interface IVault { ) external returns (uint256); /** - * @notice function to deposit whole balance of underlying token to current strategy + * @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 */ - function vaultDepositAllToStrategy() 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 + */ + 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 @@ -219,6 +207,18 @@ interface IVault { bytes32 _s ) 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 Provide the allowances for the spenders to spent vault owned tokens * @dev the length of tokens and spenders should be same @@ -326,38 +326,13 @@ 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 - * @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); - - /** - * @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 - * @return keccak256 hash of the invest strategy and underlying tokens hash - */ - function computeInvestStrategyHash(DataTypes.StrategyStep[] memory _investStrategySteps) - external - view - returns (bytes32); + function getLastStrategyStepBalanceLP(bytes32 _strategyHash) external view returns (uint256); /** * @dev Emitted when emergency shutdown over vault is changed @@ -393,4 +368,16 @@ interface IVault { * @param caller Address of user who has called the respective function to trigger this event */ event LogTotalValueLockedLimitUT(uint256 indexed totalValueLockedLimitUT, address indexed caller); + + /** + * @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/TestVault.sol b/contracts/mocks/contracts/TestVault.sol index 970847e5e..fabf9722c 100644 --- a/contracts/mocks/contracts/TestVault.sol +++ b/contracts/mocks/contracts/TestVault.sol @@ -49,4 +49,12 @@ 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; + } + + function getERC20Balance(address _owner, ERC20 _token) external view returns (uint256) { + return _token.balanceOf(_owner); + } } diff --git a/contracts/protocol/adapters/ethereum/AaveV1Helper.sol b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol new file mode 100644 index 000000000..539769fa1 --- /dev/null +++ b/contracts/protocol/adapters/ethereum/AaveV1Helper.sol @@ -0,0 +1,61 @@ +// 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; + + // solhint-disable-next-line var-name-mixedcase + IWETH public immutable WETH; + + // solhint-disable-next-line var-name-mixedcase + address public constant ETH = address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); + + /* solhint-disable no-empty-blocks*/ + constructor(IWETH _weth) { + WETH = _weth; + } + + /* 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)); + 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); + } + + // 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); + 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); + } + + /* 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 new file mode 100644 index 000000000..a092350ff --- /dev/null +++ b/contracts/protocol/adapters/ethereum/CompoundHelper.sol @@ -0,0 +1,70 @@ +// 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; + + // solhint-disable-next-line var-name-mixedcase + IWETH public immutable WETH; + + // 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 + returns (uint256) + { + return (_liquidityPoolTokenAmount * ICompound(_liquidityPool).exchangeRateStored()) / 1e18; + } + + // solhint-disable-next-line func-name-mixedcase + function calculateAmountInLPToken_Compound(address _liquidityPool, uint256 _depositAmount) + external + view + returns (uint256) + { + 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); + 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); + } + + // 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); + 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); + } + + /* 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 new file mode 100644 index 000000000..739330c84 --- /dev/null +++ b/contracts/protocol/adapters/ethereum/CurveHelper.sol @@ -0,0 +1,325 @@ +// SPDX-License-Identifier:MIT + +pragma solidity ^0.6.12; + +// interfaces +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"; + +// libraries +import { Errors } from "../../../utils/Errors.sol"; + +contract CurveHelper is Modifiers { + using SafeERC20 for IERC20; + + IWETH public immutable WETH; + + constructor(address _registry, IWETH _weth) public Modifiers(_registry) { + WETH = _weth; + } + + // solhint-disable-next-line func-name-mixedcase + 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)); + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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 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, + uint256 _amount, + bool _isDeposit + ) external view returns (uint256) { + 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, + bool _isDeposit + ) external view returns (uint256) { + 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); + } + + // 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); + } + + // 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); + } + + // 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); + } + + // 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); + } + + // 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); + } + + // 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); + } + + // solhint-disable-next-line func-name-mixedcase + 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); + 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); + } + } + + /* solhint-disable no-empty-blocks*/ + receive() external payable {} + /* solhint-enable no-empty-blocks*/ +} diff --git a/contracts/protocol/adapters/ethereum/SwapHelper.sol b/contracts/protocol/adapters/ethereum/SwapHelper.sol new file mode 100644 index 000000000..6a36fcba5 --- /dev/null +++ b/contracts/protocol/adapters/ethereum/SwapHelper.sol @@ -0,0 +1,70 @@ +// SPDX-License-Identifier:MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +// interfaces +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 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); + + // 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, + 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 + }) + ); + } + + 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/earn-protocol-configuration b/contracts/protocol/earn-protocol-configuration index 939b91598..1fe7c2b1f 160000 --- a/contracts/protocol/earn-protocol-configuration +++ b/contracts/protocol/earn-protocol-configuration @@ -1 +1 @@ -Subproject commit 939b91598e62c86de62deb1b419febcda656577f +Subproject commit 1fe7c2b1fa86d54cff66c8ad31d05f2a86253a83 diff --git a/contracts/protocol/lib/CommandBuilder.sol b/contracts/protocol/lib/CommandBuilder.sol new file mode 100644 index 000000000..de3d89531 --- /dev/null +++ b/contracts/protocol/lib/CommandBuilder.sol @@ -0,0 +1,153 @@ +/*solhint-disable no-inline-assembly*/ +/*solhint-disable state-visibility*/ +// SPDX-License-Identifier: MIT + +pragma solidity ^0.6.12; +pragma experimental ABIEncoderV2; + +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 + ) 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 + + 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 + ) public 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 + ) public 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 + ) 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/Modifiers.sol b/contracts/protocol/tokenization/Modifiers.sol new file mode 100644 index 000000000..74b328686 --- /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/VM.sol b/contracts/protocol/tokenization/VM.sol new file mode 100644 index 000000000..48a0e1fc7 --- /dev/null +++ b/contracts/protocol/tokenization/VM.sol @@ -0,0 +1,164 @@ +/*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; + +import { CommandBuilder } from "../lib/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 _writeExecute(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("_writeExecute"); + } + + if (flags & FLAG_TUPLE_RETURN != 0) { + state.writeTuple(bytes1(command << 88), outdata); + } else { + state = state.writeOutputs(bytes1(command << 88), outdata); + } + ++i; + } + 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_STATICCALL) { + (success, outdata) = address(uint160(uint256(command))).staticcall( // target + // inputs + state.buildInputs( + //selector + bytes4(command), + indices + ) + ); + } else { + revert("Invalid calltype"); + } + + if (!success) { + if (outdata.length > 0) { + assembly { + outdata := add(outdata, 68) + } + } + revert("_readExecute"); + } + + 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/Vault.sol b/contracts/protocol/tokenization/Vault.sol index c4c986ac7..1ab12ecdc 100644 --- a/contracts/protocol/tokenization/Vault.sol +++ b/contracts/protocol/tokenization/Vault.sol @@ -4,21 +4,21 @@ pragma solidity ^0.6.12; pragma experimental ABIEncoderV2; // 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 "../earn-protocol-configuration/contracts/Modifiers.sol"; -import { VaultStorageV3 } from "./VaultStorage.sol"; +import { Modifiers } from "./Modifiers.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"; // interfaces import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; @@ -26,9 +26,7 @@ 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 { 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 @@ -40,21 +38,19 @@ 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 @@ -67,7 +63,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) { @@ -109,7 +105,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(); @@ -118,7 +114,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(); } @@ -126,21 +123,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); } @@ -151,59 +151,62 @@ 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 payable override { + _onlyGovernance(); vaultConfiguration = vaultConfiguration & 0xFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; if (_active) { vaultConfiguration = vaultConfiguration | 0x0100000000000000000000000000000000000000000000000000000000000000; - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAllFromStrategy(investStrategySteps); - investStrategyHash = Constants.ZERO_BYTES32; - delete investStrategySteps; - } + _vaultWithdrawFromAllStrategies(); } emit LogEmergencyShutdown((vaultConfiguration & (1 << 248)) != 0, msg.sender); } @@ -211,14 +214,11 @@ contract Vault is /** * @inheritdoc IVault */ - function setUnpaused(bool _unpaused) external override onlyGovernance { + function setUnpaused(bool _unpaused) external payable override { + _onlyGovernance(); vaultConfiguration = (vaultConfiguration & 0xFDFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF); if (!_unpaused) { - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultWithdrawAllFromStrategy(investStrategySteps); - investStrategyHash = Constants.ZERO_BYTES32; - delete investStrategySteps; - } + _vaultWithdrawFromAllStrategies(); } else { vaultConfiguration = 0x0200000000000000000000000000000000000000000000000000000000000000 | @@ -230,14 +230,11 @@ 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++) { + for (uint256 _i; _i < _tokensLen; _i++) { _tokens[_i].safeApprove(_spenders[_i], uint256(-1)); } } @@ -245,42 +242,15 @@ 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++) { + for (uint256 _i; _i < _tokensLen; _i++) { _tokens[_i].safeApprove(_spenders[_i], 0); } } - /** - * @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 */ @@ -311,62 +281,49 @@ contract Vault is /** * @inheritdoc IVault */ - function vaultDepositAllToStrategy() external override { + function vaultDepositSomeToStrategy(bytes32 _strategyHash, uint256 _depositValueUT) external payable override { + _onlyStrategyOperator(); _checkVaultDeposit(); - if (investStrategyHash != Constants.ZERO_BYTES32) { - _vaultDepositToStrategy(investStrategySteps, balanceUT()); - } + _vaultDepositSomeToStrategy(_strategyHash, _depositValueUT); } /** * @inheritdoc IVault */ - function adminCall(bytes[] memory _codes) external override onlyGovernance { - executeCodes(_codes, Errors.ADMIN_CALL); + function vaultWithdrawSomeFromStrategy(bytes32 _strategyHash, uint256 _withdrawAmountLP) external payable override { + _onlyStrategyOperator(); + _vaultWithdrawSomeFromStrategy(_strategyHash, _withdrawAmountLP); } /** * @inheritdoc IVault */ - function claimRewardToken(address _liquidityPool) external override onlyStrategyOperator { - executeCodes( - _liquidityPool.getClaimRewardTokenCode(address(registryContract), payable(address(this))), - Errors.CLAIM_REWARD_FAILED - ); + function adminCall(bytes32[] calldata _commands, bytes[] memory _state) external payable override { + _onlyGovernance(); + _writeExecute(_commands, _state); } /** * @inheritdoc IVault */ - function harvest( - address _rewardToken, - address _dex, - bool _isUniV3, - uint256 _minimumUnderlyingTokenAmount, - uint256 _deadline, - address[] memory _path, - bytes memory _pathUniV3 - ) external 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, + function claimRewardToken(bytes32 _strategyHash) external payable override { + _onlyStrategyOperator(); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + IStrategyRegistry(registryContract.getStrategyRegistry()).getClaimRewardsPlan(address(this), _strategyHash); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } + + /** + * @inheritdoc IVault + */ + function harvestRewards(bytes32 _strategyHash) external payable override { + _onlyStrategyOperator(); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + IStrategyRegistry(registryContract.getStrategyRegistry()).getHarvestRewardsPlan( address(this), - _deadline + _strategyHash ); - } + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); } /** @@ -398,7 +355,26 @@ contract Vault is _approve(_owner, _spender, _value); } - //===Public functions===// + /** + * @inheritdoc IVault + */ + function addStrategy(bytes32 _strategyHash) external override { + _onlyStrategyOperator(); + require(strategies.add(_strategyHash), Errors.ADD_STRATEGY); + emit AddStrategy(_strategyHash); + } + + /** + * @inheritdoc IVault + */ + function removeStrategy(bytes32 _strategyHash) external override { + _onlyStrategyOperator(); + _vaultWithdrawSomeFromStrategy(_strategyHash, getLastStrategyStepBalanceLP(_strategyHash)); + require(strategies.remove(_strategyHash), Errors.REMOVE_STRATEGY); + emit RemoveStrategy(_strategyHash); + } + + //===Public view functions===// /** * @inheritdoc EIP712Base @@ -436,11 +412,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()); } /** @@ -535,61 +507,30 @@ contract Vault is /** * @inheritdoc IVault */ - function getNextBestInvestStrategy() public view override returns (DataTypes.StrategyStep[] memory) { + function getLastStrategyStepBalanceLP(bytes32 _strategyHash) public view override returns (uint256) { return - IRiskManager(registryContract.getRiskManager()).getBestStrategy( - uint256(uint8(vaultConfiguration >> 240)), - underlyingTokensHash + _getUint256( + IStrategyRegistry(registryContract.getStrategyRegistry()).getLastStepBalanceLPPlan( + address(this), + _strategyHash + ) ); } - /** - * @inheritdoc IVault - */ - function getLastStrategyStepBalanceLP(DataTypes.StrategyStep[] memory _investStrategySteps) - public - view - override - returns (uint256) - { - return - _investStrategySteps.getLastStrategyStepBalanceLP( - address(registryContract), - payable(address(this)), - underlyingToken - ); + function getCacheValueUT() external view returns (uint256) { + return _cacheValueUT; } - /** - * @inheritdoc IVault - */ - function getInvestStrategySteps() public view override returns (DataTypes.StrategyStep[] memory) { - return investStrategySteps; + function getCacheAmountLP() external view returns (uint256) { + return _cacheAmountLP; } - /** - * @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].isSwap - ) - ); - } - return keccak256(abi.encodePacked(underlyingTokensHash, hashes)); + 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); } - return Constants.ZERO_BYTES32; } //===Internal functions===// @@ -688,7 +629,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(); @@ -697,24 +639,25 @@ contract Vault is if (_vaultValuePreStratWithdrawUT < _oraUserWithdrawUT) { // withdraw UT shortage from strategy uint256 _expectedStratWithdrawUT = _oraUserWithdrawUT.sub(_vaultValuePreStratWithdrawUT); - - uint256 _oraAmountLP = - investStrategySteps.getOraSomeValueLP( - address(registryContract), - underlyingToken, - _expectedStratWithdrawUT + IStrategyRegistry strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + + for (uint256 _i; _i < strategies.length(); _i++) { + uint256 _a = _oraStratValueUTByStrategy(strategyRegistry, strategies.at(_i)); + _cacheValueUT = _a.sub( + ( + ((_poolOraValueUT.sub(_expectedStratWithdrawUT)).mul((_a.mul(10000))).div(_poolOraValueUT)).div( + 10000 + ) + ) ); + _cacheAmountLP = _getOraSomeValueLPByStrategy(strategyRegistry, strategies.at(_i)); + DataTypes.StrategyPlanInput memory _strategyPlanInput = + strategyRegistry.getWithdrawSomeFromStrategyPlan(address(this), strategies.at(_i)); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } - _vaultWithdrawSomeFromStrategy(investStrategySteps, _oraAmountLP); - - // 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) { @@ -733,69 +676,45 @@ contract Vault is } /** - * @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 + * @dev Internal function to withdraw all investments */ - 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 - ); + function _vaultWithdrawFromAllStrategies() internal { + for (uint256 _i; _i < strategies.length(); _i++) { + _vaultWithdrawSomeFromStrategy(strategies.at(_i), getLastStrategyStepBalanceLP(strategies.at(_i))); } } - /** - * @dev Internal function to withdraw all investments from current strategy - * @param _investStrategySteps array of strategy step tuple - */ - function _vaultWithdrawAllFromStrategy(DataTypes.StrategyStep[] memory _investStrategySteps) internal { - _vaultWithdrawSomeFromStrategy(_investStrategySteps, getLastStrategyStepBalanceLP(_investStrategySteps)); - } - /** * @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_SET); + if (_withdrawAmountLP != 0) { + _cacheAmountLP = _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_SET); + _cacheValueUT = _depositValueUT; + DataTypes.StrategyPlanInput memory _strategyPlanInput = + IStrategyRegistry(registryContract.getStrategyRegistry()).getDepositSomeToStrategyPlan( + address(this), + _strategyHash + ); + _writeExecute(_strategyPlanInput.commands, _strategyPlanInput.state); + } + /** * @inheritdoc IncentivisedERC20 */ @@ -836,17 +755,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 @@ -879,15 +787,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 @@ -917,14 +816,38 @@ 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) { - // totaldebt + function _oraStratValueUT() internal view returns (uint256 _sum) { + IStrategyRegistry _strategyRegistry = IStrategyRegistry(registryContract.getStrategyRegistry()); + uint256 _len = strategies.length(); + for (uint256 _i; _i < _len; _i++) { + _sum += _getUint256(_strategyRegistry.getOraValueUTPlan(address(this), strategies.at(_i))); + } + } + + 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 - investStrategyHash != Constants.ZERO_BYTES32 - ? investStrategySteps.getOraValueUT(address(registryContract), payable(address(this)), underlyingToken) - : 0; + 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/protocol/tokenization/VaultHelper.sol b/contracts/protocol/tokenization/VaultHelper.sol new file mode 100644 index 000000000..35d9e2edd --- /dev/null +++ b/contracts/protocol/tokenization/VaultHelper.sol @@ -0,0 +1,36 @@ +// 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"; + +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, + 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/VaultStorage.sol b/contracts/protocol/tokenization/VaultStorage.sol index b99481e8e..cb0cc8906 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 @@ -35,9 +36,9 @@ contract VaultStorage { mapping(uint256 => DataTypes.BlockVaultValue[]) public blockToBlockVaultValues; /** - * @dev Current vault invest strategy + * @dev cache value in UT for intermediate calculations */ - bytes32 public investStrategyHash; + uint256 internal _cacheValueUT; /** * @dev Maximum amount in underlying token allowed to be deposited by user @@ -107,3 +108,10 @@ 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; + + uint256 internal _cacheAmountLP; +} diff --git a/contracts/utils/BytesLib.sol b/contracts/utils/BytesLib.sol new file mode 100644 index 000000000..31e5f8fb4 --- /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/Errors.sol b/contracts/utils/Errors.sol index 671a5706d..47adbe591 100644 --- a/contracts/utils/Errors.sol +++ b/contracts/utils/Errors.sol @@ -4,31 +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 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/contracts/utils/optyfi-oracle b/contracts/utils/optyfi-oracle new file mode 160000 index 000000000..6eff57139 --- /dev/null +++ b/contracts/utils/optyfi-oracle @@ -0,0 +1 @@ +Subproject commit 6eff57139927efa3f6e749267dfe952b02f1a8d8 diff --git a/deploy/003_deploy_riskmanager.ts b/deploy/003_deploy_riskmanager.ts deleted file mode 100644 index f8cb57e47..000000000 --- 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/004_deploy_strategyprovider.ts b/deploy/003_deploy_strategyregistry.ts similarity index 71% rename from deploy/004_deploy_strategyprovider.ts rename to deploy/003_deploy_strategyregistry.ts index 12956348f..58ee4131f 100644 --- a/deploy/004_deploy_strategyprovider.ts +++ b/deploy/003_deploy_strategyregistry.ts @@ -18,14 +18,14 @@ const func: DeployFunction = async ({ }: HardhatRuntimeEnvironment) => { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.STRATEGY_PROVIDER); + 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("StrategyProvider", { + const result = await deploy("StrategyRegistry", { from: deployer, contract: { abi: artifact.abi, @@ -43,50 +43,50 @@ const func: DeployFunction = async ({ const operatorAddress = await registryV2Instance.operator(); const operatorSigner = await ethers.getSigner(operatorAddress); - const oldStrategyProvider = await registryV2Instance.getStrategyProvider(); - const strategyProviderV2 = await deployments.get("StrategyProvider"); + const oldStrategyRegistry = await registryV2Instance.getStrategyRegistry(); + const strategyRegistry = await deployments.get("StrategyRegistry"); - console.log("==StrategyProvider registration=="); + console.log("==StrategyRegistry registration=="); console.log("\n"); - if (getAddress(oldStrategyProvider) !== getAddress(strategyProviderV2.address)) { - console.log("operator registering StrategyProvider.."); + if (getAddress(oldStrategyRegistry) !== getAddress(strategyRegistry.address)) { + console.log("operator registering StrategyRegistry.."); feeData = await ethers.provider.getFeeData(); - const setStrategyProviderTx = await registryV2Instance + const setStrategyRegistryTx = await registryV2Instance .connect(operatorSigner) - .setStrategyProvider(strategyProviderV2.address, { + .setStrategyRegistry(strategyRegistry.address, { type: 2, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), }); - await setStrategyProviderTx.wait(1); + await setStrategyRegistryTx.wait(1); } else { - console.log("StrategyProvider already registered"); + console.log("StrategyRegistry already registered"); console.log("\n"); } if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const strategyProviderV2 = await deployments.get("StrategyProvider"); + const strategyRegistry = await deployments.get("StrategyRegistry"); if (networkName === "tenderly") { await tenderly.verify({ - name: "StrategyProvider", - address: strategyProviderV2.address, + name: "StrategyRegistry", + address: strategyRegistry.address, constructorArguments: [registryProxyAddress], - contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyProvider.sol:StrategyProvider", + contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyRegistry.sol:StrategyRegistry", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "StrategyProvider", - address: strategyProviderV2.address, + name: "StrategyRegistry", + address: strategyRegistry.address, constructorArguments: [registryProxyAddress], - contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyProvider.sol:StrategyProvider", + contract: "contracts/protocol/earn-protocol-configuration/contracts/StrategyRegistry.sol:StrategyRegistry", }); } } } }; export default func; -func.tags = ["StrategyProvider"]; +func.tags = ["StrategyRegistry"]; func.dependencies = ["Registry"]; diff --git a/deploy/005_deploy_strategymanager.ts b/deploy/004_deploy_commandbuilder.ts similarity index 75% rename from deploy/005_deploy_strategymanager.ts rename to deploy/004_deploy_commandbuilder.ts index 7b44c2d8a..45ef43e0b 100644 --- a/deploy/005_deploy_strategymanager.ts +++ b/deploy/004_deploy_commandbuilder.ts @@ -17,13 +17,13 @@ const func: DeployFunction = async ({ }: HardhatRuntimeEnvironment) => { const { deploy } = deployments; const { deployer } = await getNamedAccounts(); - const artifact = await deployments.getArtifact(ESSENTIAL_CONTRACTS.STRATEGY_MANAGER); + 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("StrategyManager", { + const result = await deploy("CommandBuilder", { from: deployer, contract: { abi: artifact.abi, @@ -39,26 +39,26 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY === "true") { if (result.newlyDeployed) { - const strategyManager = await deployments.get("StrategyManager"); + const commandBuilder = await deployments.get("CommandBuilder"); if (networkName === "tenderly") { await tenderly.verify({ - name: "StrategyManager", - address: strategyManager.address, + name: "CommandBuilder", + address: commandBuilder.address, constructorArguments: [], - contract: "contracts/protocol/lib/StrategyManager.sol:StrategyManager", + contract: "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "StrategyManager", - address: strategyManager.address, + name: "CommandBuilder", + address: commandBuilder.address, constructorArguments: [], - contract: "contracts/protocol/lib/StrategyManager.sol:StrategyManager", + contract: "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder", }); } } } }; export default func; -func.tags = ["StrategyManager"]; +func.tags = ["CommandBuilder"]; diff --git a/deploy_mainnet/004_deploy_aavev2adapter.ts b/deploy/005_deploy_vaulthelper.ts similarity index 69% rename from deploy_mainnet/004_deploy_aavev2adapter.ts rename to deploy/005_deploy_vaulthelper.ts index f26cb3b7c..8b820f46b 100644 --- a/deploy_mainnet/004_deploy_aavev2adapter.ts +++ b/deploy/005_deploy_vaulthelper.ts @@ -9,27 +9,26 @@ 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("AaveV2Adapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - + const artifact = await deployments.getArtifact("VaultHelper"); const chainId = await getChainId(); const networkName = network.name; + const feeData = await ethers.provider.getFeeData(); - const result = await deploy("AaveV2Adapter", { + const result = await deploy("VaultHelper", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress], + args: [], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -38,25 +37,27 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const aavev2Adapter = await deployments.get("AaveV2Adapter"); + const vaultHelperMainnet = await deployments.get("VaultHelper"); if (networkName === "tenderly") { await tenderly.verify({ - name: "AaveV2Adapter", - address: aavev2Adapter.address, - constructorArguments: [registryProxyAddress], + name: "VaultHelper", + address: vaultHelperMainnet.address, + constructorArguments: [], + contract: "VaultHelperMainnet", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "AaveV2Adapter", - address: aavev2Adapter.address, - constructorArguments: [registryProxyAddress], + name: "VaultHelper", + address: vaultHelperMainnet.address, + constructorArguments: [], + contract: "VaultHelper", }); } } } }; export default func; -func.tags = ["AaveV2Adapter"]; +func.tags = ["VaultHelper"]; func.dependencies = ["Registry"]; diff --git a/deploy/006_deploy_harvestcodeprovider.ts b/deploy/006_deploy_harvestcodeprovider.ts deleted file mode 100644 index c6ec14f20..000000000 --- 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_avalanche/005_deploy_opUSDC-Earn.ts b/deploy_avalanche/005_deploy_opUSDC-Earn.ts index 247aff06b..8bb582f76 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 b02a177b6..40f9a299b 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 8b0d318ce..975b8ee86 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/001_deploy_curveswappooladapter.ts b/deploy_mainnet/001_deploy_curveswappooladapter.ts deleted file mode 100644 index 75b3120e2..000000000 --- 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/005_deploy_compoundadapter.ts b/deploy_mainnet/002_deploy_aavev1helper.ts similarity index 66% rename from deploy_mainnet/005_deploy_compoundadapter.ts rename to deploy_mainnet/002_deploy_aavev1helper.ts index 39f1c5600..fa232c440 100644 --- a/deploy_mainnet/005_deploy_compoundadapter.ts +++ b/deploy_mainnet/002_deploy_aavev1helper.ts @@ -1,6 +1,7 @@ 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; @@ -9,27 +10,26 @@ 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("AaveV1Helper"); const chainId = await getChainId(); const networkName = network.name; + const feeData = await ethers.provider.getFeeData(); - const result = await deploy("CompoundAdapter", { + const result = await deploy("AaveV1Helper", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress], + args: [EthereumTokens.WRAPPED_TOKENS.WETH], log: true, skipIfAlreadyDeployed: true, maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas @@ -38,25 +38,27 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const compoundAdapter = await deployments.get("CompoundAdapter"); + const aavev1helper = await deployments.get("AaveV1Helper"); if (networkName === "tenderly") { await tenderly.verify({ - name: "CompoundAdapter", - address: compoundAdapter.address, - constructorArguments: [registryProxyAddress], + 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: "CompoundAdapter", - address: compoundAdapter.address, - constructorArguments: [registryProxyAddress], + name: "AaveV1Helper", + address: aavev1helper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH], + contract: "AaveV1Helper", }); } } } }; export default func; -func.tags = ["CompoundAdapter"]; +func.tags = ["AaveV1Helper"]; func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/012_deploy_sushi-bar-adapter.ts b/deploy_mainnet/002_deploy_compoundhelper.ts similarity index 61% rename from deploy_mainnet/012_deploy_sushi-bar-adapter.ts rename to deploy_mainnet/002_deploy_compoundhelper.ts index 817bae59e..2b37ffc70 100644 --- a/deploy_mainnet/012_deploy_sushi-bar-adapter.ts +++ b/deploy_mainnet/002_deploy_compoundhelper.ts @@ -1,6 +1,8 @@ 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; @@ -9,55 +11,55 @@ 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("SushiBarAdapter"); - const registryProxyAddress = await (await deployments.get("RegistryProxy")).address; - + const artifact = await deployments.getArtifact("CompoundHelper"); const chainId = await getChainId(); const networkName = network.name; + const feeData = await ethers.provider.getFeeData(); - const result = await deploy("SushiBarAdapter", { + const result = await deploy("CompoundHelper", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress], + 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"]), }); - const sushiBarAdapter = await deployments.get("SushiBarAdapter"); - if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { + const compoundhelper = await deployments.get("CompoundHelper"); if (networkName === "tenderly") { await tenderly.verify({ - name: "SushiBarAdapter", - address: sushiBarAdapter.address, - constructorArguments: [registryProxyAddress], + 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: "SushiBarAdapter", - address: sushiBarAdapter.address, - constructorArguments: [registryProxyAddress], + name: "CompoundHelper", + address: compoundhelper.address, + constructorArguments: [EthereumTokens.WRAPPED_TOKENS.WETH, Compound.pools.eth.lpToken], + contract: "CompoundHelper", }); } } } }; export default func; -func.tags = ["SushiBarAdapter"]; +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 000000000..a2c5303d8 --- /dev/null +++ b/deploy_mainnet/002_deploy_curvehelper.ts @@ -0,0 +1,152 @@ +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; + +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 registryProxyAddress = (await deployments.get("RegistryProxy")).address; + const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); + 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: [registryProxyAddress, 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 curvehelper = await deployments.get("CurveHelper"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "CurveHelper", + address: curvehelper.address, + constructorArguments: [registryProxyAddress, EthereumTokens.WRAPPED_TOKENS.WETH], + contract: "CurveHelper", + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "CurveHelper", + address: curvehelper.address, + constructorArguments: [registryProxyAddress, EthereumTokens.WRAPPED_TOKENS.WETH], + contract: "CurveHelper", + }); + } + } + } + + 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 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, + Curve.CurveSwapPool.usdc_3crv.pool, + ); + + const threeCrvCurveFraxAllowance = await threeCrvInstance.allowance( + curveHelperInstance.address, + 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); + } + + if (!threeCrvCurveFraxAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + 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); + 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"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts b/deploy_mainnet/002_deploy_curvemetapoolswapadapter.ts deleted file mode 100644 index caa6fc144..000000000 --- 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/003_deploy_aavev1adapter.ts b/deploy_mainnet/002_deploy_swaphelper.ts similarity index 68% rename from deploy_mainnet/003_deploy_aavev1adapter.ts rename to deploy_mainnet/002_deploy_swaphelper.ts index d8340e657..51abd462d 100644 --- a/deploy_mainnet/003_deploy_aavev1adapter.ts +++ b/deploy_mainnet/002_deploy_swaphelper.ts @@ -5,31 +5,33 @@ 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("AaveV1Adapter"); + const artifact = await deployments.getArtifact("SwapHelper"); 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", { + const result = await deploy("SwapHelper", { from: deployer, contract: { abi: artifact.abi, bytecode: artifact.bytecode, deployedBytecode: artifact.deployedBytecode, }, - args: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], + args: [registryProxyAddress, 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 aavev1Adapter = await deployments.get("AaveV1Adapter"); + const swapHelper = await deployments.get("SwapHelper"); if (networkName === "tenderly") { await tenderly.verify({ - name: "AaveV1Adapter", - address: aavev1Adapter.address, - constructorArguments: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], + name: "SwapHelper", + address: swapHelper.address, + constructorArguments: [registryProxyAddress, uniswapV3Router], + contract: "SwapHelper", }); } else if (!["31337"].includes(chainId)) { await waitforme(20000); await run("verify:verify", { - name: "AaveV1Adapter", - address: aavev1Adapter.address, - constructorArguments: [registryProxyAddress, "0x68d2BA9fc2009c39384F5a0e28a4f1E72E6AB1fA"], + name: "SwapHelper", + address: swapHelper.address, + constructorArguments: [registryProxyAddress, uniswapV3Router], + contract: "SwapHelper", }); } } } }; export default func; -func.tags = ["AaveV1Adapter"]; +func.tags = ["SwapHelper"]; func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/015_deploy_opWETH-Save.ts b/deploy_mainnet/003_deploy_opWETH-Save.ts similarity index 64% rename from deploy_mainnet/015_deploy_opWETH-Save.ts rename to deploy_mainnet/003_deploy_opWETH-Save.ts index 29dada57a..7030ffa0d 100644 --- a/deploy_mainnet/015_deploy_opWETH-Save.ts +++ b/deploy_mainnet/003_deploy_opWETH-Save.ts @@ -2,10 +2,22 @@ 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 { + CompoundHelper, + CompoundHelper__factory, + ERC20, + ERC20__factory, + Vault, + VaultHelper__factory, + Vault__factory, +} from "../typechain"; const CONTRACTS_VERIFY = process.env.CONTRACTS_VERIFY; @@ -24,7 +36,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 +126,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, @@ -153,7 +165,67 @@ const func: DeployFunction = async ({ } } } + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Save_Proxy")).address) + ); + + const compoundHelperInstance = ( + await ethers.getContractAt(CompoundHelper__factory.abi, (await deployments.get("CompoundHelper")).address) + ); + + const approvalTokens = []; + const approvalSpender = []; + + 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 wethCompoundHelperAllowance = await wethInstance.allowance( + vaultInstance.address, + compoundHelperInstance.address, + ); + const cethCompoundHelperAllowance = await cethInstance.allowance( + vaultInstance.address, + compoundHelperInstance.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 (!wethCompoundHelperAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(wethInstance.address); + approvalSpender.push(compoundHelperInstance.address); + } + + if (!cethCompoundHelperAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(cethInstance.address); + approvalSpender.push(compoundHelperInstance.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/016_deploy_opWETH-Earn.ts b/deploy_mainnet/004_deploy_opWETH-Earn.ts similarity index 80% rename from deploy_mainnet/016_deploy_opWETH-Earn.ts rename to deploy_mainnet/004_deploy_opWETH-Earn.ts index 5be90ceaf..af9d52864 100644 --- a/deploy_mainnet/016_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"; @@ -26,7 +28,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 +116,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, @@ -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/017_deploy_opNEWO-Invst.ts b/deploy_mainnet/005_deploy_opNEWO-Invst.ts similarity index 97% rename from deploy_mainnet/017_deploy_opNEWO-Invst.ts rename to deploy_mainnet/005_deploy_opNEWO-Invst.ts index 8c2fc87af..ab7c069d5 100644 --- a/deploy_mainnet/017_deploy_opNEWO-Invst.ts +++ b/deploy_mainnet/005_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/006_deploy_convexadapter.ts b/deploy_mainnet/006_deploy_convexadapter.ts deleted file mode 100644 index 261c5319a..000000000 --- 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 97% rename from deploy_mainnet/018_deploy_opAAVE-Invst.ts rename to deploy_mainnet/006_deploy_opAAVE-Invst.ts index 338829499..44b1ae38d 100644 --- a/deploy_mainnet/018_deploy_opAAVE-Invst.ts +++ b/deploy_mainnet/006_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/007_deploy_newostakingadapter.ts b/deploy_mainnet/007_deploy_newostakingadapter.ts deleted file mode 100644 index 6580c4551..000000000 --- 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 97% rename from deploy_mainnet/019_deploy_opAPE-Invst.ts rename to deploy_mainnet/007_deploy_opAPE-Invst.ts index 8977e6e49..e0c64a53b 100644 --- a/deploy_mainnet/019_deploy_opAPE-Invst.ts +++ b/deploy_mainnet/007_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/008_deploy_opMANA-Invst.ts similarity index 97% rename from deploy_mainnet/020_deploy_opMANA-Invst.ts rename to deploy_mainnet/008_deploy_opMANA-Invst.ts index da0e66efa..f5d9a3910 100644 --- a/deploy_mainnet/020_deploy_opMANA-Invst.ts +++ b/deploy_mainnet/008_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/009_deploy_opSUSHI-Invst.ts similarity index 97% rename from deploy_mainnet/021_deploy_opSUSHI-Invst.ts rename to deploy_mainnet/009_deploy_opSUSHI-Invst.ts index 2b91cb9ed..af0177288 100644 --- a/deploy_mainnet/021_deploy_opSUSHI-Invst.ts +++ b/deploy_mainnet/009_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/009_deploy_sushiswappooladapterethereum.ts b/deploy_mainnet/009_deploy_sushiswappooladapterethereum.ts deleted file mode 100644 index 834864ea7..000000000 --- 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 97% rename from deploy_mainnet/022_deploy_opLINK-Invst.ts rename to deploy_mainnet/010_deploy_opLINK-Invst.ts index 7e39dc0b8..5bf461d6f 100644 --- a/deploy_mainnet/022_deploy_opLINK-Invst.ts +++ b/deploy_mainnet/010_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/010_deploy_sushiswapmasterchefv1adapter.ts b/deploy_mainnet/010_deploy_sushiswapmasterchefv1adapter.ts deleted file mode 100644 index 20739f6c9..000000000 --- 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 97% rename from deploy_mainnet/023_deploy_opENS-Invst.ts rename to deploy_mainnet/011_deploy_opENS-Invst.ts index 8c160ab90..279ce4ae6 100644 --- a/deploy_mainnet/023_deploy_opENS-Invst.ts +++ b/deploy_mainnet/011_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/011_deploy_sushiswapmasterchefv2adapter.ts b/deploy_mainnet/011_deploy_sushiswapmasterchefv2adapter.ts deleted file mode 100644 index a8ff16e95..000000000 --- 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 97% rename from deploy_mainnet/024_deploy_opCOMP-Invst.ts rename to deploy_mainnet/012_deploy_opCOMP-Invst.ts index de9ee93ac..766a16e14 100644 --- a/deploy_mainnet/024_deploy_opCOMP-Invst.ts +++ b/deploy_mainnet/012_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/013_deploy_curvecryptopooladapter.ts b/deploy_mainnet/013_deploy_curvecryptopooladapter.ts deleted file mode 100644 index 412c30a1b..000000000 --- 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 97% rename from deploy_mainnet/025_deploy_opIMX-Invst.ts rename to deploy_mainnet/013_deploy_opIMX-Invst.ts index afa1ac5a8..10d65921d 100644 --- a/deploy_mainnet/025_deploy_opIMX-Invst.ts +++ b/deploy_mainnet/013_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/014_deploy_metapoolgaugeadapter.ts b/deploy_mainnet/014_deploy_metapoolgaugeadapter.ts deleted file mode 100644 index a6ca456f3..000000000 --- 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 97% rename from deploy_mainnet/026_deploy_opALCX-Invst.ts rename to deploy_mainnet/014_deploy_opALCX-Invst.ts index 1a568a7f3..1967aebdf 100644 --- a/deploy_mainnet/026_deploy_opALCX-Invst.ts +++ b/deploy_mainnet/014_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/015_deploy_opCRV-Invst.ts similarity index 97% rename from deploy_mainnet/027_deploy_opCRV-Invst.ts rename to deploy_mainnet/015_deploy_opCRV-Invst.ts index b583acfd6..c0bcc637f 100644 --- a/deploy_mainnet/027_deploy_opCRV-Invst.ts +++ b/deploy_mainnet/015_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/016_deploy_opCVX-Invst.ts similarity index 97% rename from deploy_mainnet/028_deploy_opCVX-Invst.ts rename to deploy_mainnet/016_deploy_opCVX-Invst.ts index bc28c4d29..da64a56d6 100644 --- a/deploy_mainnet/028_deploy_opCVX-Invst.ts +++ b/deploy_mainnet/016_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/017_deploy_opYFI-Invst.ts similarity index 97% rename from deploy_mainnet/029_deploy_opYFI-Invst.ts rename to deploy_mainnet/017_deploy_opYFI-Invst.ts index 17ad78826..a7f010058 100644 --- a/deploy_mainnet/029_deploy_opYFI-Invst.ts +++ b/deploy_mainnet/017_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/018_deploy_opDAI-Save.ts similarity index 69% rename from deploy_mainnet/030_deploy_opDAI-Save.ts rename to deploy_mainnet/018_deploy_opDAI-Save.ts index 737000b72..3d42ba14c 100644 --- a/deploy_mainnet/030_deploy_opDAI-Save.ts +++ b/deploy_mainnet/018_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; @@ -23,7 +27,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 +117,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, @@ -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/019_deploy_opUSDT-Save.ts similarity index 68% rename from deploy_mainnet/031_deploy_opUSDT-Save.ts rename to deploy_mainnet/019_deploy_opUSDT-Save.ts index cd1fc17fa..68487e45e 100644 --- a/deploy_mainnet/031_deploy_opUSDT-Save.ts +++ b/deploy_mainnet/019_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; @@ -23,7 +27,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 +117,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, @@ -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/020_deploy_opWBTC-Save.ts similarity index 68% rename from deploy_mainnet/032_deploy_opWBTC-Save.ts rename to deploy_mainnet/020_deploy_opWBTC-Save.ts index ae753fffc..401679ef2 100644 --- a/deploy_mainnet/032_deploy_opWBTC-Save.ts +++ b/deploy_mainnet/020_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; @@ -23,7 +27,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 +117,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, @@ -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/deploy_mainnet/033_deploy_opUSD3-Earn.ts b/deploy_mainnet/021_deploy_opUSD3-Earn.ts similarity index 94% rename from deploy_mainnet/033_deploy_opUSD3-Earn.ts rename to deploy_mainnet/021_deploy_opUSD3-Earn.ts index afb2cf8cd..659421286 100644 --- a/deploy_mainnet/033_deploy_opUSD3-Earn.ts +++ b/deploy_mainnet/021_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, @@ -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/deploy_mainnet/034_deploy_opWBTC-Earn.ts b/deploy_mainnet/022_deploy_opWBTC-Earn.ts similarity index 97% rename from deploy_mainnet/034_deploy_opWBTC-Earn.ts rename to deploy_mainnet/022_deploy_opWBTC-Earn.ts index af1e67325..1873fc9bb 100644 --- a/deploy_mainnet/034_deploy_opWBTC-Earn.ts +++ b/deploy_mainnet/022_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/023_deploy_opUSDC-Earn.ts b/deploy_mainnet/023_deploy_opUSDC-Earn.ts new file mode 100644 index 000000000..f7de44dcb --- /dev/null +++ b/deploy_mainnet/023_deploy_opUSDC-Earn.ts @@ -0,0 +1,338 @@ +import hre from "hardhat"; +import { DeployFunction } from "hardhat-deploy/dist/types"; +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 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 { CurveHelper, CurveHelper__factory, ERC20, ERC20__factory, Vault, Vault__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, admin } = await getNamedAccounts(); + const chainId = await getChainId(); + const artifact = await deployments.getArtifact("Vault"); + const registryProxyAddress = (await deployments.get("RegistryProxy")).address; + 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); + + const onlySetTokensHash = []; + const approveTokenAndMapHash = []; + const usdcApproved = await registryInstance.isApprovedToken(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address); + const tokenHashes: string[] = await registryInstance.getTokenHashes(); + if (usdcApproved && !tokenHashes.includes(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash)) { + console.log("only set USDC hash"); + console.log("\n"); + onlySetTokensHash.push([ + MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, + [MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address], + ]); + } + if (!usdcApproved && !tokenHashes.includes(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash)) { + console.log("approve USDC and set hash"); + console.log("\n"); + approveTokenAndMapHash.push([ + MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, + [MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address], + ]); + } + if (approveTokenAndMapHash.length > 0) { + console.log("approveTokenAndMapHash ", JSON.stringify(approveTokenAndMapHash, null, 4)); + if (getAddress(deployer) === getAddress(operatorAddress)) { + console.log("approve token and map hash"); + console.log("\n"); + const feeData = await ethers.provider.getFeeData(); + const approveTokenAndMapToTokensHashTx = await registryInstance + .connect(operator) + ["approveTokenAndMapToTokensHash((bytes32,address[])[])"](approveTokenAndMapHash, { + type: 2, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await approveTokenAndMapToTokensHashTx.wait(1); + } else { + console.log("cannot approve token and map hash as signer is not the operator"); + } + } + + if (onlySetTokensHash.length > 0) { + console.log("onlySetTokensHash ", JSON.stringify(onlySetTokensHash, null, 4)); + if (getAddress(deployer) === getAddress(operatorAddress)) { + console.log("operator mapping only tokenshash to tokens.."); + console.log("\n"); + const feeData = await ethers.provider.getFeeData(); + const onlyMapToTokensHashTx = await registryInstance + .connect(operator) + ["setTokensHashToTokens((bytes32,address[])[])"](onlySetTokensHash, { + type: 2, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + await onlyMapToTokensHashTx.wait(1); + } else { + console.log("cannot map tokenshash to tokens as signer is not the operator"); + } + } + + const networkName = network.name; + const feeData = await ethers.provider.getFeeData(); + const proxyArgs: { methodName: string; args: any[] } = { + methodName: "initialize", + args: [ + registryProxyAddress, //address _registry + MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, //bytes32 _underlyingTokensHash + "0x62689e8751ba85bee0855c30d61d17345faa5b23e82626a83f8d63db50d67694", //bytes32 _whitelistedAccountsRoot + "USDC", //string memory _symbol + "1", //uint256 _riskProfileCode + "905369955037451290754171167376807445279006054759646228016501227483694104576", //uint256 _vaultConfiguration + "100000000000", //uint256 _userDepositCapUT + "0", //uint256 _minimumDepositValueUT + "10000000000000", //uint256 _totalValueLockedLimitUT + ], + }; + const result = await deploy("opUSDC-Earn", { + from: deployer, + contract: { + abi: artifact.abi, + bytecode: artifact.bytecode, + deployedBytecode: artifact.deployedBytecode, + }, + args: [registryProxyAddress], + log: true, + skipIfAlreadyDeployed: true, + libraries: { + "contracts/protocol/lib/CommandBuilder.sol:CommandBuilder": commandBuilder.address, + }, + proxy: { + owner: admin, + upgradeIndex: networkName == "hardhat" ? 0 : 3, + proxyContract: "AdminUpgradeabilityProxy", + implementationName: "opWETH-Save_Implementation", + execute: { + init: proxyArgs, + onUpgrade: proxyArgs, + }, + }, + maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas + maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), + }); + + if (CONTRACTS_VERIFY == "true") { + if (result.newlyDeployed) { + const vault = await deployments.get("opUSDC-Earn"); + if (networkName === "tenderly") { + await tenderly.verify({ + name: "opUSDC-Earn", + address: vault.address, + constructorArguments: [registryProxyAddress], + }); + } else if (!["31337"].includes(chainId)) { + await waitforme(20000); + + await run("verify:verify", { + name: "opUSDC-Earn", + address: vault.address, + constructorArguments: [registryProxyAddress], + }); + } + } + } + const vaultInstance = ( + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).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 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 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); + 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, + ); + const usdcSushiswapAllowance = await usdcInstance.allowance( + vaultInstance.address, + 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, + 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, + ); + + 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, + ); + + 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); + } + + 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); + } + + if (!usdcSushiswapAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + approvalSpender.push(EthereumSushiswap.SushiswapRouter.address); + } + + if (!usdcCurveHelperAllowance.gt(parseUnits("1000000", "6"))) { + approvalTokens.push(usdcInstance.address); + 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); + } + + if (!threeCrvCurveGaugeAllowance.gt(parseEther("1000000"))) { + approvalTokens.push(threeCrvInstance.address); + 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 (!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 (!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); + 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-Earn"]; +func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/035_deploy_opUSDC-Earn.ts b/deploy_mainnet/024_deploy_opUSDC-Save.ts similarity index 77% rename from deploy_mainnet/035_deploy_opUSDC-Earn.ts rename to deploy_mainnet/024_deploy_opUSDC-Save.ts index 375014ad0..49567a277 100644 --- a/deploy_mainnet/035_deploy_opUSDC-Earn.ts +++ b/deploy_mainnet/024_deploy_opUSDC-Save.ts @@ -1,10 +1,11 @@ import hre from "hardhat"; import { DeployFunction } from "hardhat-deploy/dist/types"; import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { getAddress, parseEther, parseUnits } 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 EthereumSushiswap from "@optyfi/defi-legos/ethereum/sushiswap/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"; @@ -26,7 +27,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); @@ -51,6 +52,7 @@ const func: DeployFunction = async ({ [MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address], ]); } + if (approveTokenAndMapHash.length > 0) { console.log("approveTokenAndMapHash ", JSON.stringify(approveTokenAndMapHash, null, 4)); if (getAddress(deployer) === getAddress(operatorAddress)) { @@ -96,16 +98,16 @@ const func: DeployFunction = async ({ args: [ registryProxyAddress, //address _registry MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, //bytes32 _underlyingTokensHash - "0x62689e8751ba85bee0855c30d61d17345faa5b23e82626a83f8d63db50d67694", //bytes32 _whitelistedAccountsRoot + "0x0000000000000000000000000000000000000000000000000000000000000000", //bytes32 _whitelistedAccountsRoot "USDC", //string memory _symbol - "1", //uint256 _riskProfileCode - "905369955037451290754171167376807445279006054759646228016501227483694104576", //uint256 _vaultConfiguration - "100000000000", //uint256 _userDepositCapUT + "0", //uint256 _riskProfileCode + "0", //uint256 _vaultConfiguration + "0", //uint256 _userDepositCapUT "0", //uint256 _minimumDepositValueUT - "10000000000000", //uint256 _totalValueLockedLimitUT + "0", //uint256 _totalValueLockedLimitUT ], }; - const result = await deploy("opUSDC-Earn", { + const result = await deploy("opUSDC-Save", { from: deployer, contract: { abi: artifact.abi, @@ -116,11 +118,11 @@ 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, - upgradeIndex: networkName == "hardhat" ? 0 : 3, + upgradeIndex: 0, proxyContract: "AdminUpgradeabilityProxy", implementationName: "opWETH-Save_Implementation", execute: { @@ -134,10 +136,10 @@ const func: DeployFunction = async ({ if (CONTRACTS_VERIFY == "true") { if (result.newlyDeployed) { - const vault = await deployments.get("opUSDC-Earn"); + const vault = await deployments.get("opUSDC-Save"); if (networkName === "tenderly") { await tenderly.verify({ - name: "opUSDC-Earn", + name: "opUSDC-Save", address: vault.address, constructorArguments: [registryProxyAddress], }); @@ -145,41 +147,43 @@ const func: DeployFunction = async ({ await waitforme(20000); await run("verify:verify", { - name: "opUSDC-Earn", + name: "opUSDC-Save", address: vault.address, constructorArguments: [registryProxyAddress], }); } } } + const vaultInstance = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opUSDC-Save_Proxy")).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 ausdcInstance = await ethers.getContractAt(ERC20__factory.abi, AaveV2.pools.usdc.lpToken); + const cusdcInstance = await ethers.getContractAt(Compound.cToken.abi, Compound.pools.usdc.pool); - const wethSushiswapAllowance = await wethInstance.allowance( - vaultInstance.address, - EthereumSushiswap.SushiswapRouter.address, - ); - const usdcSushiswapAllowance = await usdcInstance.allowance( - vaultInstance.address, - EthereumSushiswap.SushiswapRouter.address, - ); + const usdcAaveV2Allowance = await usdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); + const ausdcAaveV2Allowance = await ausdcInstance.allowance(vaultInstance.address, AaveV2.LendingPool.address); - if (!wethSushiswapAllowance.gt(parseEther("1000000"))) { - approvalTokens.push(wethInstance.address); - approvalSpender.push(EthereumSushiswap.SushiswapRouter.address); - } + const usdcCompoundAllowance = await usdcInstance.allowance(vaultInstance.address, cusdcInstance.address); - if (!usdcSushiswapAllowance.gt(parseUnits("1000000", "6"))) { + if (!usdcAaveV2Allowance.gt(parseUnits("1000000", "6"))) { approvalTokens.push(usdcInstance.address); - approvalSpender.push(EthereumSushiswap.SushiswapRouter.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); @@ -196,5 +200,5 @@ const func: DeployFunction = async ({ } }; export default func; -func.tags = ["opUSDC-Earn"]; -func.dependencies = ["Registry"]; +func.tags = ["opUSDC-Save"]; +func.dependencies = ["Registry", "CommandBuilder"]; diff --git a/deploy_mainnet/037_deploy_opUSDC-Invst.ts b/deploy_mainnet/025_deploy_opUSDC-Invst.ts similarity index 98% rename from deploy_mainnet/037_deploy_opUSDC-Invst.ts rename to deploy_mainnet/025_deploy_opUSDC-Invst.ts index 99d343d6e..9ec03610f 100644 --- a/deploy_mainnet/037_deploy_opUSDC-Invst.ts +++ b/deploy_mainnet/025_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/026_deploy_opWETH-Invst.ts similarity index 98% rename from deploy_mainnet/038_deploy_opWETH-Invst.ts rename to deploy_mainnet/026_deploy_opWETH-Invst.ts index acc50c8a2..32788478e 100644 --- a/deploy_mainnet/038_deploy_opWETH-Invst.ts +++ b/deploy_mainnet/026_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_mainnet/040_set_price_oracle_feed.ts b/deploy_mainnet/027_set_price_oracle_feed.ts similarity index 84% rename from deploy_mainnet/040_set_price_oracle_feed.ts rename to deploy_mainnet/027_set_price_oracle_feed.ts index eaf160fb3..2af3bd938 100644 --- a/deploy_mainnet/040_set_price_oracle_feed.ts +++ b/deploy_mainnet/027_set_price_oracle_feed.ts @@ -14,6 +14,9 @@ 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 USDC_USD_FEED = "0x8fFfFfd4AfB6115b954Bd326cbe7B4BA576818f6"; const owner = await optyfiOracleInstance.owner(); const feedToTokens = [ @@ -32,6 +35,21 @@ 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, + }, + { + tokenA: ethereumTokens.PLAIN_TOKENS.USDC, + tokenB: ethereumTokens.PLAIN_TOKENS.USD, + priceFeed: USDC_USD_FEED, + }, ]; const pendingFeedToTokens = []; for (const feedToToken of feedToTokens) { @@ -66,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/deploy_mainnet/036_deploy_opUSDC-Save.ts b/deploy_mainnet/036_deploy_opUSDC-Save.ts deleted file mode 100644 index 53b112188..000000000 --- a/deploy_mainnet/036_deploy_opUSDC-Save.ts +++ /dev/null @@ -1,156 +0,0 @@ -import hre from "hardhat"; -import { DeployFunction } from "hardhat-deploy/dist/types"; -import { HardhatRuntimeEnvironment } from "hardhat/types"; -import { getAddress } from "ethers/lib/utils"; -import { BigNumber } from "ethers"; -import { MULTI_CHAIN_VAULT_TOKENS } from "../helpers/constants/tokens"; -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, admin } = await getNamedAccounts(); - const chainId = await getChainId(); - const artifact = await deployments.getArtifact("Vault"); - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; - const strategyManager = await deployments.get("StrategyManager"); - const registryInstance = await hre.ethers.getContractAt(ESSENTIAL_CONTRACTS.REGISTRY, registryProxyAddress); - const operatorAddress = await registryInstance.getOperator(); - const operator = await hre.ethers.getSigner(operatorAddress); - - const onlySetTokensHash = []; - const approveTokenAndMapHash = []; - const usdcApproved = await registryInstance.isApprovedToken(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address); - const tokenHashes: string[] = await registryInstance.getTokenHashes(); - if (usdcApproved && !tokenHashes.includes(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash)) { - console.log("only set USDC hash"); - console.log("\n"); - onlySetTokensHash.push([ - MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, - [MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address], - ]); - } - if (!usdcApproved && !tokenHashes.includes(MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash)) { - console.log("approve USDC and set hash"); - console.log("\n"); - approveTokenAndMapHash.push([ - MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, - [MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.address], - ]); - } - - if (approveTokenAndMapHash.length > 0) { - console.log("approveTokenAndMapHash ", JSON.stringify(approveTokenAndMapHash, null, 4)); - if (getAddress(deployer) === getAddress(operatorAddress)) { - console.log("approve token and map hash"); - console.log("\n"); - const feeData = await ethers.provider.getFeeData(); - const approveTokenAndMapToTokensHashTx = await registryInstance - .connect(operator) - ["approveTokenAndMapToTokensHash((bytes32,address[])[])"](approveTokenAndMapHash, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await approveTokenAndMapToTokensHashTx.wait(1); - } else { - console.log("cannot approve token and map hash as signer is not the operator"); - } - } - - if (onlySetTokensHash.length > 0) { - console.log("onlySetTokensHash ", JSON.stringify(onlySetTokensHash, null, 4)); - if (getAddress(deployer) === getAddress(operatorAddress)) { - console.log("operator mapping only tokenshash to tokens.."); - console.log("\n"); - const feeData = await ethers.provider.getFeeData(); - const onlyMapToTokensHashTx = await registryInstance - .connect(operator) - ["setTokensHashToTokens((bytes32,address[])[])"](onlySetTokensHash, { - type: 2, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - await onlyMapToTokensHashTx.wait(1); - } else { - console.log("cannot map tokenshash to tokens as signer is not the operator"); - } - } - - const networkName = network.name; - const feeData = await ethers.provider.getFeeData(); - const proxyArgs: { methodName: string; args: any[] } = { - methodName: "initialize", - args: [ - registryProxyAddress, //address _registry - MULTI_CHAIN_VAULT_TOKENS[chainId].USDC.hash, //bytes32 _underlyingTokensHash - "0x0000000000000000000000000000000000000000000000000000000000000000", //bytes32 _whitelistedAccountsRoot - "USDC", //string memory _symbol - "0", //uint256 _riskProfileCode - "0", //uint256 _vaultConfiguration - "0", //uint256 _userDepositCapUT - "0", //uint256 _minimumDepositValueUT - "0", //uint256 _totalValueLockedLimitUT - ], - }; - const result = await deploy("opUSDC-Save", { - from: deployer, - contract: { - abi: artifact.abi, - bytecode: artifact.bytecode, - deployedBytecode: artifact.deployedBytecode, - }, - args: [registryProxyAddress], - log: true, - skipIfAlreadyDeployed: true, - libraries: { - "contracts/protocol/lib/StrategyManager.sol:StrategyManager": strategyManager.address, - }, - proxy: { - owner: admin, - upgradeIndex: 0, - proxyContract: "AdminUpgradeabilityProxy", - implementationName: "opWETH-Save_Implementation", - execute: { - init: proxyArgs, - onUpgrade: proxyArgs, - }, - }, - maxPriorityFeePerGas: BigNumber.from(feeData["maxPriorityFeePerGas"]), // Recommended maxPriorityFeePerGas - maxFeePerGas: BigNumber.from(feeData["maxFeePerGas"]), - }); - - if (CONTRACTS_VERIFY == "true") { - if (result.newlyDeployed) { - const vault = await deployments.get("opUSDC-Save"); - if (networkName === "tenderly") { - await tenderly.verify({ - name: "opUSDC-Save", - address: vault.address, - constructorArguments: [registryProxyAddress], - }); - } else if (!["31337"].includes(chainId)) { - await waitforme(20000); - - await run("verify:verify", { - name: "opUSDC-Save", - address: vault.address, - constructorArguments: [registryProxyAddress], - }); - } - } - } -}; -export default func; -func.tags = ["opUSDC-Save"]; -func.dependencies = ["Registry"]; diff --git a/deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts b/deploy_mainnet/039_approveandmapliquiditypooltoadapter.ts deleted file mode 100644 index e3bfacfb5..000000000 --- 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 a734bccf9..000000000 --- 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 02336aa09..000000000 --- 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 019b4ac01..000000000 --- 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 8e8aa6e50..000000000 --- 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/deploy_polygon/011_deploy_opUSDC-Earn.ts b/deploy_polygon/011_deploy_opUSDC-Earn.ts index f1a94e986..f0261f1b6 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 34983c9ee..54243c564 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/hardhat.config.ts b/hardhat.config.ts index 469bddf17..713ee6a81 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, diff --git a/helper-hardhat-config.ts b/helper-hardhat-config.ts index 139ff8043..834d53962 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]: 15876273, + [eEVMNetwork.mainnet]: 16195489, [eEVMNetwork.kovan]: 29962003, [eEVMNetwork.ropsten]: undefined, [eEVMNetwork.hardhat]: undefined, diff --git a/helpers/adapters/AdapterInterface.ts b/helpers/adapters/AdapterInterface.ts new file mode 100644 index 000000000..aaca0f887 --- /dev/null +++ b/helpers/adapters/AdapterInterface.ts @@ -0,0 +1,94 @@ +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 { + 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; + + getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _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 new file mode 100644 index 000000000..43d55d227 --- /dev/null +++ b/helpers/adapters/ethereum/AaveV1Adapter.ts @@ -0,0 +1,162 @@ +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"; +import { getAddress } from "ethers/lib/utils"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import { ReturnValue } from "../../type"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; +export class AaveV1Adapter implements AdapterInterface { + vaultHelperInstance; + aaveV1HelperInstance; + + 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), + ); + } + + getDepositPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const lendingPoolContract = new ethers.Contract(Aave.LendingPool.address, Aave.LendingPool.abi); + const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add( + this.aaveV1HelperInstance["depositETH_AaveV1(address,address,uint256)"]( + lendingPoolContract.address, + outputToken, + inputTokenAmount, + ), + ); + } else { + planner.add(lendingPoolInstance["deposit(address,uint256,uint16)"](inputToken, inputTokenAmount, "0")); + } + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + const lpTokenContract = new ethers.Contract(outputToken, Aave.ATokenAbi); + const lpTokenInstance = weirollContract.createContract(lpTokenContract); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add(this.aaveV1HelperInstance["withdrawETH_AaveV1(address,uint256)"](outputToken, outputTokenAmount)); + } else { + planner.add(lpTokenInstance["redeem(uint256)"](outputTokenAmount)); + } + return planner; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + 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 for ${_vaultUnderlyingToken}`); + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelper = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelper["getERC20Balance(address,address)"](outputToken, 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 new file mode 100644 index 000000000..0527ebcc6 --- /dev/null +++ b/helpers/adapters/ethereum/AaveV2Adapter.ts @@ -0,0 +1,289 @@ +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 { JsonRpcProvider } from "@ethersproject/providers"; + +const STK_AAVE = "0x4da27a545c0c5B758a6BA100e3a049001de870f5"; +export class Aavev2Adapter implements AdapterInterface { + optyFiOracleAddress; + vaultHelperInstance; + swapHelperInstance; + + constructor(_optyFiOracleAddress: string, vaultHelperContract: Contract, swapHelperContract: Contract) { + this.optyFiOracleAddress = _optyFiOracleAddress; + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + 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 lendingPoolContract = new ethers.Contract(AaveV2.LendingPool.address, AaveV2.LendingPool.abi); + const lendingPoolInstance = weirollContract.createContract(lendingPoolContract); + planner.add( + lendingPoolInstance["deposit(address,uint256,address,uint16)"]( + inputToken, + inputTokenAmount, + vaultInstance.address, + "0", + ), + ); + return planner; + } + + getWithdrawPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): weirollPlanner { + 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), + ); + 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 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, + vaultUnderlyingToken: string, + ): weirollPlanner { + const rewardAmount = planner.add( + 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 minimumOutputAmount = planner.add( + this.vaultHelperInstance["getMinimumExpectedTokenOutPrice(uint256,uint256)"](outputAmount, 100), + ); + 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( + 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; + } +} + +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 new file mode 100644 index 000000000..30aea7f7f --- /dev/null +++ b/helpers/adapters/ethereum/CompoundAdapter.ts @@ -0,0 +1,322 @@ +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"; +import EthereumTokens from "@optyfi/defi-legos/ethereum/tokens/index"; +import UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; +import { ReturnValue } from "../../type"; +import { ICompound__factory } from "../../../typechain"; +import { getAddress, parseEther } from "ethers/lib/utils"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; + +export class CompoundAdapter implements AdapterInterface { + vaultHelperInstance; + optyFiOracleAddress; + compoundHelperInstance; + + 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( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): weirollPlanner { + const poolInstance = weirollContract.createContract(new ethers.Contract(pool, ICompound__factory.abi)); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add(this.compoundHelperInstance["depositETH_Compound(address,uint256)"](pool, inputTokenAmount)); + } else { + planner.add(poolInstance["mint(uint256)"](inputTokenAmount)); + } + 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, ICompound__factory.abi)); + if (getAddress(inputToken) === getAddress(EthereumTokens.WRAPPED_TOKENS.WETH)) { + planner.add(this.compoundHelperInstance["withdrawETH_Compound(address,uint256)"](pool, outputTokenAmount)); + } else { + planner.add(poolInstance["redeem(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 + const amountUT = planner.add( + this.compoundHelperInstance["calculateAmountInToken_Compound(address,uint256)"]( + pool, + outputTokenAmount, + ).staticcall(), + ); + return amountUT as ReturnValue; + } + + getAmountInOutputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + inputTokenAmount: ReturnValue, + ): ReturnValue { + const amountLP = planner.add( + this.compoundHelperInstance["calculateAmountInLPToken_Compound(address,uint256)"]( + pool, + inputTokenAmount, + ).staticcall(), + ); + 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 { + 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), + ); + return planner; + } + + getHarvestRewardsPlan( + planner: weirollPlanner, + vaultInstance: Contract, + vaultUnderlyingToken: string, + ): weirollPlanner { + const uniswapV2RouterContract = new ethers.Contract(UniswapV2.router02.address, UniswapV2.router02.abi); + const uniswapV2RouterInstance = weirollContract.createContract(uniswapV2RouterContract); + const rewardAmount = planner.add( + this.vaultHelperInstance["getERC20Balance(address,address)"]( + EthereumTokens.REWARD_TOKENS.COMP, + vaultInstance.address, + ).staticcall(), + ); + const outputAmount = planner.add( + this.vaultHelperInstance["getTokenOutPrice_OptyFiOracle(address,address,address,uint256)"]( + this.optyFiOracleAddress, + EthereumTokens.REWARD_TOKENS.COMP, + vaultUnderlyingToken, + rewardAmount, + ).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, + minimumOutputAmount, + [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, + minimumOutputAmount, + [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, + minimumOutputAmount, + [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, + minimumOutputAmount, + [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, + minimumOutputAmount, + [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}`); + } + } + return planner; + } + + async getOutputTokenBalance( + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + _isSwap: boolean, + provider: JsonRpcProvider, + ): Promise { + const vaultHelper = new ethers.Contract( + this.vaultHelperInstance.address, + this.vaultHelperInstance.interface, + provider, + ); + return await vaultHelper["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 + 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 = [ + { + 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/adapters/ethereum/ConvexAdapter.ts b/helpers/adapters/ethereum/ConvexAdapter.ts new file mode 100644 index 000000000..be464f804 --- /dev/null +++ b/helpers/adapters/ethereum/ConvexAdapter.ts @@ -0,0 +1,167 @@ +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; + readonly boosterDepositPool = "0xF403C135812408BFbE8713b5A23a04b3D48AAE31"; + + constructor(vaultHelperContract: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + } + + 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].pid, 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].pid, 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 { + throw new Error("not implemented"); + } + + getHarvestRewardsPlan( + _planner: weirollPlanner, + _vaultInstance: Contract, + _vaultUnderlyingToken: string, + ): weirollPlanner { + throw new Error(`not implemented for ${_vaultUnderlyingToken}`); + } + + 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, + }, + "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA": { + pid: 32, + }, + "0xabB54222c2b77158CC975a2b715a3d703c256F05": { + pid: 40, + }, + "0x9518c9063eB0262D791f38d8d6Eb0aca33c63ed0": { + pid: 25, + }, +}; diff --git a/helpers/adapters/ethereum/ConvexStakingAdapter.ts b/helpers/adapters/ethereum/ConvexStakingAdapter.ts new file mode 100644 index 000000000..94d438071 --- /dev/null +++ b/helpers/adapters/ethereum/ConvexStakingAdapter.ts @@ -0,0 +1,339 @@ +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 ConvexStakingAdapter 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 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; + } + + 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 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; + } + + 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; + } + 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( + 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 new file mode 100644 index 000000000..ed2c13519 --- /dev/null +++ b/helpers/adapters/ethereum/CurveAdapter.ts @@ -0,0 +1,615 @@ +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"; +import { AdapterInterface } from "../AdapterInterface"; +import { JsonRpcProvider } from "@ethersproject/providers"; + +export class CurveAdapter implements AdapterInterface { + vaultHelperInstance; + curveHelperInstance; + + constructor(vaultHelperContract: Contract, curveHelper: Contract) { + this.vaultHelperInstance = weirollContract.createContract( + new ethers.Contract(vaultHelperContract.address, vaultHelperContract.interface), + ); + 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[getAddress(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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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).staticcall(), + ); + 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( + 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(), + ); + 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; + } + + getAmountInInputTokenPlan( + planner: weirollPlanner, + vaultInstance: Contract, + inputToken: string, + pool: string, + outputToken: string, + isSwap: boolean, + outputTokenAmount: ReturnValue, + ): ReturnValue { + const tokenIndex = this.getTokenIndex(inputToken, poolRegistry[pool].underlyingTokens); + const amountUT = planner.add( + this.curveHelperInstance["getCalc_withdraw_one_coin(address,uint256,int128)"]( + pool, + 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 for ${_vaultUnderlyingToken}`); + } + + 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); + 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( + 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.entries()) { + 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", + ], + }, + "0xd632f22692FaC7611d2AA1C0D552930D43CAEd3B": { + underlyingTokens: ["0x853d955aCEf822Db058eb8505911ED77F175b99e", "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"], + }, + "0x5a6A4D54456819380173272A5E8E9B9904BdF41B": { + underlyingTokens: ["0x99D8a9C45b2ecA8864373A26D1459e3Dff1e17F3", "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"], + }, + "0xDC24316b9AE028F1497c275EB9192a3Ea0f67022": { + underlyingTokens: ["0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84"], + }, +}; diff --git a/helpers/adapters/ethereum/CurveGaugeAdapter.ts b/helpers/adapters/ethereum/CurveGaugeAdapter.ts new file mode 100644 index 000000000..1bb529997 --- /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).staticcall(), + ); + 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/constants/essential-contracts-name.ts b/helpers/constants/essential-contracts-name.ts index e4784479d..3d586cc41 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/data/adapter-with-strategies.ts b/helpers/data/adapter-with-strategies.ts index 740f314fc..4df01a2b1 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,186 +281,212 @@ 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", - isSwap: false, - outputTokenSymbol: "FRAX3CRV-f", - adapterName: "CurveMetapoolSwapAdapter", - protocol: "Curve", - }, { - contract: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", - outputToken: "0xbE0F6478E0E4894CFb14f32855603A083A57c7dA", + contract: "0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8", + outputToken: "0x689440f2Ff927E1f24c72F1087E1FAF471eCe1c8", isSwap: false, - outputTokenSymbol: "cvxFRAX3CRV-f", + outputTokenSymbol: "cvx3Crv-gauge", adapterName: "ConvexFinanceAdapter", 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", - 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", + name: "3CRV LP Staking on Convex", 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.", + "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-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": { @@ -499,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: [ { @@ -545,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", + contract: "0x0A760466E1B4621579a82a39CB56Dda2F4E70f03", + outputToken: "0x0A760466E1B4621579a82a39CB56Dda2F4E70f03", 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", - isSwap: false, - outputTokenSymbol: "cvxsteCRV", - adapterName: "ConvexFinanceAdapter", + outputTokenSymbol: "cvxsteCRV-stake", + adapterName: "ConvexStakingAdapter", protocol: "Convex", }, ], @@ -613,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": { @@ -2660,7 +2694,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: { @@ -3009,3 +3043,314 @@ 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, + }, + "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, + 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, + }, + "0x9be5e54cb32b066ac47ab45dd0f04ee8aa5835e389478c5eb00050d23a10578e": { + // weth-DEPOSIT-CurveSwapPool-steCRV-DEPOSIT-Convex-cvxsteCRV-DEPOSIT-Convex-stake + oraValueUTIndex: 1, + oraValueLPIndex: 2, + 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 new file mode 100644 index 000000000..bfc07349a --- /dev/null +++ b/helpers/strategy-manager.ts @@ -0,0 +1,419 @@ +import { Planner as weirollPlanner, Contract as weirollContract } from "@weiroll/weiroll.js"; +import { ethers } from "@weiroll/weiroll.js/node_modules/ethers"; +import { BigNumber, Contract } from "ethers"; +import { JsonRpcProvider } from "@ethersproject/providers"; +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 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, + curveHelperContract: Contract, + ) { + this.vaultHelperContract = weirollContract.createContract( + new ethers.Contract(vaultHelperInstance.address, vaultHelperInstance.interface), + ); + 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 + 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; // 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 + 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 { + const planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + new ethers.Contract(vaultInstance.address, vaultInstance.interface), + ); + for (const [index, strategyStep] of strategySteps.entries()) { + const adapterObj = this.liquidityPoolToAdapter[strategyStep.pool]; + + if (index > 0) { + const inputTokenAmount = ( + planner.add( + this.vaultHelperContract["getERC20Balance(address,address)"]( + strategySteps[index - 1].outputToken, + vaultContract.address, + ).staticcall(), + ) + ); + adapterObj.getDepositPlan( + planner, + vaultInstance, + strategySteps[index - 1].outputToken, + strategyStep.pool, + strategyStep.outputToken, + strategyStep.isSwap, + inputTokenAmount, + ); + } else { + const amountUT = planner.add(vaultContract["getCacheValueUT()"]().staticcall()); + adapterObj.getDepositPlan( + planner, + vaultInstance, + underlyingToken, + strategyStep.pool, + strategyStep.outputToken, + strategyStep.isSwap, + amountUT, + ); + } + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getWithdrawPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { + const planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + 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].pool]; + if (iteratorIndex === strategySteps.length - 1) { + // last step + const amountLP = planner.add(vaultContract.getCacheAmountLP().staticcall()); + adapterObj.getWithdrawPlan( + planner, + vaultInstance, + iteratorIndex > 0 ? strategySteps[iteratorIndex - 1].outputToken : underlyingToken, + strategySteps[iteratorIndex].pool, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP as ReturnValue, + ); + } else { + const amountLP = ( + planner.add( + this.vaultHelperContract["getERC20Balance(address,address)"]( + strategySteps[iteratorIndex].outputToken, + vaultContract.address, + ).staticcall(), + ) + ); + adapterObj.getWithdrawPlan( + planner, + vaultInstance, + iteratorIndex > 0 ? strategySteps[iteratorIndex - 1].outputToken : underlyingToken, + strategySteps[iteratorIndex].pool, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP, + ); + } + } + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getOraValueUTPlan(underlyingToken: string, strategySteps: StrategyStepType[], vaultInstance: Contract): WeirollPlan { + const planner = new weirollPlanner(); + const amountLP = ( + planner.add( + this.vaultHelperContract["getERC20Balance(address,address)"]( + strategySteps[strategySteps.length - 1].outputToken, + vaultInstance.address, + ).staticcall(), + ) + ); + let amountUT; + for (const [index, _strategyStep] of strategySteps.entries()) { + const iteratorIndex = strategySteps.length - index - 1; + const adapterObj = this.liquidityPoolToAdapter[strategySteps[iteratorIndex].pool]; + let inputToken = underlyingToken; + if (iteratorIndex !== 0) { + inputToken = strategySteps[iteratorIndex - 1].outputToken; + } + if (iteratorIndex === strategySteps.length - 1) { + amountUT = ( + adapterObj.getAmountInInputTokenPlan( + planner, + vaultInstance, + inputToken, + strategySteps[iteratorIndex].pool, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountLP, + ) + ); + } else { + amountUT = ( + adapterObj.getAmountInInputTokenPlan( + planner, + vaultInstance, + inputToken, + strategySteps[iteratorIndex].pool, + strategySteps[iteratorIndex].outputToken, + strategySteps[iteratorIndex].isSwap, + amountUT as ReturnValue, + ) + ); + } + } + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountUT).staticcall()); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getOraSomeValueLPPlan( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + ): WeirollPlan { + const planner = new weirollPlanner(); + const vaultContract = weirollContract.createContract( + new ethers.Contract(vaultInstance.address, vaultInstance.interface), + ); + const valueUT = planner.add(vaultContract.getCacheValueUT().staticcall()); + let amountLP; + 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 = adapterObj.getAmountInOutputTokenPlan( + planner, + vaultInstance, + inputToken, + strategyStep.pool, + strategyStep.outputToken, + strategyStep.isSwap, + valueUT, + ); + } else { + amountLP = adapterObj.getAmountInOutputTokenPlan( + planner, + vaultInstance, + inputToken, + strategyStep.pool, + strategyStep.outputToken, + strategyStep.isSwap, + amountLP as ReturnValue, + ); + } + } + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getLastStrategyStepBalancePlan( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + ): WeirollPlan { + const planner = new weirollPlanner(); + const amountLP = planner.add( + this.vaultHelperContract["getERC20Balance(address,address)"]( + strategySteps[strategySteps.length - 1].outputToken, + vaultInstance.address, + ).staticcall(), + ); + planner.add(this.vaultHelperContract["pureFunctionUint256(uint256)"](amountLP).staticcall()); + const { commands, state } = planner.plan(); + return { commands, state, outputIndex: 0 }; + } + + getClaimRewardsPlan( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + ): WeirollPlan { + 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; + } + } + + getHarvestRewardsPlan( + underlyingToken: string, + strategySteps: StrategyStepType[], + vaultInstance: Contract, + ): WeirollPlan { + 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; + } + } + + 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; + } + + 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/helpers/type.d.ts b/helpers/type.d.ts index cdaf838b5..0607cfda3 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,27 @@ export type PoolRate = { pool: string; rate: number; }; + +export type WeirollPlan = { + commands: string[]; + state: string[]; + outputIndex: number; +}; + +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/helpers/utils.ts b/helpers/utils.ts index 55d138fa7..9db96dc84 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, @@ -30,6 +32,13 @@ import { TestStrategyManager, UniswapV2ExchangeAdapter, CurveExchangeAdapter, + StrategyRegistry, + ERC20__factory, + VaultHelper, + AaveV1Helper, + CompoundHelper, + SwapHelper, + CurveHelper, } from "../typechain"; import { VaultV3 } from "./types/vaultv3"; import { expect } from "chai"; @@ -40,6 +49,18 @@ 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 { 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), + 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 export function getSoliditySHA3Hash(argTypes: string[], args: any[]): string { @@ -93,12 +114,19 @@ declare module "mocha" { curveSwapPoolAdapter: CurveSwapPoolAdapter; curveSwapEthGateway: CurveSwapETHGateway; strategyProvider: StrategyProvider; + strategyRegistry: StrategyRegistry; + strategyManager: StrategyManager; strategyProviderV1: StrategyProviderV1; riskManagerProxy: RiskManagerProxyV1; riskManager: RiskManager; riskManagerV2: RiskManagerV2; vault: Vault; vaultProxy: InitializableImmutableAdminUpgradeabilityProxy; + vaultHelper: VaultHelper; + swapHelper: SwapHelper; + aaveV1Helper: AaveV1Helper; + compoundHelper: CompoundHelper; + curveHelper: CurveHelper; opUSDCearn: Vault; opUSDCearnProxy: InitializableImmutableAdminUpgradeabilityProxy; opWETHearn: Vault; @@ -338,6 +366,306 @@ 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 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.getValueInOutputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + vaultDepositUT, + 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); + let outputTokenDecimals; + if (noDecimalPools.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); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 14).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.getValueInOutputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + expectedStratWithdrawUT, + provider, + ); + const expectedVaultBalanceLP = vaultBalanceBeforeLP.sub(expectedAmountWithdrawLP); + const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + let outputTokenDecimals; + if (noDecimalPools.includes(getAddress(steps[steps.length - 1].pool))) { + outputTokenDecimals = 18; + } else { + outputTokenDecimals = await outputTokenInstance.decimals(); + } + expect(vaultBalanceAfterLP).to.closeTo( + expectedVaultBalanceLP, + parseUnits("9", Math.floor((outputTokenDecimals * 3) / 4)).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", Math.ceil(((await underlyingTokenInstance.decimals()) * 4) / 5)).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.getValueInInputToken( + underlyingTokenInstance.address, + steps, + vaultInstance, + vaultWithdrawLP, + 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", Math.ceil(((await underlyingTokenInstance.decimals()) * 3) / 4)).toNumber(), + ); + const outputTokenInstance = new ethers.Contract(steps[steps.length - 1].outputToken, ERC20__factory.abi, provider); + let outputTokenDecimals; + if (noDecimalPools.includes(getAddress(steps[steps.length - 1].pool))) { + outputTokenDecimals = 18; + } else { + outputTokenDecimals = await outputTokenInstance.decimals(); + } + expect(vaultBalanceAfterLP).to.closeTo( + expectedVaultBalanceAfterLP, + parseUnits("9", outputTokenDecimals / 2).toNumber(), + ); + expect(vaultTotalSupplyAfterVT).to.eq(expectedTotalSupplyVT); + expect(ppsAfter).to.closeTo(expectedPPS, parseUnits("9", 14).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 7e3e91e97..ffce43cc7 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": "github:weiroll/weiroll.js#main", "bignumber.js": "^9.0.2", "chai": "^4.3.4", "commitizen": "^4.2.4", @@ -58,6 +59,7 @@ "ethers": "^5.4.7", "fs-extra": "^10.0.0", "hardhat": "^2.11.2", + "hardhat-contract-sizer": "^2.6.1", "hardhat-deploy": "^0.9.24", "hardhat-docgen": "^1.1.2", "hardhat-gas-reporter": "^1.0.4", @@ -147,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/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts b/scripts/harvest/002_swap_LDO_CRV_CVX_to_WETH.ts index a246ffc0a..c1d72b3d5 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 94b246641..d1dd95aa4 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 c56b1f97e..3ec03c812 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 c4c28a205..6a8c69a46 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 925b1471e..5032a6bb4 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 07995243c..c337f5e76 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 { parseEther, parseUnits } from "ethers/lib/utils"; +import { VaultV6, VaultV6__factory } from "../../helpers/types/vaultv6"; const cryptoPoolGaugeABI = [ { @@ -52,7 +51,7 @@ const cryptoPoolGaugeABI = [ }, ]; -const comptrollerABI = [ +export const comptrollerABI = [ { constant: true, inputs: [{ internalType: "address", name: "", type: "address" }], @@ -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") @@ -205,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; @@ -219,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/008_vault_repurpose_harvest.ts b/scripts/harvest/008_vault_repurpose_harvest.ts index 5be916a98..d7aa4330e 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 deleted file mode 100644 index b008ccafc..000000000 --- a/scripts/harvest/009_mim_harvest.ts +++ /dev/null @@ -1,408 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unused-vars */ -import Convex from "@optyfi/defi-legos/ethereum/convex"; -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, - IConvexStake, - ICurveSwap, - 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"; - -const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; - -async function main() { - const opUSDCEarn = ( - await ethers.getContractAt(Vault__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 opWETHEarn = ( - await ethers.getContractAt(Vault__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 opWETHInvst = ( - await ethers.getContractAt(Vault__factory.abi, (await deployments.get("opWETH-Invst_Proxy")).address) - ); - const sushiswapRouter = ( - await ethers.getContractAt(IUniswapV2Router02__factory.abi, EthereumSushiswap.SushiswapRouter.address) - ); - - 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", - }, - ]; - - const cvxMIM3lp3GaugeInstance = await ethers.getContractAt(cvxABI, Convex.pools.mim.stakingPool); - - 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); - - const cvxUnclaimedForopUSDCEarn = getCVXUnclaimed( - crvUnclaimedForopUSDCEarn, - await cvxInstance.totalSupply(), - BigNumber.from("100000000000000000000000"), - BigNumber.from("1000"), - BigNumber.from("100000000000000000000000000"), - ); - const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); - - const ldoUnclaimedForopWETHEarn = parseEther("7.022800921361593268"); - 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.63", "6"); // crv - usdc - const oneCVXToUSDC = parseUnits("3.9", "6"); - const oneLDOToWETH = parseEther("0.00091"); - const oneSUSHIToUSDC = parseUnits("1", "6"); - const oneSUSHIToWETH = parseEther("0.001"); // sushi-dai-weth - - const expectedMinUSDCFromCRVForopUSDCEarn = BigNumber.from(crvBalanceForopUSDCEarn.add(crvUnclaimedForopUSDCEarn)) - .mul(oneCRVToUSDC) - .div(parseEther("1")); - - const expectedMinWETHFromLDOForopWETHEarn = BigNumber.from(ldoBalanceForopWETHEarn.add(ldoUnclaimedForopWETHEarn)) - .mul(oneLDOToWETH) - .div(parseEther("1")); - - const expectedMInUSDCFromCVXForopUSDCEarn = BigNumber.from(cvxBalanceForopUSDCEarn.add(cvxUnclaimedForopUSDCEarn)) - .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")); - - const timestamp = (await ethers.provider.getBlock("latest")).timestamp; - - 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[] = [ - // // 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 - // { - // 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: 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.WRAPPED_TOKENS.WETH, - EthereumTokens.REWARD_TOKENS.SUSHI, - EthereumTokens.PLAIN_TOKENS.USDC, - ], - opUSD3Earn.address, - BigNumber.from(timestamp).add(600), - ]), - ], - ), - ], - ]), - }, - // ---- - // // harvest CVX for opUSDCEarn - // { - // to: opUSDCEarn.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, - // ]), - // }, - // ---- - // 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, - ], - "17000000000000000000", - ]), - ], - ), - ], - ]), - }, - // ---- - // // 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, - // value: "0", - // data: opUSDCInvst.interface.encodeFunctionData("harvest", [ - // sushiInstance.address, - // sushiswapRouter.address, - // false, - // expectedMinUSDCFromSUSHIForopUSDCInvst, - // BigNumber.from(timestamp).add("600"), - // [sushiInstance.address, 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, - // 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"), - // }, - ]; - - 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 safeTransaction = await safeSdk.createTransaction(transactions); - - console.log("safeTransaction ", safeTransaction.data); - 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/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts new file mode 100644 index 000000000..fe4c190b7 --- /dev/null +++ b/scripts/harvest/009_usdc-mim-sushi_weth-ldo-sushi_harvest.ts @@ -0,0 +1,461 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +import Convex from "@optyfi/defi-legos/ethereum/convex"; +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, IUniswapV2Router02, IUniswapV2Router02__factory } from "../../typechain"; +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"; + +const uniswapV3Router = "0xE592427A0AEce92De3Edee1F18E0157C05861564"; + +async function main() { + const opUSDCEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Earn_Proxy")).address) + ); + const opWETHEarn = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opWETH-Earn_Proxy")).address) + ); + const opUSDCInvst = ( + await ethers.getContractAt(VaultV6__factory.abi, (await deployments.get("opUSDC-Invst_Proxy")).address) + ); + 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 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) + ); + + 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", + }, + ]; + + 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); + 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 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, + await cvxInstance.totalSupply(), + BigNumber.from("100000000000000000000000"), + BigNumber.from("1000"), + BigNumber.from("100000000000000000000000000"), + ); + const cvxBalanceForopUSDCEarn = await cvxInstance.balanceOf(opUSDCEarn.address); + + const ldoUnclaimedForopWETHEarn = parseEther("21.460000000000000000"); + const ldoBalanceForopWETHEarn = await ldoInstance.balanceOf(opWETHEarn.address); + + const sushiBalanceForopUSDCInvst = await sushiInstance.balanceOf(opUSDCInvst.address); + const sushiBalanceForopWETHInvst = await sushiInstance.balanceOf(opWETHInvst.address); + + 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.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.7", "18"); + const oneCVXToUSD3 = parseUnits("3.4", "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) + .div(parseEther("1")); + + const expectedMinWETHFromLDOForopWETHEarn = BigNumber.from(ldoBalanceForopWETHEarn.add(ldoUnclaimedForopWETHEarn)) + .mul(oneLDOToWETH) + .div(parseEther("1")); + + const expectedMInUSDCFromCVXForopUSDCEarn = BigNumber.from(cvxBalanceForopUSDCEarn.add(cvxUnclaimedForopUSDCEarn)) + .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")); + + const timestamp = (await ethers.provider.getBlock("latest")).timestamp; + + 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 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 + { + to: cvxMIM3lp3GaugeInstance.address, + value: "0", + data: cvxMIM3lp3GaugeInstance.interface.encodeFunctionData("getReward", [opUSDCEarn.address, true]), + }, + // claim LDO,CRV,CVX for opWETHEarn + { + to: cvxStEthGaugeInstance.address, + value: "0", + data: cvxStEthGaugeInstance.interface.encodeFunctionData("getReward", [opWETHEarn.address, true]), + }, + // claim CRV, CVX for opUSD3Earn + { + to: cvxMIM3lp3GaugeInstance.address, + value: "0", + 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, + 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, + ]), + }, + // harvest CVX for opUSDCEarn + { + to: opUSDCEarn.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, + ]), + }, + // 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 CRV for opWBTCEarn + // { + // to: opWBTCEarn.address, + // value: "0", + // data: opWBTCEarn.interface.encodeFunctionData("harvest", [ + // crvInstance.address, + // uniswapV3Router, // "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D", + // true, + // "0", + // BigNumber.from(timestamp).add("600"), + // [/*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, + // 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 opWETHEarn + { + to: opWETHEarn.address, + value: "0", + data: opWETHEarn.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"), + // }, + // vaultDepositAllToStrategy for opWBTCEarn + { + to: opUSD3Earn.address, + value: "0", + data: opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy"), + }, + ]; + + const safeOwner = ethers.provider.getSigner(0); + const safeAddress = "0x9DD0E8A985315785473f7EB81bc4e28838e13D96"; + const ethAdapter = new EthersAdapter({ + ethers, + signer: safeOwner, + }); + const safeSdk: Safe = await Safe.create({ ethAdapter, safeAddress }); + const safeTransaction = await safeSdk.createTransaction(transactions); + + console.log("safeTransaction ", safeTransaction.data); + const tx = await safeSdk.executeTransaction(safeTransaction); + const txR = await tx.transactionResponse?.wait(1); + console.log(txR); +} + +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_bDAMM_wbtc.ts b/scripts/harvest/010_harvest_bDAMM_wbtc.ts deleted file mode 100644 index 87019f92d..000000000 --- a/scripts/harvest/010_harvest_bDAMM_wbtc.ts +++ /dev/null @@ -1,155 +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, - IUniswapV2Router02, - IUniswapV2Router02__factory, - Vault, - Vault__factory, -} from "../../typechain"; -import { parseUnits } from "ethers/lib/utils"; -import { BigNumber } from "ethers"; - -const redemptionABI = [ - { - inputs: [ - { - internalType: "uint256", - name: "amount", - type: "uint256", - }, - { - internalType: "uint256", - name: "redemptionFee", - type: "uint256", - }, - ], - name: "redeem", - outputs: [], - stateMutability: "nonpayable", - type: "function", - }, -]; - -async function main() { - 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 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: 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("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, - ]), - }, - { - 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 000000000..cd612e1e3 --- /dev/null +++ b/scripts/harvest/010_harvest_wbtc-damm_3crv-mim.ts @@ -0,0 +1,422 @@ +/* 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 { formatEther, 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"), + ); + + console.log(formatEther(crvUnclaimedForopUSD3Earn)); + console.log(formatEther(cvxUnclaimedForopUSD3Earn)); + + 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) + .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 crvWSBTCGaugeInstance = await ethers.getContractAt(curveGuageABI, "0x6D787113F23bED1D5e1530402B3f364D0A6e5Af3"); + 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, + ], + "11000000000000000000", + ]), + ], + ), + ethers.utils.defaultAbiCoder.encode( + ["address", "bytes"], + [opUSD3Earn.address, opUSD3Earn.interface.encodeFunctionData("vaultDepositAllToStrategy")], + ), + ], + ]), + }, + // { + // 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); + + 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", + }, +]; + +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", + }, +]; diff --git a/tasks/actions/print-strategy-hash.ts b/tasks/actions/print-strategy-hash.ts index 534e8687e..84a098fb3 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/2_vault.spec.ts b/test/test-opty/2_vault.spec.ts index 0ba71ce92..414465f72 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 UniswapV2 from "@optyfi/defi-legos/ethereum/uniswapV2/index"; import { assertVaultConfiguration, getAccountsMerkleProof, @@ -23,104 +26,86 @@ 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"); -describe(`::${fork}-Vault-rev4`, function () { +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 => ({ + 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-rev7`, function () { before(async function () { await deployments.fixture(); this.testVaultArtifact = await artifacts.readArtifact("TestVault"); @@ -132,10 +117,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 +129,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,6 +182,151 @@ 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 () { @@ -186,6 +349,10 @@ 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(`${fork}-#setValueControlParams(uint256,uint256,uint256)`, function () { @@ -412,8 +579,6 @@ 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); }); }); @@ -441,8 +606,6 @@ 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); }); }); @@ -456,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("5"); + ).to.be.revertedWith(RISK_PROFILE_EXISTS); }); it("setRiskProfileCode() call by governance", async function () { @@ -477,49 +640,35 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#adminCall(bytes[])`, function () { + describe(`${fork}-#adminCall(bytes32[],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 () { 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(`${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); - 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(VAULT_PAUSED); assertVaultConfiguration( await this.opUSDCearn.vaultConfiguration(), BigNumber.from("1"), @@ -535,59 +684,33 @@ describe(`::${fork}-Vault-rev4`, function () { ); }); - it("rebalance(), 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")); - 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]), - ]); }); }); @@ -603,7 +726,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${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], @@ -628,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, "8"]); + ).to.have.members([false, EOA_NOT_WHITELISTED]); }); it("userDepositPermitted() return false,MINIMUM_USER_DEPOSIT_VALUE_UT", async function () { @@ -640,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, "10"]); + ).to.have.members([false, EOA_NOT_WHITELISTED]); }); it("userDepositPermitted() return false,TOTAL_VALUE_LOCKED_LIMIT_UT", async function () { @@ -652,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, "11"]); + ).to.have.members([false, TOTAL_VALUE_LOCKED_LIMIT_UT]); }); it("userDepositPermitted() return false,USER_DEPOSIT_CAP_UT", async function () { @@ -664,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, "12"]); + ).to.have.members([false, USER_DEPOSIT_CAP_UT]); }); it('call userDepositPermitted() from EOA return true,""', async function () { @@ -698,7 +821,7 @@ describe(`::${fork}-Vault-rev4`, function () { describe(`${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, VAULT_PAUSED]); }); it("vaultDepositPermitted() return false,VAULT_EMERGENCY_SHUTDOWN", async function () { @@ -718,7 +841,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, VAULT_EMERGENCY_SHUTDOWN]); }); it('vaultDepositPermitted() return true,""', async function () { @@ -745,7 +868,7 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${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], @@ -898,7 +1021,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(VAULT_PAUSED); }); it("first userDepositVault(), mint same shares as deposit", async function () { @@ -981,7 +1104,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proofs), - ).to.revertedWith("8"); + ).to.revertedWith(EOA_NOT_WHITELISTED); }); it("fail userDepositVault() good user alice calls on bob's proof,EOA_NOT_WHITELISTED", async function () { @@ -1012,7 +1135,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.alice) .userDepositVault(this.signers.alice.address, _depositAmountUSDC, [], _bobProofs), - ).to.revertedWith("8"); + ).to.revertedWith(EOA_NOT_WHITELISTED); }); it("userDepositVault, deposit fees", async function () { @@ -1126,7 +1249,7 @@ describe(`::${fork}-Vault-rev4`, function () { this.opUSDCearn .connect(this.signers.eve) .userDepositVault(this.signers.eve.address, _depositAmountUSDC, [], _proof), - ).to.revertedWith("13"); + ).to.revertedWith(VAULT_EMERGENCY_SHUTDOWN); }); }); @@ -1202,7 +1325,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("25"); + ).to.be.revertedWith(ZERO_ADDRESS_NOT_VALID); }); it("fail, expired timestamp", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1228,7 +1351,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("26"); + ).to.be.revertedWith(INVALID_EXPIRATION); }); it("fail, invalid signature(wrong signer)", async function () { @@ -1255,7 +1378,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("27"); + ).to.be.revertedWith(INVALID_SIGNATURE); }); it("fail, invalid signature(reuse nonce)", async function () { const vaultTokenBalanceAlice = await this.opUSDCearn.balanceOf(this.signers.alice.address); @@ -1281,7 +1404,7 @@ describe(`::${fork}-Vault-rev4`, function () { r, s, ), - ).to.be.revertedWith("27"); + ).to.be.revertedWith(INVALID_SIGNATURE); }); }); @@ -1291,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"); @@ -1314,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 () { @@ -1336,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"), @@ -1351,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 () { @@ -1375,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); @@ -1398,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"), @@ -1439,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); }); }); @@ -1457,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 () { @@ -1493,37 +1607,26 @@ 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, - ); - }); - }); - - describe(`${fork}-#claimRewardToken(address)`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[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); }); }); 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( @@ -1535,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) @@ -1548,7 +1658,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( @@ -1560,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( @@ -1573,53 +1683,37 @@ describe(`::${fork}-Vault-rev4`, function () { }); }); - describe(`${fork}-#harvest(address)`, function () { - const _pool = testStrategy[fork][strategyKeys[0]].steps[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], @@ -1627,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 = testStrategy[fork][strategyKeys[0]].steps[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); @@ -1679,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, + ); }); }); diff --git a/test/test-opty/3_vault_strategies.spec.ts b/test/test-opty/3_vault_strategies.spec.ts index d4fd2cb4d..aa03c93d5 100644 --- a/test/test-opty/3_vault_strategies.spec.ts +++ b/test/test-opty/3_vault_strategies.spec.ts @@ -3,35 +3,45 @@ 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 { formatEther, formatUnits, getAddress } from "ethers/lib/utils"; -import { Signers, to_10powNumber_BN } from "../../helpers/utils"; -import { MultiChainVaults, StrategiesByTokenByChain } from "../../helpers/data/adapter-with-strategies"; +import { BigNumber, Contract } from "ethers"; +import { formatEther, formatUnits, getAddress, parseUnits } from "ethers/lib/utils"; +import Compound from "@optyfi/defi-legos/ethereum/compound/index"; +import { + assertPostUserDepositState, + assertPostUserWithdrawState, + assertPostVaultDepositState, + assertPostVaultWithdrawState, + Signers, + to_10powNumber_BN, +} from "../../helpers/utils"; +import { + MultiChainVaults, + StrategiesByTokenByChain, + strategyHashReadIndexes, +} from "../../helpers/data/adapter-with-strategies"; 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, + StrategyRegistry__factory, + VaultHelper, + AaveV1Helper__factory, + CompoundHelper__factory, + SwapHelper__factory, + VaultHelper__factory, + CurveHelper__factory, } from "../../typechain"; import { generateTokenHashV2, generateStrategyHashV2 } from "../../helpers/helpers"; import { StrategyStepType } from "../../helpers/type"; -import { setTokenBalanceInStorage, getLastStrategyStepBalanceLP } 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"; chai.use(solidity); @@ -39,10 +49,55 @@ 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`, () => { +describe(`${fork}-Vault-rev7`, () => { before(async function () { - await deployments.fixture(); - const registryProxyAddress = (await deployments.get("RegistryProxy")).address; + this.strategyRegistry = await ethers.getContractAt( + StrategyRegistry__factory.abi, + ( + await deployments.get("StrategyRegistry") + ).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.registry = ( + await ethers.getContractAt(Registry__factory.abi, (await deployments.get("RegistryProxy")).address) + ); + this.vaults = {}; + this.tokens = {}; this.signers = {} as Signers; const signers: SignerWithAddress[] = await ethers.getSigners(); this.signers.deployer = signers[0]; @@ -50,25 +105,10 @@ describe(`${fork}-Vault-rev4`, () => { 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"); - 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.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 +132,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, @@ -106,31 +146,13 @@ describe(`${fork}-Vault-rev4`, () => { 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(`${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,22 +171,62 @@ 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), - ) - ); + console.log(`${token}-${strategy} `, strategyHash); + 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], + ), + outputIndex: strategyHashReadIndexes[strategyHash].oraValueUTIndex, + }, + oraValueLPPlan: { + ...this.strategyManager.getOraSomeValueLPPlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + outputIndex: strategyHashReadIndexes[strategyHash].oraValueLPIndex, + }, + lastStepBalanceLPPlan: { + ...this.strategyManager.getLastStrategyStepBalancePlan( + StrategiesByTokenByChain[fork][riskProfile][token][strategy].token, + steps, + this.vaults[riskProfile][token], + ), + outputIndex: strategyHashReadIndexes[strategyHash].lastStepLPBalanceIndex, + }, + 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)) == - (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, @@ -176,55 +238,87 @@ 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(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"); - } - } + // 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"); + // } + // } + } + 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)); + } else { + _userDepositInDecimals = _userDepositInDecimals.mul(3); } + 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]; if (fork == eEVMNetwork.mainnet) { - await deposit(signers, this.vaults[riskProfile][token], this.tokens[token], this.tokens["WETH"]); + await userDeposit( + 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 userDeposit( + 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 () { @@ -278,14 +372,56 @@ 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].rebalance(); - await tx.wait(1); - expect(await this.vaults[riskProfile][token].getInvestStrategySteps()).to.deep.eq( - steps.map(item => Object.values(item)), + const vaultAddStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.strategyOperator) + .addStrategy(strategyHash); + await vaultAddStrategyTx.wait(1); + + 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, vaultDepositUT); + await vaultDepositTx.wait(1); + + await assertPostVaultDepositState( + this.signers.alice, + this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + vaultDepositUT, + vaultTotalSupplyBeforeVT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, ); - expect(await this.vaults[riskProfile][token].investStrategyHash()).to.eq(strategyHash); if (DEBUG == true) { console.log("\n"); console.log("After rebalance "); @@ -334,49 +470,76 @@ 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 () { const signers = [this.signers.alice, this.signers.bob]; if (fork == eEVMNetwork.mainnet) { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], steps as StrategyStepType[], this.registry, this.tokens["WETH"], + this.strategyManager, ); } else { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], steps as StrategyStepType[], this.registry, undefined, + this.strategyManager, ); } }); 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 userDeposit( + 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 userDeposit( + 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 () { 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, - this.vaults[riskProfile][token], - this.tokens[token], - ); 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)); @@ -425,23 +588,60 @@ 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 vaultBalanceAfter = await this.vaults[riskProfile][token].balanceUT(); - const poolBalanceAfter = await getLastStrategyStepBalanceLP( - steps as StrategyStepType[], - this.registry, + 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, vaultDepositUT); + await vaultDepositSomeTx.wait(1); + await assertPostVaultDepositState( + this.signers.bob, this.tokens[token], + this.vaults[riskProfile][token], + this.strategyManager, + steps, + ethers.provider, + userBalanceBeforeUT, + vaultDepositUT, + vaultTotalSupplyBeforeVT, + userBalanceBeforeVT, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, ); - expect(vaultBalanceBefore).gt(vaultBalanceAfter); - expect(poolBalanceBefore).lt(poolBalanceAfter); 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 `, @@ -488,30 +688,96 @@ 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( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], steps as StrategyStepType[], this.registry, this.tokens["WETH"], + this.strategyManager, ); } else { - await withdraw( + await userWithdraw( signers, this.vaults[riskProfile][token], this.tokens[token], steps as StrategyStepType[], this.registry, undefined, + this.strategyManager, ); } }); + after(async function () { + 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 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, + ); + + const removeStrategyTx = await this.vaults[riskProfile][token] + .connect(this.signers.operator) + .removeStrategy(strategyHash); + await removeStrategyTx.wait(1); + } + }); }); } } @@ -519,19 +785,22 @@ describe(`${fork}-Vault-rev4`, () => { }); }); -async function deposit( +async function userDeposit( signers: SignerWithAddress[], vaultInstance: Vault, underlyingTokenInstance: ERC20, wethTokenInstance: ERC20 | undefined, + steps: StrategyStepType[], + strategyManager: StrategyManager, ) { 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); + _userDepositInDecimals = BigNumber.from("2").mul(parseUnits("1", decimals)); } const vaultTokenSymbol = await vaultInstance.symbol(); + if (DEBUG == true) { console.log("\n"); console.log("Before user deposit "); @@ -573,19 +842,36 @@ 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, userBalanceBeforeVT, vaultTotalSupplyBeforeVT, vaultValueBeforeUT } = + await getPreActionState(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, + ); if (DEBUG == true) { console.log("\n"); console.log("After user deposit "); @@ -627,29 +913,43 @@ 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()); } } } -async function withdraw( +async function userWithdraw( signers: SignerWithAddress[], vaultInstance: Vault, underlyingTokenInstance: ERC20, 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 + ].getOutputTokenBalance( 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, ); + 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 "); @@ -692,31 +992,42 @@ 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]) .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, + + await assertPostUserWithdrawState( + signers[i], underlyingTokenInstance, + vaultInstance, + 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); - } 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 `, @@ -756,6 +1067,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/4_vault_multi_strategy.spec.ts b/test/test-opty/4_vault_multi_strategy.spec.ts new file mode 100644 index 000000000..04c185b48 --- /dev/null +++ b/test/test-opty/4_vault_multi_strategy.spec.ts @@ -0,0 +1,464 @@ +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"; +import BN from "bignumber.js"; +import { + Registry, + StrategyRegistry, + Vault, + 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"; +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(); + 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.isSwap, + })); + 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)); +// } +// } +// } diff --git a/test/test-opty/utils.ts b/test/test-opty/utils.ts index 1e5bd8aad..b1749b32c 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, 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"; const setStorageAt = (address: string, slot: string, val: string): Promise => hre.network.provider.send("hardhat_setStorageAt", [address, slot, val]); @@ -59,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 { @@ -510,3 +513,51 @@ export async function getPermitLegacySignature( ), ); } + +export async function getPreActionState( + signer: SignerWithAddress, + underlyingTokenInstance: ERC20, + vaultInstance: Vault, + strategyManager: StrategyManager, + steps: StrategyStepType[], +): Promise<{ + userBalanceBeforeUT: BigNumber; + userBalanceBeforeVT: BigNumber; + vaultTotalSupplyBeforeVT: BigNumber; + vaultValueBeforeUT: BigNumber; + vaultBalanceBeforeLP: BigNumber; + vaultBalanceBeforeUT: 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, + vaultBalanceBeforeLP, + vaultBalanceBeforeUT, + }; +} diff --git a/yarn.lock b/yarn.lock index e3c95b1f2..4ad89118e 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" @@ -1606,13 +2008,20 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.1.2, @noble/hashes@npm:~1.1.1": +"@noble/hashes@npm:1.1.2": version: 1.1.2 resolution: "@noble/hashes@npm:1.1.2" checksum: 3c2a8cb7c2e053811032f242155d870c5eb98844d924d69702244d48804cb03b42d4a666c49c2b71164420d8229cb9a6f242b972d50d5bb2f1d673b98b041de2 languageName: node linkType: hard +"@noble/hashes@npm:~1.1.1": + version: 1.1.3 + resolution: "@noble/hashes@npm:1.1.3" + checksum: a6f9783d2a33fc528c8709532b1c26cc3f5866f79c66256e881b28c61a1585be3899b008aa4e5e2b4e01b95c713722f52591cbb18ec51aa0ec63e7eaece1b89c + languageName: node + linkType: hard + "@noble/secp256k1@npm:1.6.3, @noble/secp256k1@npm:~1.6.0": version: 1.6.3 resolution: "@noble/secp256k1@npm:1.6.3" @@ -2998,6 +3407,15 @@ __metadata: languageName: node linkType: hard +"@weiroll/weiroll.js@github:weiroll/weiroll.js#main": + version: 0.3.0 + resolution: "@weiroll/weiroll.js@https://github.com/weiroll/weiroll.js.git#commit=d1bdb851c42183e27b341e39e58eb3956d3237fb" + dependencies: + ethers: ^5.3.1 + checksum: fd828fb42769a5da0243caa1709e072fde45d82446d84084bf9291187ad280aeda7f08ed62cdb77c15acf949248c7a5b0c0564ea7eda00bfa779c2a8523444a3 + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -4400,18 +4818,18 @@ __metadata: linkType: hard "bigint-crypto-utils@npm:^3.0.23": - version: 3.1.6 - resolution: "bigint-crypto-utils@npm:3.1.6" + version: 3.1.7 + resolution: "bigint-crypto-utils@npm:3.1.7" dependencies: bigint-mod-arith: ^3.1.0 - checksum: 5fb427d0344b60e95a7916a128bdbda5581133f76e1f6fcfb967a1831b44ff48bbe9fd98d4e424f269927382b7fa485860552151184fe0c7e5a9ab2383c92bcd + checksum: 10fa35d3e3d37639c8d501f45e0044c9062e7aa60783ae514e4d4ed3235ac24ac180e0dd0c77dad8cb5410ef24de42e1ea12527a997fec4c59f15fa83ea477ba languageName: node linkType: hard "bigint-mod-arith@npm:^3.1.0": - version: 3.1.1 - resolution: "bigint-mod-arith@npm:3.1.1" - checksum: 9cf87ec8f4d295d9d66199022bb2e8c3a75cf6132cc8bebac2d5cd3d31f4c9cd4694a9eb5d12abdb07009ff5a460b9c0d172682205617d242dc06492124e4ba9 + version: 3.1.2 + resolution: "bigint-mod-arith@npm:3.1.2" + checksum: badddd745f6e6c45674b22335d26a9ea83250e749abde20c5f84b24afbc747e259bc36798530953332349ed898f38ec39125b326cae8b8ee2dddfaea7ddf8448 languageName: node linkType: hard @@ -4803,6 +5221,15 @@ __metadata: languageName: node linkType: hard +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + "bytes@npm:3.1.0": version: 3.1.0 resolution: "bytes@npm:3.1.0" @@ -5345,15 +5772,15 @@ __metadata: linkType: hard "cli-table3@npm:^0.6.0": - version: 0.6.3 - resolution: "cli-table3@npm:0.6.3" + version: 0.6.2 + resolution: "cli-table3@npm:0.6.2" dependencies: "@colors/colors": 1.5.0 string-width: ^4.2.0 dependenciesMeta: "@colors/colors": optional: true - checksum: 09897f68467973f827c04e7eaadf13b55f8aec49ecd6647cc276386ea660059322e2dd8020a8b6b84d422dbdd619597046fa89cbbbdc95b2cea149a2df7c096c + checksum: 2f82391698b8a2a2a5e45d2adcfea5d93e557207f90455a8d4c1aac688e9b18a204d9eb4ba1d322fa123b17d64ea3dc5e11de8b005529f3c3e7dbeb27cb4d9be languageName: node linkType: hard @@ -6625,6 +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": "github:weiroll/weiroll.js#main" bignumber.js: ^9.0.2 chai: ^4.3.4 commitizen: ^4.2.4 @@ -7838,6 +8266,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" @@ -15694,6 +16160,13 @@ resolve@1.17.0: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + "strict-uri-encode@npm:^1.0.0": version: 1.1.0 resolution: "strict-uri-encode@npm:1.1.0" @@ -16807,9 +17280,11 @@ typescript@^4.4.3: linkType: hard "undici@npm:^5.4.0": - version: 5.10.0 - resolution: "undici@npm:5.10.0" - checksum: 7ba2b71dccc74cd2bdf645b83e9aaef374ae04855943d0a2f42a3d0b9e5556f37cc9b5156fb5288277a2fa95fd46a56f3ae0d5cf73db3f008d75ec41104b136c + version: 5.11.0 + resolution: "undici@npm:5.11.0" + dependencies: + busboy: ^1.6.0 + checksum: 20669ef4023d24a75e81ed8142829f525f16af14d61f833b5f19cfc6bed7c706adde343a190d764dcceb227eba3211222d2b194010462a2ca221a7da0487f563 languageName: node linkType: hard