From 4cd4e20021822c6c1d9cdafb4b5d270eb8cf1a69 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:45:52 -0400 Subject: [PATCH 01/70] add msg.sender to depositLpToken --- contracts/facets/VaultAccountingFacet.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/facets/VaultAccountingFacet.sol b/contracts/facets/VaultAccountingFacet.sol index 77095b2..19f6321 100644 --- a/contracts/facets/VaultAccountingFacet.sol +++ b/contracts/facets/VaultAccountingFacet.sol @@ -70,7 +70,7 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender - IVault(vaultAddress).depositLpToken(amount); + IVault(vaultAddress).depositLpToken(amount, msg.sender); vs.userVaultBalances[msg.sender][vaultName] += amount; emit DepositLpToken(msg.sender, vaultName, amount); From 3f849f728b85b897be6b82f5f81469a26ff0168d Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:46:33 -0400 Subject: [PATCH 02/70] add user param to depositLpToken --- contracts/interfaces/IVault.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/interfaces/IVault.sol b/contracts/interfaces/IVault.sol index 1b0ec28..c5087a1 100644 --- a/contracts/interfaces/IVault.sol +++ b/contracts/interfaces/IVault.sol @@ -12,9 +12,10 @@ interface IVault { /** * @dev Deposits LP token directly into vault * + * @param user The user depositing * @param amount The amount of LP tokens to deposit */ - function depositLpToken(uint256 amount) external; + function depositLpToken(uint256 amount, address user) external; /** * @dev Converts LP token and withdraws as ETH From f0b50e43d294d392657b3a117d9a8a7382a672e8 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:47:49 -0400 Subject: [PATCH 03/70] add address param --- contracts/vaults/EmptyVault.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/vaults/EmptyVault.sol b/contracts/vaults/EmptyVault.sol index b22a5ee..54ea5c6 100644 --- a/contracts/vaults/EmptyVault.sol +++ b/contracts/vaults/EmptyVault.sol @@ -12,7 +12,7 @@ contract EmptyVault is BaseVault { return msg.value; } - function depositLpToken(uint256) external override onlyDiamond nonReentrant { + function depositLpToken(uint256, address) external override onlyDiamond nonReentrant { revert("Disabled."); } From 6a18b22fb6c513904ac86b2510f3112d8fe29876 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:48:11 -0400 Subject: [PATCH 04/70] initial commit --- .../interfaces/RocketDepositPoolInterface.sol | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 contracts/interfaces/RocketDepositPoolInterface.sol diff --git a/contracts/interfaces/RocketDepositPoolInterface.sol b/contracts/interfaces/RocketDepositPoolInterface.sol new file mode 100644 index 0000000..0e1c5c3 --- /dev/null +++ b/contracts/interfaces/RocketDepositPoolInterface.sol @@ -0,0 +1,15 @@ +pragma solidity 0.8.0; + +// SPDX-License-Identifier: GPL-3.0-only + +interface RocketDepositPoolInterface { + function getBalance() external view returns (uint256); + function getExcessBalance() external view returns (uint256); + function deposit() external payable; + function recycleDissolvedDeposit() external payable; + function recycleExcessCollateral() external payable; + function recycleLiquidatedStake() external payable; + function assignDeposits() external; + function withdrawExcessBalance(uint256 _amount) external; + function getUserLastDepositBlock(address _address) external view returns (uint256); +} \ No newline at end of file From 2e9152efc9d2759f1023e11e4c959a718c4ac079 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:48:31 -0400 Subject: [PATCH 05/70] initial commit --- .../interfaces/RocketTokenRETHInterface.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 contracts/interfaces/RocketTokenRETHInterface.sol diff --git a/contracts/interfaces/RocketTokenRETHInterface.sol b/contracts/interfaces/RocketTokenRETHInterface.sol new file mode 100644 index 0000000..d4a9ae6 --- /dev/null +++ b/contracts/interfaces/RocketTokenRETHInterface.sol @@ -0,0 +1,17 @@ +pragma solidity 0.8.0; + +// SPDX-License-Identifier: GPL-3.0-only + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface RocketTokenRETHInterface is IERC20 { + function getEthValue(uint256 _rethAmount) external view returns (uint256); + function getRethValue(uint256 _ethAmount) external view returns (uint256); + function getExchangeRate() external view returns (uint256); + function getTotalCollateral() external view returns (uint256); + function getCollateralRate() external view returns (uint256); + function depositExcess() external payable; + function depositExcessCollateral() external; + function mint(uint256 _ethAmount, address _to) external; + function burn(uint256 _rethAmount) external; +} \ No newline at end of file From 6edefca40c7cd28f886a565f3247a26371bac051 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Sat, 23 Apr 2022 14:50:55 -0400 Subject: [PATCH 06/70] inital commit --- contracts/vaults/RocketPoolVault.sol | 66 ++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 contracts/vaults/RocketPoolVault.sol diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol new file mode 100644 index 0000000..693709e --- /dev/null +++ b/contracts/vaults/RocketPoolVault.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { BaseVault } from "./BaseVault.sol"; +import { RocketDepositPoolInterface } from "../interfaces/RocketDepositPoolInterface.sol"; +import { RocketTokenRETHInterface } from "../interfaces/RocketTokenRETHInterface.sol"; + +contract RocketVault is BaseVault { + + RocketDepositPoolInterface depositPool; + RocketTokenRETHInterface rETH; + + constructor (address _diamond, address _rETHAddress, address _depositPool) BaseVault(_diamond) { + depositPool = RocketDepositPoolInterface(_depositPool); + rETH = RocketTokenRETHInterface(_rETHAddress); + } + + function deposit() external payable override onlyDiamond nonReentrant returns (uint256) { + // Get amount of tokens before and after to determine how many were minted + uint256 balanceBefore = rETH.balanceOf(address(this)); + depositPool.deposit{ value: msg.value }(); + uint256 balanceAfter = rETH.balanceOf(address(this)); + + uint256 sharesMinted = balanceAfter - balanceBefore; + + return sharesMinted; + } + + function depositLpToken(uint256 amount, address user) external override onlyDiamond nonReentrant { + rETH.transferFrom(user, address(this), amount); + } + + function withdraw(uint256 lpTokenAmount, address payable recipient) external override onlyDiamond nonReentrant returns (uint256) { + uint256 balanceBefore = address(this).balance; + rETH.burn(lpTokenAmount); + uint256 balanceAfter = address(this).balance; + + uint256 ethReturned = balanceAfter - balanceBefore; + + recipient.transfer(ethReturned); + + return ethReturned; + } + + function withdrawLpToken (uint256 lpTokenAmount, address recipient) external override onlyDiamond nonReentrant { + rETH.transferFrom(address(this), recipient, lpTokenAmount); + } + + function transferFunds(address payable newVaultAddress) external override onlyDiamond { + // redeem all rETH + rETH.burn(rETH.balanceOf(address(this))); + newVaultAddress.transfer(address(this).balance); + } + + function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { + return rETH.getEthValue(lpTokenAmount); + } + + function getAmountLpTokens(uint256 ethAmount) external view override returns (uint256) { + return rETH.getRethValue(ethAmount); + } + + function getLpToken() external view override returns (address) { + return address(rETH); + } +} \ No newline at end of file From d676eea810dc204465b30c566f2d6b6dbdda7f9c Mon Sep 17 00:00:00 2001 From: ez7212 Date: Thu, 28 Apr 2022 11:45:10 -0400 Subject: [PATCH 07/70] fix IVault directory --- contracts/facets/BidMarketFacet.sol | 2 +- contracts/facets/OptionMarketFacet.sol | 2 +- contracts/facets/VaultAccountingFacet.sol | 2 +- contracts/facets/VaultManagementFacet.sol | 2 +- contracts/libraries/LibVaultUtils.sol | 2 +- contracts/vaults/BaseVault.sol | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/facets/BidMarketFacet.sol b/contracts/facets/BidMarketFacet.sol index 1ddae91..6c537ed 100644 --- a/contracts/facets/BidMarketFacet.sol +++ b/contracts/facets/BidMarketFacet.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import { IBidMarket } from "../interfaces/facets/IBidMarket.sol"; import { ICryptoPunksMarket } from "../interfaces/ICryptoPunksMarket.sol"; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { LibStorage, BidMarketStorage, VaultStorage, TokenAddressStorage } from "../libraries/LibStorage.sol"; import { LibVaultUtils } from "../libraries/LibVaultUtils.sol"; import { BidInputParams, BidInfo } from "../FukuTypes.sol"; diff --git a/contracts/facets/OptionMarketFacet.sol b/contracts/facets/OptionMarketFacet.sol index f5fc6d8..9ce63ee 100644 --- a/contracts/facets/OptionMarketFacet.sol +++ b/contracts/facets/OptionMarketFacet.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import { IOptionMarket } from "../interfaces/facets/IOptionMarket.sol"; import { ICryptoPunksMarket } from "../interfaces/ICryptoPunksMarket.sol"; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { LibStorage, OptionMarketStorage, VaultStorage, TokenAddressStorage } from "../libraries/LibStorage.sol"; import { LibVaultUtils } from "../libraries/LibVaultUtils.sol"; import { OptionDuration, OptionInputParams, OptionInfo } from "../FukuTypes.sol"; diff --git a/contracts/facets/VaultAccountingFacet.sol b/contracts/facets/VaultAccountingFacet.sol index 19f6321..d4e67bf 100644 --- a/contracts/facets/VaultAccountingFacet.sol +++ b/contracts/facets/VaultAccountingFacet.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { IVaultAccounting } from "../interfaces/facets/IVaultAccounting.sol"; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { LibStorage, VaultStorage, DepositsRewardsStorage, TokenAddressStorage } from "../libraries/LibStorage.sol"; import { LibVaultUtils } from "../libraries/LibVaultUtils.sol"; import { LibDiamond } from "../vendor/libraries/LibDiamond.sol"; diff --git a/contracts/facets/VaultManagementFacet.sol b/contracts/facets/VaultManagementFacet.sol index 408fa46..7b74b74 100644 --- a/contracts/facets/VaultManagementFacet.sol +++ b/contracts/facets/VaultManagementFacet.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { IVaultManagement } from "../interfaces/facets/IVaultManagement.sol"; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { LibStorage, VaultStorage } from "../libraries/LibStorage.sol"; import { LibDiamond } from "../vendor/libraries/LibDiamond.sol"; diff --git a/contracts/libraries/LibVaultUtils.sol b/contracts/libraries/LibVaultUtils.sol index 65f914f..8a19f7f 100644 --- a/contracts/libraries/LibVaultUtils.sol +++ b/contracts/libraries/LibVaultUtils.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.0; import { LibStorage, VaultStorage } from "./LibStorage.sol"; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; diff --git a/contracts/vaults/BaseVault.sol b/contracts/vaults/BaseVault.sol index 1cc0180..458eb6c 100644 --- a/contracts/vaults/BaseVault.sol +++ b/contracts/vaults/BaseVault.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import { IVault } from "../interfaces/IVault.sol"; +import { IVault } from "../interfaces/vaults/IVault.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; From f608078ca79ca0ac649e9ee97cba9ae1b81f5eb2 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Thu, 28 Apr 2022 11:46:57 -0400 Subject: [PATCH 08/70] new directory for vault interfaces --- contracts/interfaces/vaults/IVault.sol | 62 +++++++++++++++++++ .../vaults/RocketDepositPoolInterface.sol | 9 +++ .../vaults/RocketStorageInterface.sol | 9 +++ .../vaults/RocketTokenRETHInterface.sol | 14 +++++ 4 files changed, 94 insertions(+) create mode 100644 contracts/interfaces/vaults/IVault.sol create mode 100644 contracts/interfaces/vaults/RocketDepositPoolInterface.sol create mode 100644 contracts/interfaces/vaults/RocketStorageInterface.sol create mode 100644 contracts/interfaces/vaults/RocketTokenRETHInterface.sol diff --git a/contracts/interfaces/vaults/IVault.sol b/contracts/interfaces/vaults/IVault.sol new file mode 100644 index 0000000..c5087a1 --- /dev/null +++ b/contracts/interfaces/vaults/IVault.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +interface IVault { + /** + * @dev Deposits ETH and converts to vault's LP token + * + * @return The amount of LP tokens received from ETH deposit + */ + function deposit() external payable returns (uint256); + + /** + * @dev Deposits LP token directly into vault + * + * @param user The user depositing + * @param amount The amount of LP tokens to deposit + */ + function depositLpToken(uint256 amount, address user) external; + + /** + * @dev Converts LP token and withdraws as ETH + * + * @param lpTokenAmount The amount of LP tokens to withdraw before converting + * @param recipient The recipient of the converted ETH + * @return The amount of ETH withdrawn + */ + function withdraw(uint256 lpTokenAmount, address payable recipient) external returns (uint256); + + /** + * @dev Withdraws LP token directly from vault + * + * @param lpTokenAmount The amount of LP tokens to withdraw + * @param recipient The recipient of the LP tokens + */ + function withdrawLpToken(uint256 lpTokenAmount, address recipient) external; + + /** + * @dev Transfers LP tokens to new vault + * + * @param newVaultAddress The new vault which will receive the LP tokens + */ + function transferFunds(address payable newVaultAddress) external; + + /** + * @dev Gets the conversion from LP token to ETH + * + * @param lpTokenAmount The LP token amount + */ + function getAmountETH(uint256 lpTokenAmount) external view returns (uint256); + + /** + * @dev Gets the conversion from ETH to LP token + * + * @param ethAmount The ETH amount + */ + function getAmountLpTokens(uint256 ethAmount) external view returns (uint256); + + /** + * @dev Get the LP token address of the vault + */ + function getLpToken() external view returns (address); +} diff --git a/contracts/interfaces/vaults/RocketDepositPoolInterface.sol b/contracts/interfaces/vaults/RocketDepositPoolInterface.sol new file mode 100644 index 0000000..496c951 --- /dev/null +++ b/contracts/interfaces/vaults/RocketDepositPoolInterface.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.0; + +interface RocketDepositPoolInterface { + + function deposit() external payable; + + function getUserLastDepositBlock(address _address) external view returns (uint256); +} \ No newline at end of file diff --git a/contracts/interfaces/vaults/RocketStorageInterface.sol b/contracts/interfaces/vaults/RocketStorageInterface.sol new file mode 100644 index 0000000..5782e20 --- /dev/null +++ b/contracts/interfaces/vaults/RocketStorageInterface.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.0; + +interface RocketStorageInterface { + + // Getters + function getAddress(bytes32 _key) external view returns (address); + +} \ No newline at end of file diff --git a/contracts/interfaces/vaults/RocketTokenRETHInterface.sol b/contracts/interfaces/vaults/RocketTokenRETHInterface.sol new file mode 100644 index 0000000..a03f7c1 --- /dev/null +++ b/contracts/interfaces/vaults/RocketTokenRETHInterface.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity 0.8.0; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface RocketTokenRETHInterface is IERC20 { + function getEthValue(uint256 _rethAmount) external view returns (uint256); + + function getRethValue(uint256 _ethAmount) external view returns (uint256); + + function mint(uint256 _ethAmount, address _to) external; + + function burn(uint256 _rethAmount) external; +} \ No newline at end of file From f6d128016b5cf6efdadc00b033e7cf4d41186b9e Mon Sep 17 00:00:00 2001 From: ez7212 Date: Thu, 28 Apr 2022 11:48:24 -0400 Subject: [PATCH 09/70] import only RocketStorage and add withdrawal check --- contracts/vaults/RocketPoolVault.sol | 53 +++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 9 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index 693709e..e274174 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -2,20 +2,25 @@ pragma solidity ^0.8.0; import { BaseVault } from "./BaseVault.sol"; -import { RocketDepositPoolInterface } from "../interfaces/RocketDepositPoolInterface.sol"; -import { RocketTokenRETHInterface } from "../interfaces/RocketTokenRETHInterface.sol"; +import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; +import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; +import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; contract RocketVault is BaseVault { + // Contract for depositing ETH and receiving rETH + RocketStorageInterface rocketStorage; - RocketDepositPoolInterface depositPool; - RocketTokenRETHInterface rETH; - - constructor (address _diamond, address _rETHAddress, address _depositPool) BaseVault(_diamond) { - depositPool = RocketDepositPoolInterface(_depositPool); - rETH = RocketTokenRETHInterface(_rETHAddress); + constructor (address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { + rocketStorage = RocketStorageInterface(_rocketStorageAddress); } function deposit() external payable override onlyDiamond nonReentrant returns (uint256) { + // Load contracts + address depositPoolAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketDepositPool"))); + RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(depositPoolAddress); + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + // Get amount of tokens before and after to determine how many were minted uint256 balanceBefore = rETH.balanceOf(address(this)); depositPool.deposit{ value: msg.value }(); @@ -27,10 +32,24 @@ contract RocketVault is BaseVault { } function depositLpToken(uint256 amount, address user) external override onlyDiamond nonReentrant { + // Load rETH contract + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + rETH.transferFrom(user, address(this), amount); } function withdraw(uint256 lpTokenAmount, address payable recipient) external override onlyDiamond nonReentrant returns (uint256) { + // Load contracts + address rocketVault = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + + // check that rocketVault has enough ETH to handle withdrawal + uint256 amountToWithdraw = rETH.getEthValue(lpTokenAmount); + require(rocketVault.balance >= amountToWithdraw, "not enough ETH to withdraw"); + + // Redeem rETH for ETH and send to recipient uint256 balanceBefore = address(this).balance; rETH.burn(lpTokenAmount); uint256 balanceAfter = address(this).balance; @@ -43,24 +62,40 @@ contract RocketVault is BaseVault { } function withdrawLpToken (uint256 lpTokenAmount, address recipient) external override onlyDiamond nonReentrant { + // Load rETH contract + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + rETH.transferFrom(address(this), recipient, lpTokenAmount); } function transferFunds(address payable newVaultAddress) external override onlyDiamond { + // Load rETH contract + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + // redeem all rETH rETH.burn(rETH.balanceOf(address(this))); newVaultAddress.transfer(address(this).balance); } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { + // Load rETH contract + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + return rETH.getEthValue(lpTokenAmount); } function getAmountLpTokens(uint256 ethAmount) external view override returns (uint256) { + // Load rETH contract + address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + return rETH.getRethValue(ethAmount); } function getLpToken() external view override returns (address) { - return address(rETH); + return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); } } \ No newline at end of file From 210d35f0501d9d50df3acba2172780b585440a31 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Mon, 2 May 2022 22:22:15 -0400 Subject: [PATCH 10/70] update solidity version --- contracts/interfaces/IVault.sol | 62 ------------------- .../interfaces/RocketDepositPoolInterface.sol | 15 ----- .../interfaces/RocketTokenRETHInterface.sol | 17 ----- .../vaults/RocketDepositPoolInterface.sol | 2 +- .../vaults/RocketTokenRETHInterface.sol | 2 +- 5 files changed, 2 insertions(+), 96 deletions(-) delete mode 100644 contracts/interfaces/IVault.sol delete mode 100644 contracts/interfaces/RocketDepositPoolInterface.sol delete mode 100644 contracts/interfaces/RocketTokenRETHInterface.sol diff --git a/contracts/interfaces/IVault.sol b/contracts/interfaces/IVault.sol deleted file mode 100644 index c5087a1..0000000 --- a/contracts/interfaces/IVault.sol +++ /dev/null @@ -1,62 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -interface IVault { - /** - * @dev Deposits ETH and converts to vault's LP token - * - * @return The amount of LP tokens received from ETH deposit - */ - function deposit() external payable returns (uint256); - - /** - * @dev Deposits LP token directly into vault - * - * @param user The user depositing - * @param amount The amount of LP tokens to deposit - */ - function depositLpToken(uint256 amount, address user) external; - - /** - * @dev Converts LP token and withdraws as ETH - * - * @param lpTokenAmount The amount of LP tokens to withdraw before converting - * @param recipient The recipient of the converted ETH - * @return The amount of ETH withdrawn - */ - function withdraw(uint256 lpTokenAmount, address payable recipient) external returns (uint256); - - /** - * @dev Withdraws LP token directly from vault - * - * @param lpTokenAmount The amount of LP tokens to withdraw - * @param recipient The recipient of the LP tokens - */ - function withdrawLpToken(uint256 lpTokenAmount, address recipient) external; - - /** - * @dev Transfers LP tokens to new vault - * - * @param newVaultAddress The new vault which will receive the LP tokens - */ - function transferFunds(address payable newVaultAddress) external; - - /** - * @dev Gets the conversion from LP token to ETH - * - * @param lpTokenAmount The LP token amount - */ - function getAmountETH(uint256 lpTokenAmount) external view returns (uint256); - - /** - * @dev Gets the conversion from ETH to LP token - * - * @param ethAmount The ETH amount - */ - function getAmountLpTokens(uint256 ethAmount) external view returns (uint256); - - /** - * @dev Get the LP token address of the vault - */ - function getLpToken() external view returns (address); -} diff --git a/contracts/interfaces/RocketDepositPoolInterface.sol b/contracts/interfaces/RocketDepositPoolInterface.sol deleted file mode 100644 index 0e1c5c3..0000000 --- a/contracts/interfaces/RocketDepositPoolInterface.sol +++ /dev/null @@ -1,15 +0,0 @@ -pragma solidity 0.8.0; - -// SPDX-License-Identifier: GPL-3.0-only - -interface RocketDepositPoolInterface { - function getBalance() external view returns (uint256); - function getExcessBalance() external view returns (uint256); - function deposit() external payable; - function recycleDissolvedDeposit() external payable; - function recycleExcessCollateral() external payable; - function recycleLiquidatedStake() external payable; - function assignDeposits() external; - function withdrawExcessBalance(uint256 _amount) external; - function getUserLastDepositBlock(address _address) external view returns (uint256); -} \ No newline at end of file diff --git a/contracts/interfaces/RocketTokenRETHInterface.sol b/contracts/interfaces/RocketTokenRETHInterface.sol deleted file mode 100644 index d4a9ae6..0000000 --- a/contracts/interfaces/RocketTokenRETHInterface.sol +++ /dev/null @@ -1,17 +0,0 @@ -pragma solidity 0.8.0; - -// SPDX-License-Identifier: GPL-3.0-only - -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; - -interface RocketTokenRETHInterface is IERC20 { - function getEthValue(uint256 _rethAmount) external view returns (uint256); - function getRethValue(uint256 _ethAmount) external view returns (uint256); - function getExchangeRate() external view returns (uint256); - function getTotalCollateral() external view returns (uint256); - function getCollateralRate() external view returns (uint256); - function depositExcess() external payable; - function depositExcessCollateral() external; - function mint(uint256 _ethAmount, address _to) external; - function burn(uint256 _rethAmount) external; -} \ No newline at end of file diff --git a/contracts/interfaces/vaults/RocketDepositPoolInterface.sol b/contracts/interfaces/vaults/RocketDepositPoolInterface.sol index 496c951..7723715 100644 --- a/contracts/interfaces/vaults/RocketDepositPoolInterface.sol +++ b/contracts/interfaces/vaults/RocketDepositPoolInterface.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-only -pragma solidity 0.8.0; +pragma solidity ^0.8.0; interface RocketDepositPoolInterface { diff --git a/contracts/interfaces/vaults/RocketTokenRETHInterface.sol b/contracts/interfaces/vaults/RocketTokenRETHInterface.sol index a03f7c1..02f5570 100644 --- a/contracts/interfaces/vaults/RocketTokenRETHInterface.sol +++ b/contracts/interfaces/vaults/RocketTokenRETHInterface.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-3.0-only -pragma solidity 0.8.0; +pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; From 962ae49fd241c685b82ce8598155c3550808016e Mon Sep 17 00:00:00 2001 From: ez7212 Date: Mon, 2 May 2022 22:23:44 -0400 Subject: [PATCH 11/70] update solidity version and remove comment --- contracts/interfaces/vaults/RocketStorageInterface.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contracts/interfaces/vaults/RocketStorageInterface.sol b/contracts/interfaces/vaults/RocketStorageInterface.sol index 5782e20..fe5a876 100644 --- a/contracts/interfaces/vaults/RocketStorageInterface.sol +++ b/contracts/interfaces/vaults/RocketStorageInterface.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only -pragma solidity 0.8.0; +pragma solidity ^0.8.0; interface RocketStorageInterface { - // Getters function getAddress(bytes32 _key) external view returns (address); } \ No newline at end of file From 7543bace0bde8e58523ee816bdd9db78ff7cc0aa Mon Sep 17 00:00:00 2001 From: ez7212 Date: Mon, 2 May 2022 23:04:46 -0400 Subject: [PATCH 12/70] move withdraw check to getAmountEth --- contracts/vaults/RocketPoolVault.sol | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index e274174..c14b1dd 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -7,7 +7,7 @@ import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHIn import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; contract RocketVault is BaseVault { - // Contract for depositing ETH and receiving rETH + // stores state for Rocket Protocol RocketStorageInterface rocketStorage; constructor (address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { @@ -41,13 +41,11 @@ contract RocketVault is BaseVault { function withdraw(uint256 lpTokenAmount, address payable recipient) external override onlyDiamond nonReentrant returns (uint256) { // Load contracts - address rocketVault = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); // check that rocketVault has enough ETH to handle withdrawal uint256 amountToWithdraw = rETH.getEthValue(lpTokenAmount); - require(rocketVault.balance >= amountToWithdraw, "not enough ETH to withdraw"); // Redeem rETH for ETH and send to recipient uint256 balanceBefore = address(this).balance; @@ -80,11 +78,19 @@ contract RocketVault is BaseVault { } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { - // Load rETH contract + // Load rocketVault address and rETH contract + address rocketVault = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - return rETH.getEthValue(lpTokenAmount); + uint256 ethAmount = rETH.getEthValue(lpTokenAmount); + + // check if rocket vault has enough to withdraw, if not return rocket vault balance + if (rocketVault.balance >= ethAmount) { + return ethAmount; + } else { + return rocketVault.balance; + } } function getAmountLpTokens(uint256 ethAmount) external view override returns (uint256) { From 87920b391ce284458481b7ac0b594ff6a6a1a3e9 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 17:07:32 -0400 Subject: [PATCH 13/70] rearrange params --- contracts/interfaces/vaults/IVault.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/vaults/IVault.sol b/contracts/interfaces/vaults/IVault.sol index c5087a1..2f1934d 100644 --- a/contracts/interfaces/vaults/IVault.sol +++ b/contracts/interfaces/vaults/IVault.sol @@ -12,8 +12,8 @@ interface IVault { /** * @dev Deposits LP token directly into vault * - * @param user The user depositing * @param amount The amount of LP tokens to deposit + * @param user The user depositing */ function depositLpToken(uint256 amount, address user) external; From 2f39bbdd06edb2986f3b566d415c433856ae90ca Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 17:07:55 -0400 Subject: [PATCH 14/70] change vault balance getter function --- contracts/vaults/RocketPoolVault.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index c14b1dd..6bc39d0 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -5,6 +5,7 @@ import { BaseVault } from "./BaseVault.sol"; import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; +import { RocketVaultInterface } from "../interfaces/vaults/RocketVaultInterface.sol"; contract RocketVault is BaseVault { // stores state for Rocket Protocol @@ -43,9 +44,6 @@ contract RocketVault is BaseVault { // Load contracts address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - - // check that rocketVault has enough ETH to handle withdrawal - uint256 amountToWithdraw = rETH.getEthValue(lpTokenAmount); // Redeem rETH for ETH and send to recipient uint256 balanceBefore = address(this).balance; @@ -78,18 +76,20 @@ contract RocketVault is BaseVault { } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { - // Load rocketVault address and rETH contract - address rocketVault = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); + // Load rocketVault and rETH contracts + address rocketVaultAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); + RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress); address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); uint256 ethAmount = rETH.getEthValue(lpTokenAmount); // check if rocket vault has enough to withdraw, if not return rocket vault balance - if (rocketVault.balance >= ethAmount) { + uint256 vaultBalance = rocketVault.balanceOf("rocketDepositPool"); + if (vaultBalance >= ethAmount) { return ethAmount; } else { - return rocketVault.balance; + return vaultBalance; } } From 4cca37b44edec51fe0ac3469b0cb6c36faa09a45 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 17:08:12 -0400 Subject: [PATCH 15/70] initial commit --- .../interfaces/vaults/RocketVaultInterface.sol | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 contracts/interfaces/vaults/RocketVaultInterface.sol diff --git a/contracts/interfaces/vaults/RocketVaultInterface.sol b/contracts/interfaces/vaults/RocketVaultInterface.sol new file mode 100644 index 0000000..8a433fc --- /dev/null +++ b/contracts/interfaces/vaults/RocketVaultInterface.sol @@ -0,0 +1,16 @@ +pragma solidity ^0.8.0; + +// SPDX-License-Identifier: GPL-3.0-only +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; + +interface RocketVaultInterface { + function balanceOf(string memory _networkContractName) external view returns (uint256); + function depositEther() external payable; + function withdrawEther(uint256 _amount) external; + function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; + function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external; + function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256); + function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; + function burnToken(ERC20Burnable _tokenAddress, uint256 _amount) external; +} \ No newline at end of file From 609ce9ab709ca4004d31d907ee4765feecbee382 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 22:37:17 -0400 Subject: [PATCH 16/70] add forking config --- hardhat.config.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hardhat.config.js b/hardhat.config.js index 7b7c098..d83f8bf 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -5,7 +5,17 @@ require("hardhat-deploy-ethers"); require("hardhat-gas-reporter"); require("dotenv").config(); +const MAINNET_URL = process.env.MAINNET_URL ? process.env.MAINNET_URL : ""; + module.exports = { + networks: { + hardhat: { + forking: { + url: MAINNET_URL, + enabled: true, + }, + }, + }, solidity: { version: "0.8.4", settings: { From 03bd3011ece3754fb79778d02a339a38a6037302 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 22:37:34 -0400 Subject: [PATCH 17/70] add rocket vault deployment --- test/fixture.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/fixture.js b/test/fixture.js index ac6e409..d01a25b 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -101,8 +101,14 @@ const fixture = deployments.createFixture(async () => { // create vault names const vaultNames = { empty: "0xeeeeeeeeeeeeeeeeeeeeeeee", + rocketVault: "0xeeeeeeeeeeeeeeeeeeeeeeed" }; + // rocket pool addresses for deployment + const rocketAddresses = { + rocketStorage: "0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46" + } + // create and register vault const EmptyVault = await ethers.getContractFactory("EmptyVault"); const emptyVault = await EmptyVault.deploy(diamond.address); @@ -110,6 +116,13 @@ const fixture = deployments.createFixture(async () => { tx = await vaultManagement.registerVault(vaultNames.empty, emptyVault.address); await tx.wait(); + // create and register rocket vault + const RocketVault = await ethers.getContractFactory("RocketVault"); + const rocketVault = await RocketVault.deploy(diamond.address, rocketAddresses.rocketStorage); + await rocketVault.deployed(); + tx = await vaultManagement.registerVault(vaultNames.rocketVault, rocketVault.address); + await tx.wait(); + return { diamond, diamondCut, From dfa61c050d873661bcf8b82a6a07678966294ae5 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 22:47:51 -0400 Subject: [PATCH 18/70] rocket test initial commit --- test/vaults/RocketVault.js | 41 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 test/vaults/RocketVault.js diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js new file mode 100644 index 0000000..e31f377 --- /dev/null +++ b/test/vaults/RocketVault.js @@ -0,0 +1,41 @@ +const { ethers } = require("hardhat"); +const { expect } = require("chai"); + +const { fixture } = require("../fixture"); + +describe("Rocket Vault", async () => { + // fixture values + let deployer, user; + let vaultAccounting; + let vaultManagement; + let vaultNames; + + // vault parameters + let depositAmount; + let expectedEthAmount; + let expectedLpToken; + let expectedLpTokenAmount; + let rocketVault; + + beforeEach(async () => { + // initialize fixture values + ({ vaultAccounting, vaultManagement, vaultNames } = await fixture()); + [deployer, user] = await ethers.getSigners(); + + // initialize vault parameters + depositAmount = ethers.utils.parseEther("1.0"); + expectedEthAmount = ethers.utils.parseEther("1.0"); + expectedLpToken = "0xae78736Cd615f374D3085123A210448E74Fc6393"; // rETH token address + rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); + expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount minted when depositing 1 ETH + }); + + it("Should reflect correct conversion from lp tokens to ETH", async () => { + expect(await rocketVault.getAmountETH(expectedLpTokenAmount)).to.be.equal(expectedEthAmount); + }); + + it("Should reflect correct conversion from ETH to lp tokens", async () => { + expect(await rocketVault.getAmountLpTokens(expectedEthAmount)).to.be.equal(expectedLpTokenAmount); + }); + +}); From ba2a92d1996fd914e875dd9f52dce80bba126103 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 3 May 2022 22:49:06 -0400 Subject: [PATCH 19/70] fix comment --- test/vaults/RocketVault.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index e31f377..7255ad4 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -27,7 +27,7 @@ describe("Rocket Vault", async () => { expectedEthAmount = ethers.utils.parseEther("1.0"); expectedLpToken = "0xae78736Cd615f374D3085123A210448E74Fc6393"; // rETH token address rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); - expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount minted when depositing 1 ETH + expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount equivalent to 1 ETH }); it("Should reflect correct conversion from lp tokens to ETH", async () => { From 8d364a5b19a7afec6194eddc259f2426b53bc273 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 4 May 2022 18:58:34 -0400 Subject: [PATCH 20/70] fix getAddress parameter --- contracts/vaults/RocketPoolVault.sol | 58 ++++++++++++++++++++-------- 1 file changed, 41 insertions(+), 17 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index 6bc39d0..fd0e510 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -11,15 +11,19 @@ contract RocketVault is BaseVault { // stores state for Rocket Protocol RocketStorageInterface rocketStorage; - constructor (address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { + constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = RocketStorageInterface(_rocketStorageAddress); } function deposit() external payable override onlyDiamond nonReentrant returns (uint256) { // Load contracts - address depositPoolAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketDepositPool"))); + address depositPoolAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketDepositPool")) + ); RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(depositPoolAddress); - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); // Get amount of tokens before and after to determine how many were minted @@ -34,17 +38,27 @@ contract RocketVault is BaseVault { function depositLpToken(uint256 amount, address user) external override onlyDiamond nonReentrant { // Load rETH contract - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); rETH.transferFrom(user, address(this), amount); } - function withdraw(uint256 lpTokenAmount, address payable recipient) external override onlyDiamond nonReentrant returns (uint256) { + function withdraw(uint256 lpTokenAmount, address payable recipient) + external + override + onlyDiamond + nonReentrant + returns (uint256) + { // Load contracts - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - + // Redeem rETH for ETH and send to recipient uint256 balanceBefore = address(this).balance; rETH.burn(lpTokenAmount); @@ -57,17 +71,21 @@ contract RocketVault is BaseVault { return ethReturned; } - function withdrawLpToken (uint256 lpTokenAmount, address recipient) external override onlyDiamond nonReentrant { + function withdrawLpToken(uint256 lpTokenAmount, address recipient) external override onlyDiamond nonReentrant { // Load rETH contract - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - rETH.transferFrom(address(this), recipient, lpTokenAmount); + rETH.transfer(recipient, lpTokenAmount); } function transferFunds(address payable newVaultAddress) external override onlyDiamond { // Load rETH contract - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); // redeem all rETH @@ -77,14 +95,18 @@ contract RocketVault is BaseVault { function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { // Load rocketVault and rETH contracts - address rocketVaultAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketVault"))); + address rocketVaultAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketVault")) + ); RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress); - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); uint256 ethAmount = rETH.getEthValue(lpTokenAmount); - // check if rocket vault has enough to withdraw, if not return rocket vault balance + // check if rocket vault has enough to withdraw, if not return rocket vault balance uint256 vaultBalance = rocketVault.balanceOf("rocketDepositPool"); if (vaultBalance >= ethAmount) { return ethAmount; @@ -95,13 +117,15 @@ contract RocketVault is BaseVault { function getAmountLpTokens(uint256 ethAmount) external view override returns (uint256) { // Load rETH contract - address rethAddress = rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); return rETH.getRethValue(ethAmount); } function getLpToken() external view override returns (address) { - return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketETHToken"))); + return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketTokenRETH"))); } -} \ No newline at end of file +} From 9eeb1b7be9e8dcd7ccd4b2e54c2c96f716eafb58 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 4 May 2022 18:58:49 -0400 Subject: [PATCH 21/70] all tests for rocket vault --- test/vaults/RocketVault.js | 235 ++++++++++++++++++++++++++++++++++++- 1 file changed, 233 insertions(+), 2 deletions(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index 7255ad4..b237cfe 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -16,6 +16,10 @@ describe("Rocket Vault", async () => { let expectedLpToken; let expectedLpTokenAmount; let rocketVault; + let rETH; + + // slippage upon getAmountEth conversion + const conversionDelta = ethers.utils.parseEther("0.00000001"); beforeEach(async () => { // initialize fixture values @@ -26,16 +30,243 @@ describe("Rocket Vault", async () => { depositAmount = ethers.utils.parseEther("1.0"); expectedEthAmount = ethers.utils.parseEther("1.0"); expectedLpToken = "0xae78736Cd615f374D3085123A210448E74Fc6393"; // rETH token address - rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); + rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount equivalent to 1 ETH + rETH = await ethers.getContractAt("RocketTokenRETHInterface", expectedLpToken); + + // impersonate user and redeem rETH to make room for testing + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: ["0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"], + }); + const signer = await ethers.getSigner("0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"); + + const burnAmount = ethers.utils.parseUnits("5.0"); // 5 rETH + + const approvetx = await rETH.connect(signer).approve(rETH.address, burnAmount); + await approvetx.wait(); + + const burntx = await rETH.connect(signer).burn(burnAmount); + await burntx.wait(); }); it("Should reflect correct conversion from lp tokens to ETH", async () => { - expect(await rocketVault.getAmountETH(expectedLpTokenAmount)).to.be.equal(expectedEthAmount); + expect(await rocketVault.getAmountETH(expectedLpTokenAmount)).to.be.closeTo(expectedEthAmount, conversionDelta); }); it("Should reflect correct conversion from ETH to lp tokens", async () => { expect(await rocketVault.getAmountLpTokens(expectedEthAmount)).to.be.equal(expectedLpTokenAmount); }); + it("Should return the correct lp token", async () => { + expect(await rocketVault.getLpToken()).to.equal(expectedLpToken); + }); + + it("Should deposit specified amount and reflect correct balance", async () => { + // user info prior to deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // user info after deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + }); + + it("Should withdraw specified amount and reflect correct balance", async () => { + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); + + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + + // fast forward through withdrawal time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // initial withdrawal + tx = await vaultAccounting.connect(user).withdraw(expectedLpTokenAmount, vaultNames.rocketVault); + await tx.wait(); + + // user info after withdrawal + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.empty)).to.equal(0); + expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); + }); + + it("Should transfer funds to new vault", async () => { + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + + // deploy new vault + const EmptyVault = await ethers.getContractFactory("EmptyVault"); + const newEmptyVault = await EmptyVault.deploy(vaultManagement.address); + await newEmptyVault.deployed(); + + // fast forward through withdrawal time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // upgrade vault + tx = await vaultManagement.upgradeVault(vaultNames.rocketVault, newEmptyVault.address); + await tx.wait(); + + //verify old vault is empty + expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); + + //verify new vault balance + expect(await ethers.provider.getBalance(newEmptyVault.address)).to.be.closeTo(expectedEthAmount, conversionDelta); + + // verify user balance has not changed + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); + }); + + it("Should allow LP token deposits", async () => { + const depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; + const depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); + + // set up the deposit + tx = await depositPool.connect(user).deposit({ value: depositAmount }); + await tx.wait(); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // approve rETH to vault + approvetx = await rETH.connect(user).approve(rocketVault.address, expectedLpTokenAmount); + await approvetx.wait(); + + // deposit rETH into rocket vault + tx = await vaultAccounting.connect(user).depositLpToken(vaultNames.rocketVault, expectedLpTokenAmount); + await tx.wait(); + + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); + + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + }); + + it("Should allow LP token withdrawals", async () => { + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); //tiny slippage + + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // initiate the lp withdrawal + tx = await vaultAccounting.connect(user).withdrawLpToken(expectedLpTokenAmount, vaultNames.rocketVault); + await tx.wait(); + + // check user info after lp withdrawal + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.empty)).to.equal(0); + + expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); + expect(await rETH.balanceOf(user.address)).to.equal(expectedLpTokenAmount); + }); + + it("should fail to deposit without passing through diamond", async () => { + await expect(rocketVault.connect(user).deposit({ value: depositAmount })).to.be.revertedWith( + "Only diamond can call function" + ); + }); + + it("should fail to withdraw without passing through diamond", async () => { + await expect(rocketVault.connect(user).withdraw(depositAmount, user.address)).to.be.revertedWith( + "Only diamond can call function" + ); + }); + + it("should fail to transfer funds without passing through diamond", async () => { + await expect(rocketVault.connect(user).transferFunds(user.address)).to.be.revertedWith( + "Only diamond can call function" + ); + }); + + it("Should fail to deposit LP tokens without passing through diamond", async () => { + const depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; + const depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); + + // set up the deposit + tx = await depositPool.connect(user).deposit({ value: depositAmount }); + await tx.wait(); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // approve rETH to vault + approvetx = await rETH.connect(user).approve(rocketVault.address, expectedLpTokenAmount); + await approvetx.wait(); + + await expect(rocketVault.connect(user).depositLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + "Only diamond can call function" + ); + }); + + it("Should fail to withdraw LP tokens without passing through diamond", async () => { + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); //tiny slippage + + expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + await expect(rocketVault.connect(user).withdrawLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + "Only diamond can call function" + ); + }); }); From 41400926e719a709ae505a30403b4b31b43e1d0c Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 4 May 2022 20:11:46 -0400 Subject: [PATCH 22/70] set forking to true --- hardhat.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.js b/hardhat.config.js index 23b87a4..2bf5dbd 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -19,7 +19,7 @@ module.exports = { hardhat: { forking: { url: MAINNET_URL, - enabled: false, + // enabled: true, }, }, rinkeby: { From 99a06296a00753f0c849d5957804c822bc4345d4 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Thu, 5 May 2022 12:15:38 +0100 Subject: [PATCH 23/70] Skip tests if not fork --- test/vaults/RocketVault.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index b237cfe..b73014b 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -1,9 +1,16 @@ +const hre = require("hardhat"); const { ethers } = require("hardhat"); const { expect } = require("chai"); const { fixture } = require("../fixture"); describe("Rocket Vault", async () => { + before(async function () { + if (!hre.network.config.forking || !hre.network.config.forking.enabled) { + this.skip(); + } + }); + // fixture values let deployer, user; let vaultAccounting; From 0bec6bd9d5bc796a314c8b6a7b80f41a3abb6bbb Mon Sep 17 00:00:00 2001 From: Oozyx Date: Thu, 5 May 2022 12:17:25 +0100 Subject: [PATCH 24/70] Network fork set to false --- hardhat.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hardhat.config.js b/hardhat.config.js index 2bf5dbd..23b87a4 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -19,7 +19,7 @@ module.exports = { hardhat: { forking: { url: MAINNET_URL, - // enabled: true, + enabled: false, }, }, rinkeby: { From 17c9df2eb5772ff7c9cb09df76246d4958372e1b Mon Sep 17 00:00:00 2001 From: Oozyx Date: Thu, 5 May 2022 14:52:59 +0100 Subject: [PATCH 25/70] Remove vault coverage --- .solcover.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.solcover.js b/.solcover.js index cf634fc..2374617 100644 --- a/.solcover.js +++ b/.solcover.js @@ -1,3 +1,3 @@ module.exports = { - skipFiles: ["vendor/", "test/", "interfaces/"], + skipFiles: ["vendor/", "test/", "interfaces/", "vaults/"], }; From ff5bfdfbc1964170cd818b5dd19d508f1c85b826 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Fri, 6 May 2022 15:35:48 +0100 Subject: [PATCH 26/70] Add test variables --- test/vaults/RocketVault.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index b73014b..10a549b 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -24,6 +24,9 @@ describe("Rocket Vault", async () => { let expectedLpTokenAmount; let rocketVault; let rETH; + let impersonateAddress; + let depositPoolAddress; + let depositPool; // slippage upon getAmountEth conversion const conversionDelta = ethers.utils.parseEther("0.00000001"); @@ -40,13 +43,16 @@ describe("Rocket Vault", async () => { rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount equivalent to 1 ETH rETH = await ethers.getContractAt("RocketTokenRETHInterface", expectedLpToken); + impersonateAddress = "0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"; + depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; + depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); // impersonate user and redeem rETH to make room for testing await hre.network.provider.request({ method: "hardhat_impersonateAccount", - params: ["0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"], + params: [impersonateAddress], }); - const signer = await ethers.getSigner("0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"); + const signer = await ethers.getSigner(impersonateAddress); const burnAmount = ethers.utils.parseUnits("5.0"); // 5 rETH @@ -157,9 +163,6 @@ describe("Rocket Vault", async () => { }); it("Should allow LP token deposits", async () => { - const depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; - const depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); - // set up the deposit tx = await depositPool.connect(user).deposit({ value: depositAmount }); await tx.wait(); @@ -235,9 +238,6 @@ describe("Rocket Vault", async () => { }); it("Should fail to deposit LP tokens without passing through diamond", async () => { - const depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; - const depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); - // set up the deposit tx = await depositPool.connect(user).deposit({ value: depositAmount }); await tx.wait(); From 559aac6180c98a66a2e7a8cbc43edb2d1630b34e Mon Sep 17 00:00:00 2001 From: Oozyx Date: Mon, 9 May 2022 18:34:32 +0100 Subject: [PATCH 27/70] Add feature info --- architectural_changes.txt | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 architectural_changes.txt diff --git a/architectural_changes.txt b/architectural_changes.txt new file mode 100644 index 0000000..75ee4a9 --- /dev/null +++ b/architectural_changes.txt @@ -0,0 +1,22 @@ +Architectural changes: + +- Add new parameter to IVault functions: bytes32 _data, optional to be used by the vault. In this case, pass user + address. +- When depositing, create new proxy contract from vault associated with each depositor. This proxy contract will deposit + into rocket pool. +- Create a mapping of user address to proxy contract. +- When withdrawing, withdraw from user's proxy contract. +- If user has an existing proxy contract associated, then reuse. +- Problem: how to upgrade vault? +- Possible solution + 3 contracts: + - RocketPoolVault (implements IVault) + Sets storage variable in RocketPoolVaultStorage (currentImplementation + - RocketPoolVaultStorage + Holds storage variable currentImplementation + Holds mapping of user to delegate contracts + - RocketPoolDelegate (2 functions, deposit, withdraw) + Holds instance of RocketPoolVaultStorage + Function modifier on both functions currentVaultImplementation + currentVaultImplementation checks value stored in RocketPoolVaultStorage + When upgrading, currentImplementation in RocketPoolVaultStorage gets set to new vault implementation From 63c503985be8921e173027865384a3728a8b7723 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 11 May 2022 17:48:51 -0400 Subject: [PATCH 28/70] initial commit --- contracts/interfaces/vaults/IDelegate.sol | 36 +++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 contracts/interfaces/vaults/IDelegate.sol diff --git a/contracts/interfaces/vaults/IDelegate.sol b/contracts/interfaces/vaults/IDelegate.sol new file mode 100644 index 0000000..1e04f5a --- /dev/null +++ b/contracts/interfaces/vaults/IDelegate.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +interface IDelegate { + /** + * @dev Deposits ETH and converts to vault's LP token + * + * @return The amount of LP tokens received from ETH deposit + */ + function deposit() external payable returns (uint256); + + /** + * @dev Deposits LP token directly into vault + * + * @param amount The amount of LP tokens to deposit + * @param user The user depositing + */ + function depositLpToken(uint256 amount, address user) external; + + /** + * @dev Converts LP token and withdraws as ETH + * + * @param lpTokenAmount The amount of LP tokens to withdraw before converting + * @param recipient The recipient of the converted ETH + * @return The amount of ETH withdrawn + */ + function withdraw(uint256 lpTokenAmount, address payable recipient) external returns (uint256); + + /** + * @dev Withdraws LP token directly from vault + * + * @param lpTokenAmount The amount of LP tokens to withdraw + * @param recipient The recipient of the LP tokens + */ + function withdrawLpToken(uint256 lpTokenAmount, address recipient) external; +} From e3e9c510ebab934e5dbf8d5d9e89ebedaeb0990f Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 11 May 2022 17:49:09 -0400 Subject: [PATCH 29/70] initial commit --- contracts/interfaces/vaults/RocketPoolVaultStorage.sol | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 contracts/interfaces/vaults/RocketPoolVaultStorage.sol diff --git a/contracts/interfaces/vaults/RocketPoolVaultStorage.sol b/contracts/interfaces/vaults/RocketPoolVaultStorage.sol new file mode 100644 index 0000000..de631c2 --- /dev/null +++ b/contracts/interfaces/vaults/RocketPoolVaultStorage.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +interface RocketPoolVaultStorage { + function getCurrentImplementation() external view returns (address); +} From cda310bbaa0d7a49ce4de63887728db0dd21be4b Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 11 May 2022 17:49:24 -0400 Subject: [PATCH 30/70] initial commit --- contracts/vaults/BaseDelegate.sol | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 contracts/vaults/BaseDelegate.sol diff --git a/contracts/vaults/BaseDelegate.sol b/contracts/vaults/BaseDelegate.sol new file mode 100644 index 0000000..7523410 --- /dev/null +++ b/contracts/vaults/BaseDelegate.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { IDelegate } from "../interfaces/vaults/IDelegate.sol"; +import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; +import { RocketPoolVaultStorage } from "../interfaces/vaults/RocketPoolVaultStorage.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; + +abstract contract BaseDelegate is IDelegate, ReentrancyGuard { + RocketStorageInterface rocketStorage; + RocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation + + constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) { + rocketStorage = RocketStorageInterface(_rocketStorageAddress); + rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketPoolVaultStorageAddress); + } + + modifier onlyCurrentImplementation() { + address currentVaultImplementation = rocketPoolVaultStorage.getCurrentImplementation(); + require(msg.sender == currentVaultImplementation, "Only the current implementation can call function"); + _; + } + + receive() external payable {} +} From a0e3fe2b8148f703b5c1505de9166c454863f7ef Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 11 May 2022 17:49:48 -0400 Subject: [PATCH 31/70] initial commit --- contracts/vaults/RocketPoolDelegate.sol | 85 +++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 contracts/vaults/RocketPoolDelegate.sol diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol new file mode 100644 index 0000000..25570b4 --- /dev/null +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { BaseDelegate } from "./BaseDelegate.sol"; +import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; +import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; + +contract RocketVaultDelegate is BaseDelegate { + constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) + BaseDelegate(_rocketStorageAddress, _rocketPoolVaultStorageAddress) + { + // do we need to store the depositor's address? + } + + function deposit() external payable override onlyCurrentImplementation nonReentrant returns (uint256) { + // Load contracts + address depositPoolAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketDepositPool")) + ); + RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(depositPoolAddress); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + + // Get amount of tokens before and after to determine how many were minted + uint256 balanceBefore = rETH.balanceOf(address(this)); + depositPool.deposit{ value: msg.value }(); + uint256 balanceAfter = rETH.balanceOf(address(this)); + + uint256 sharesMinted = balanceAfter - balanceBefore; + + return sharesMinted; + } + + function depositLpToken(uint256 amount, address user) external override onlyCurrentImplementation nonReentrant { + // Load rETH contract + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + + rETH.transferFrom(user, address(this), amount); + } + + function withdraw(uint256 lpTokenAmount, address payable recipient) + external + override + onlyCurrentImplementation + nonReentrant + returns (uint256) + { + // Load contracts + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + + // Redeem rETH for ETH and send to recipient + uint256 balanceBefore = address(this).balance; + rETH.burn(lpTokenAmount); + uint256 balanceAfter = address(this).balance; + + uint256 ethReturned = balanceAfter - balanceBefore; + + recipient.transfer(ethReturned); + + return ethReturned; + } + + function withdrawLpToken(uint256 lpTokenAmount, address recipient) + external + override + onlyCurrentImplementation + nonReentrant + { + // Load rETH contract + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + + rETH.transfer(recipient, lpTokenAmount); + } +} From 6b3a1bfad3e9c3524be5da01458a7f3fd72cd4a4 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:30:42 -0400 Subject: [PATCH 32/70] add _data param --- contracts/interfaces/vaults/IVault.sol | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/contracts/interfaces/vaults/IVault.sol b/contracts/interfaces/vaults/IVault.sol index 4224530..f2fc1b8 100644 --- a/contracts/interfaces/vaults/IVault.sol +++ b/contracts/interfaces/vaults/IVault.sol @@ -5,34 +5,51 @@ interface IVault { /** * @dev Deposits ETH and converts to vault's LP token * + * @param _data The user address * @return The amount of LP tokens received from ETH deposit */ - function deposit() external payable returns (uint256); + function deposit(bytes32 _data) external payable returns (uint256); /** * @dev Deposits LP token directly into vault * + * @param _data The user address + * @param _data The user address * @param amount The amount of LP tokens to deposit * @param user The user depositing */ - function depositLpToken(uint256 amount, address user) external; + function depositLpToken( + bytes32 _data, + uint256 amount, + address user + ) external; /** * @dev Converts LP token and withdraws as ETH * + * @param _data The user address * @param lpTokenAmount The amount of LP tokens to withdraw before converting * @param recipient The recipient of the converted ETH * @return The amount of ETH withdrawn */ - function withdraw(uint256 lpTokenAmount, address payable recipient) external returns (uint256); + function withdraw( + bytes32 _data, + uint256 lpTokenAmount, + address payable recipient + ) external returns (uint256); /** * @dev Withdraws LP token directly from vault * + * @param _data The user address * @param lpTokenAmount The amount of LP tokens to withdraw * @param recipient The recipient of the LP tokens */ - function withdrawLpToken(uint256 lpTokenAmount, address recipient) external; + function withdrawLpToken( + bytes32 _data, + uint256 lpTokenAmount, + address recipient + ) external; /** * @dev Transfers LP tokens to new vault From c50c92d3be924522b849816f75c677d82f9a067c Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:31:15 -0400 Subject: [PATCH 33/70] changed file name --- contracts/interfaces/vaults/RocketPoolVaultStorage.sol | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 contracts/interfaces/vaults/RocketPoolVaultStorage.sol diff --git a/contracts/interfaces/vaults/RocketPoolVaultStorage.sol b/contracts/interfaces/vaults/RocketPoolVaultStorage.sol deleted file mode 100644 index de631c2..0000000 --- a/contracts/interfaces/vaults/RocketPoolVaultStorage.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-only -pragma solidity ^0.8.0; - -interface RocketPoolVaultStorage { - function getCurrentImplementation() external view returns (address); -} From f2c79ebd9931a404032f933e77765e72b4c7cbe7 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:31:26 -0400 Subject: [PATCH 34/70] removed --- contracts/vaults/BaseDelegate.sol | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 contracts/vaults/BaseDelegate.sol diff --git a/contracts/vaults/BaseDelegate.sol b/contracts/vaults/BaseDelegate.sol deleted file mode 100644 index 7523410..0000000 --- a/contracts/vaults/BaseDelegate.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; - -import { IDelegate } from "../interfaces/vaults/IDelegate.sol"; -import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; -import { RocketPoolVaultStorage } from "../interfaces/vaults/RocketPoolVaultStorage.sol"; -import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; - -abstract contract BaseDelegate is IDelegate, ReentrancyGuard { - RocketStorageInterface rocketStorage; - RocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation - - constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) { - rocketStorage = RocketStorageInterface(_rocketStorageAddress); - rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketPoolVaultStorageAddress); - } - - modifier onlyCurrentImplementation() { - address currentVaultImplementation = rocketPoolVaultStorage.getCurrentImplementation(); - require(msg.sender == currentVaultImplementation, "Only the current implementation can call function"); - _; - } - - receive() external payable {} -} From 4f8ca96e5ead603626793ef8e8d53c9875abab36 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:31:55 -0400 Subject: [PATCH 35/70] move BaseDelegate code here --- contracts/vaults/RocketPoolDelegate.sol | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol index 25570b4..b24f31a 100644 --- a/contracts/vaults/RocketPoolDelegate.sol +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -1,15 +1,26 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import { BaseDelegate } from "./BaseDelegate.sol"; import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; +import { IDelegate } from "../interfaces/vaults/IDelegate.sol"; +import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; +import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -contract RocketVaultDelegate is BaseDelegate { - constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) - BaseDelegate(_rocketStorageAddress, _rocketPoolVaultStorageAddress) - { - // do we need to store the depositor's address? +contract RocketPoolDelegate is IDelegate, ReentrancyGuard { + RocketStorageInterface rocketStorage; + IRocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation + + modifier onlyCurrentImplementation() { + address currentVaultImplementation = rocketPoolVaultStorage.getCurrentImplementation(); + require(msg.sender == currentVaultImplementation, "Only the current implementation can call function"); + _; + } + + constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) { + rocketStorage = RocketStorageInterface(_rocketStorageAddress); + rocketPoolVaultStorage = IRocketPoolVaultStorage(_rocketPoolVaultStorageAddress); } function deposit() external payable override onlyCurrentImplementation nonReentrant returns (uint256) { From f753bf54b207a0cdc51fb0b1cf24c81357f21c45 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:32:42 -0400 Subject: [PATCH 36/70] add _data param and make deposit/withdraw changes --- contracts/vaults/RocketPoolVault.sol | 134 +++++++++++++++------------ 1 file changed, 73 insertions(+), 61 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index fd0e510..e1e6daa 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -6,91 +6,103 @@ import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPo import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; import { RocketVaultInterface } from "../interfaces/vaults/RocketVaultInterface.sol"; +import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; +import { RocketPoolDelegate } from "./RocketPoolDelegate.sol"; contract RocketVault is BaseVault { // stores state for Rocket Protocol RocketStorageInterface rocketStorage; - constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { + // storage for Rocket Vault + RocketPoolVaultStorage rocketPoolVaultStorage; + + constructor( + address _diamond, + address _rocketStorageAddress, + address _rocketPoolVaultStorageAddress + ) BaseVault(_diamond) { rocketStorage = RocketStorageInterface(_rocketStorageAddress); + rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketPoolVaultStorageAddress); } - function deposit() external payable override onlyDiamond nonReentrant returns (uint256) { - // Load contracts - address depositPoolAddress = rocketStorage.getAddress( - keccak256(abi.encodePacked("contract.address", "rocketDepositPool")) - ); - RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(depositPoolAddress); - address rethAddress = rocketStorage.getAddress( - keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) - ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - - // Get amount of tokens before and after to determine how many were minted - uint256 balanceBefore = rETH.balanceOf(address(this)); - depositPool.deposit{ value: msg.value }(); - uint256 balanceAfter = rETH.balanceOf(address(this)); - - uint256 sharesMinted = balanceAfter - balanceBefore; - - return sharesMinted; + function deposit(bytes32 _data) external payable override onlyDiamond nonReentrant returns (uint256) { + // retrive delegate address + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + + // if delegate doesn't exist, create new + // if delegate exists, use + if (delegateAddress == address(0)) { + RocketPoolDelegate newDelegate = new RocketPoolDelegate( + address(rocketStorage), + address(rocketPoolVaultStorage) + ); + // stores new user's delegate address + rocketPoolVaultStorage.setDelegateAddress(_data, address(newDelegate)); + return newDelegate.deposit{ value: msg.value }(); + } else { + RocketPoolDelegate existingDelegate = RocketPoolDelegate(delegateAddress); + return existingDelegate.deposit{ value: msg.value }(); + } } - function depositLpToken(uint256 amount, address user) external override onlyDiamond nonReentrant { + function depositLpToken( + bytes32 _data, + uint256 amount, + address user + ) external override onlyDiamond nonReentrant { // Load rETH contract address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - rETH.transferFrom(user, address(this), amount); + // retrive delegate address + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + + // if doesn't exist, create new delegate + // if delegate exists, use + if (delegateAddress == address(0)) { + RocketPoolDelegate newDelegate = new RocketPoolDelegate( + address(rocketStorage), + address(rocketPoolVaultStorage) + ); + // stores new user's delegate address + rocketPoolVaultStorage.setDelegateAddress(_data, address(newDelegate)); + rETH.transferFrom(user, address(newDelegate), amount); + } else { + RocketPoolDelegate existingDelegate = RocketPoolDelegate(delegateAddress); + rETH.transferFrom(user, address(existingDelegate), amount); + } } - function withdraw(uint256 lpTokenAmount, address payable recipient) - external - override - onlyDiamond - nonReentrant - returns (uint256) - { - // Load contracts - address rethAddress = rocketStorage.getAddress( - keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) - ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - - // Redeem rETH for ETH and send to recipient - uint256 balanceBefore = address(this).balance; - rETH.burn(lpTokenAmount); - uint256 balanceAfter = address(this).balance; - - uint256 ethReturned = balanceAfter - balanceBefore; - - recipient.transfer(ethReturned); + function withdraw( + bytes32 _data, + uint256 lpTokenAmount, + address payable recipient + ) external override onlyDiamond nonReentrant returns (uint256) { + // retrive delegate address + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + RocketPoolDelegate delegate = RocketPoolDelegate(delegateAddress); - return ethReturned; + return delegate.withdraw(lpTokenAmount, payable(recipient)); } - function withdrawLpToken(uint256 lpTokenAmount, address recipient) external override onlyDiamond nonReentrant { - // Load rETH contract - address rethAddress = rocketStorage.getAddress( - keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) - ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + function withdrawLpToken( + bytes32 _data, + uint256 lpTokenAmount, + address recipient + ) external override onlyDiamond nonReentrant { + // retrive delegate address + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + RocketPoolDelegate delegate = RocketPoolDelegate(delegateAddress); - rETH.transfer(recipient, lpTokenAmount); + delegate.withdrawLpToken(lpTokenAmount, recipient); } - function transferFunds(address payable newVaultAddress) external override onlyDiamond { - // Load rETH contract - address rethAddress = rocketStorage.getAddress( - keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) - ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); - - // redeem all rETH - rETH.burn(rETH.balanceOf(address(this))); - newVaultAddress.transfer(address(this).balance); + function transferFunds(address payable newImplementation) external override onlyDiamond { + // upgrade current implementation to new implementation? + // if so, just need to update in RocketPoolVaultStorage + rocketPoolVaultStorage.setNewImplementation(newImplementation); } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { From 713f2aa5c841bc2d844a41cbee104d5504520624 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:39:58 -0400 Subject: [PATCH 37/70] fix import and storage interface --- contracts/vaults/RocketPoolVault.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index e1e6daa..d5d2f3c 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -6,7 +6,7 @@ import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPo import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; import { RocketVaultInterface } from "../interfaces/vaults/RocketVaultInterface.sol"; -import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; +import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; import { RocketPoolDelegate } from "./RocketPoolDelegate.sol"; contract RocketVault is BaseVault { @@ -14,7 +14,7 @@ contract RocketVault is BaseVault { RocketStorageInterface rocketStorage; // storage for Rocket Vault - RocketPoolVaultStorage rocketPoolVaultStorage; + IRocketPoolVaultStorage rocketPoolVaultStorage; constructor( address _diamond, @@ -22,7 +22,7 @@ contract RocketVault is BaseVault { address _rocketPoolVaultStorageAddress ) BaseVault(_diamond) { rocketStorage = RocketStorageInterface(_rocketStorageAddress); - rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketPoolVaultStorageAddress); + rocketPoolVaultStorage = IRocketPoolVaultStorage(_rocketPoolVaultStorageAddress); } function deposit(bytes32 _data) external payable override onlyDiamond nonReentrant returns (uint256) { From a9e70892fe155a0df4f9a4ff3a3d7c09f826967a Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:40:14 -0400 Subject: [PATCH 38/70] initial commit --- .../interfaces/vaults/IRocketPoolVaultStorage.sol | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 contracts/interfaces/vaults/IRocketPoolVaultStorage.sol diff --git a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol new file mode 100644 index 0000000..bb2ddb4 --- /dev/null +++ b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +interface IRocketPoolVaultStorage { + function getCurrentImplementation() external view returns (address); + + function setNewImplementation(address newImplementation) external; + + function setDelegateAddress(bytes32 _data, address delegateAddress) external; + + function getDelegateAddress(bytes32 _data) external view returns (address); +} From d6268e9e724b529de475a346a7f2630d1cf5c702 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:40:39 -0400 Subject: [PATCH 39/70] initial commit --- contracts/vaults/RocketPoolVaultStorage.sol | 37 +++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 contracts/vaults/RocketPoolVaultStorage.sol diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol new file mode 100644 index 0000000..10a43b6 --- /dev/null +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { RocketVault } from "./RocketPoolVault.sol"; +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +contract RocketPoolVaultStorage is Ownable { + //stores the current vault implementation + address currentImplementation; + + mapping(bytes32 => address) delegate; + + modifier onlyCurrentImplementation() { + require(msg.sender == currentImplementation, "Only the current implementation can call function"); + _; + } + + function initialize(address _currentImplemntation) external onlyOwner { + currentImplementation = _currentImplemntation; + } + + function getCurrentImplementation() public view returns (address) { + return address(currentImplementation); + } + + function setNewImplementation(address newImplementation) external onlyCurrentImplementation { + currentImplementation = newImplementation; + } + + function setDelegateAddress(bytes32 _data, address delegateAddress) external onlyCurrentImplementation { + delegate[_data] = delegateAddress; + } + + function getDelegateAddress(bytes32 _data) external view returns (address) { + return delegate[_data]; + } +} From 766d4d999892d47a0b3c0e1837c1f82c055ecb14 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Fri, 13 May 2022 15:41:55 -0400 Subject: [PATCH 40/70] add comment --- contracts/vaults/RocketPoolVaultStorage.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index 10a43b6..608f4a8 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -16,6 +16,9 @@ contract RocketPoolVaultStorage is Ownable { } function initialize(address _currentImplemntation) external onlyOwner { + // initialize instead of constructor + // need this contract address for RocketPoolVault deployment + // will pass in current implementation after currentImplementation = _currentImplemntation; } From f8082f69e1dd3629e6e4f804a7a31bdb23451109 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:34:50 -0400 Subject: [PATCH 41/70] change interface names --- .../{IDelegate.sol => IRocketDelegate.sol} | 2 +- ...olInterface.sol => IRocketDepositPool.sol} | 7 ++--- ...torageInterface.sol => IRocketStorage.sol} | 6 ++-- ...RETHInterface.sol => IRocketTokenRETH.sol} | 4 +-- ...ketVaultInterface.sol => IRocketVault.sol} | 29 +++++++++++++++---- 5 files changed, 32 insertions(+), 16 deletions(-) rename contracts/interfaces/vaults/{IDelegate.sol => IRocketDelegate.sol} (97%) rename contracts/interfaces/vaults/{RocketDepositPoolInterface.sol => IRocketDepositPool.sol} (81%) rename contracts/interfaces/vaults/{RocketStorageInterface.sol => IRocketStorage.sol} (76%) rename contracts/interfaces/vaults/{RocketTokenRETHInterface.sol => IRocketTokenRETH.sol} (89%) rename contracts/interfaces/vaults/{RocketVaultInterface.sol => IRocketVault.sol} (56%) diff --git a/contracts/interfaces/vaults/IDelegate.sol b/contracts/interfaces/vaults/IRocketDelegate.sol similarity index 97% rename from contracts/interfaces/vaults/IDelegate.sol rename to contracts/interfaces/vaults/IRocketDelegate.sol index 1e04f5a..f4d6e00 100644 --- a/contracts/interfaces/vaults/IDelegate.sol +++ b/contracts/interfaces/vaults/IRocketDelegate.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -interface IDelegate { +interface IRocketDelegate { /** * @dev Deposits ETH and converts to vault's LP token * diff --git a/contracts/interfaces/vaults/RocketDepositPoolInterface.sol b/contracts/interfaces/vaults/IRocketDepositPool.sol similarity index 81% rename from contracts/interfaces/vaults/RocketDepositPoolInterface.sol rename to contracts/interfaces/vaults/IRocketDepositPool.sol index 7723715..296c773 100644 --- a/contracts/interfaces/vaults/RocketDepositPoolInterface.sol +++ b/contracts/interfaces/vaults/IRocketDepositPool.sol @@ -1,9 +1,8 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; -interface RocketDepositPoolInterface { - +interface IRocketDepositPool { function deposit() external payable; - + function getUserLastDepositBlock(address _address) external view returns (uint256); -} \ No newline at end of file +} diff --git a/contracts/interfaces/vaults/RocketStorageInterface.sol b/contracts/interfaces/vaults/IRocketStorage.sol similarity index 76% rename from contracts/interfaces/vaults/RocketStorageInterface.sol rename to contracts/interfaces/vaults/IRocketStorage.sol index fe5a876..a89efe1 100644 --- a/contracts/interfaces/vaults/RocketStorageInterface.sol +++ b/contracts/interfaces/vaults/IRocketStorage.sol @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-3.0-only pragma solidity ^0.8.0; -interface RocketStorageInterface { - +interface IRocketStorage { function getAddress(bytes32 _key) external view returns (address); - -} \ No newline at end of file +} diff --git a/contracts/interfaces/vaults/RocketTokenRETHInterface.sol b/contracts/interfaces/vaults/IRocketTokenRETH.sol similarity index 89% rename from contracts/interfaces/vaults/RocketTokenRETHInterface.sol rename to contracts/interfaces/vaults/IRocketTokenRETH.sol index 02f5570..fdb5011 100644 --- a/contracts/interfaces/vaults/RocketTokenRETHInterface.sol +++ b/contracts/interfaces/vaults/IRocketTokenRETH.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -interface RocketTokenRETHInterface is IERC20 { +interface IRocketTokenRETH is IERC20 { function getEthValue(uint256 _rethAmount) external view returns (uint256); function getRethValue(uint256 _ethAmount) external view returns (uint256); @@ -11,4 +11,4 @@ interface RocketTokenRETHInterface is IERC20 { function mint(uint256 _ethAmount, address _to) external; function burn(uint256 _rethAmount) external; -} \ No newline at end of file +} diff --git a/contracts/interfaces/vaults/RocketVaultInterface.sol b/contracts/interfaces/vaults/IRocketVault.sol similarity index 56% rename from contracts/interfaces/vaults/RocketVaultInterface.sol rename to contracts/interfaces/vaults/IRocketVault.sol index 8a433fc..71b0693 100644 --- a/contracts/interfaces/vaults/RocketVaultInterface.sol +++ b/contracts/interfaces/vaults/IRocketVault.sol @@ -4,13 +4,32 @@ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; -interface RocketVaultInterface { +interface IRocketVault { function balanceOf(string memory _networkContractName) external view returns (uint256); + function depositEther() external payable; + function withdrawEther(uint256 _amount) external; - function depositToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; - function withdrawToken(address _withdrawalAddress, IERC20 _tokenAddress, uint256 _amount) external; + + function depositToken( + string memory _networkContractName, + IERC20 _tokenAddress, + uint256 _amount + ) external; + + function withdrawToken( + address _withdrawalAddress, + IERC20 _tokenAddress, + uint256 _amount + ) external; + function balanceOfToken(string memory _networkContractName, IERC20 _tokenAddress) external view returns (uint256); - function transferToken(string memory _networkContractName, IERC20 _tokenAddress, uint256 _amount) external; + + function transferToken( + string memory _networkContractName, + IERC20 _tokenAddress, + uint256 _amount + ) external; + function burnToken(ERC20Burnable _tokenAddress, uint256 _amount) external; -} \ No newline at end of file +} From 2552e9fbff6ccfdaeb7c2ecc09015fa72e21b818 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:36:25 -0400 Subject: [PATCH 42/70] fix param description and order --- contracts/interfaces/vaults/IVault.sol | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/contracts/interfaces/vaults/IVault.sol b/contracts/interfaces/vaults/IVault.sol index f2fc1b8..b0b0ef4 100644 --- a/contracts/interfaces/vaults/IVault.sol +++ b/contracts/interfaces/vaults/IVault.sol @@ -5,7 +5,7 @@ interface IVault { /** * @dev Deposits ETH and converts to vault's LP token * - * @param _data The user address + * @param _data Additional user data if needed * @return The amount of LP tokens received from ETH deposit */ function deposit(bytes32 _data) external payable returns (uint256); @@ -13,42 +13,41 @@ interface IVault { /** * @dev Deposits LP token directly into vault * - * @param _data The user address - * @param _data The user address * @param amount The amount of LP tokens to deposit * @param user The user depositing + * @param _data Additional user data if needed */ function depositLpToken( - bytes32 _data, uint256 amount, - address user + address user, + bytes32 _data ) external; /** * @dev Converts LP token and withdraws as ETH * - * @param _data The user address * @param lpTokenAmount The amount of LP tokens to withdraw before converting * @param recipient The recipient of the converted ETH + * @param _data Additional user data if needed * @return The amount of ETH withdrawn */ function withdraw( - bytes32 _data, uint256 lpTokenAmount, - address payable recipient + address payable recipient, + bytes32 _data ) external returns (uint256); /** * @dev Withdraws LP token directly from vault * - * @param _data The user address * @param lpTokenAmount The amount of LP tokens to withdraw * @param recipient The recipient of the LP tokens + * @param _data Additional user data if needed */ function withdrawLpToken( - bytes32 _data, uint256 lpTokenAmount, - address recipient + address recipient, + bytes32 _data ) external; /** From 6b9c84f7606330c8efef98dd106bbd47ad087b8b Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:37:01 -0400 Subject: [PATCH 43/70] fix imports --- contracts/vaults/RocketPoolDelegate.sol | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol index b24f31a..458352c 100644 --- a/contracts/vaults/RocketPoolDelegate.sol +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -1,15 +1,16 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; -import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; -import { IDelegate } from "../interfaces/vaults/IDelegate.sol"; -import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; +import { IRocketDepositPool } from "../interfaces/vaults/IRocketDepositPool.sol"; +import { IRocketTokenRETH } from "../interfaces/vaults/IRocketTokenRETH.sol"; +import { IRocketDelegate } from "../interfaces/vaults/IRocketDelegate.sol"; +import { IRocketStorage } from "../interfaces/vaults/IRocketStorage.sol"; import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; + import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -contract RocketPoolDelegate is IDelegate, ReentrancyGuard { - RocketStorageInterface rocketStorage; +contract RocketPoolDelegate is IRocketDelegate, ReentrancyGuard { + IRocketStorage rocketStorage; IRocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation modifier onlyCurrentImplementation() { @@ -19,7 +20,7 @@ contract RocketPoolDelegate is IDelegate, ReentrancyGuard { } constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) { - rocketStorage = RocketStorageInterface(_rocketStorageAddress); + rocketStorage = IRocketStorage(_rocketStorageAddress); rocketPoolVaultStorage = IRocketPoolVaultStorage(_rocketPoolVaultStorageAddress); } @@ -28,11 +29,11 @@ contract RocketPoolDelegate is IDelegate, ReentrancyGuard { address depositPoolAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketDepositPool")) ); - RocketDepositPoolInterface depositPool = RocketDepositPoolInterface(depositPoolAddress); + IRocketDepositPool depositPool = IRocketDepositPool(depositPoolAddress); address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); // Get amount of tokens before and after to determine how many were minted uint256 balanceBefore = rETH.balanceOf(address(this)); @@ -49,7 +50,7 @@ contract RocketPoolDelegate is IDelegate, ReentrancyGuard { address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); rETH.transferFrom(user, address(this), amount); } @@ -65,7 +66,7 @@ contract RocketPoolDelegate is IDelegate, ReentrancyGuard { address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); // Redeem rETH for ETH and send to recipient uint256 balanceBefore = address(this).balance; @@ -89,7 +90,7 @@ contract RocketPoolDelegate is IDelegate, ReentrancyGuard { address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); rETH.transfer(recipient, lpTokenAmount); } From 2c7d2533f8da63d97e644fb6dce22b4ca1e225d4 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:38:02 -0400 Subject: [PATCH 44/70] fix imports, param order, and add vault storage creation --- contracts/vaults/RocketPoolVault.sol | 46 +++++++++++++--------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index d5d2f3c..48f0241 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -2,27 +2,23 @@ pragma solidity ^0.8.0; import { BaseVault } from "./BaseVault.sol"; -import { RocketDepositPoolInterface } from "../interfaces/vaults/RocketDepositPoolInterface.sol"; -import { RocketTokenRETHInterface } from "../interfaces/vaults/RocketTokenRETHInterface.sol"; -import { RocketStorageInterface } from "../interfaces/vaults/RocketStorageInterface.sol"; -import { RocketVaultInterface } from "../interfaces/vaults/RocketVaultInterface.sol"; -import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; +import { IRocketDepositPool } from "../interfaces/vaults/IRocketDepositPool.sol"; +import { IRocketTokenRETH } from "../interfaces/vaults/IRocketTokenRETH.sol"; +import { IRocketStorage } from "../interfaces/vaults/IRocketStorage.sol"; +import { IRocketVault } from "../interfaces/vaults/IRocketVault.sol"; +import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; import { RocketPoolDelegate } from "./RocketPoolDelegate.sol"; contract RocketVault is BaseVault { // stores state for Rocket Protocol - RocketStorageInterface rocketStorage; + IRocketStorage rocketStorage; // storage for Rocket Vault - IRocketPoolVaultStorage rocketPoolVaultStorage; - - constructor( - address _diamond, - address _rocketStorageAddress, - address _rocketPoolVaultStorageAddress - ) BaseVault(_diamond) { - rocketStorage = RocketStorageInterface(_rocketStorageAddress); - rocketPoolVaultStorage = IRocketPoolVaultStorage(_rocketPoolVaultStorageAddress); + RocketPoolVaultStorage rocketPoolVaultStorage; + + constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { + rocketStorage = IRocketStorage(_rocketStorageAddress); + rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); } function deposit(bytes32 _data) external payable override onlyDiamond nonReentrant returns (uint256) { @@ -46,15 +42,15 @@ contract RocketVault is BaseVault { } function depositLpToken( - bytes32 _data, uint256 amount, - address user + address user, + bytes32 _data ) external override onlyDiamond nonReentrant { // Load rETH contract address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); // retrive delegate address address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); @@ -76,9 +72,9 @@ contract RocketVault is BaseVault { } function withdraw( - bytes32 _data, uint256 lpTokenAmount, - address payable recipient + address payable recipient, + bytes32 _data ) external override onlyDiamond nonReentrant returns (uint256) { // retrive delegate address address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); @@ -88,9 +84,9 @@ contract RocketVault is BaseVault { } function withdrawLpToken( - bytes32 _data, uint256 lpTokenAmount, - address recipient + address recipient, + bytes32 _data ) external override onlyDiamond nonReentrant { // retrive delegate address address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); @@ -110,11 +106,11 @@ contract RocketVault is BaseVault { address rocketVaultAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketVault")) ); - RocketVaultInterface rocketVault = RocketVaultInterface(rocketVaultAddress); + IRocketVault rocketVault = IRocketVault(rocketVaultAddress); address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); uint256 ethAmount = rETH.getEthValue(lpTokenAmount); @@ -132,7 +128,7 @@ contract RocketVault is BaseVault { address rethAddress = rocketStorage.getAddress( keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) ); - RocketTokenRETHInterface rETH = RocketTokenRETHInterface(rethAddress); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); return rETH.getRethValue(ethAmount); } From de0a184ef5ad760e32f155d4861db441281daf73 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:38:50 -0400 Subject: [PATCH 45/70] remove initialize and ownable --- contracts/vaults/RocketPoolVaultStorage.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index 608f4a8..8ff283c 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -1,10 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -import { RocketVault } from "./RocketPoolVault.sol"; -import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; - -contract RocketPoolVaultStorage is Ownable { +contract RocketPoolVaultStorage { //stores the current vault implementation address currentImplementation; @@ -15,15 +12,12 @@ contract RocketPoolVaultStorage is Ownable { _; } - function initialize(address _currentImplemntation) external onlyOwner { - // initialize instead of constructor - // need this contract address for RocketPoolVault deployment - // will pass in current implementation after + constructor(address _currentImplemntation) { currentImplementation = _currentImplemntation; } function getCurrentImplementation() public view returns (address) { - return address(currentImplementation); + return currentImplementation; } function setNewImplementation(address newImplementation) external onlyCurrentImplementation { From da6db33b19bc7230a65036529f985ef80637f61b Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 17 May 2022 16:46:25 -0400 Subject: [PATCH 46/70] add comment --- contracts/vaults/RocketPoolVault.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index 48f0241..dc159c9 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -18,7 +18,7 @@ contract RocketVault is BaseVault { constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = IRocketStorage(_rocketStorageAddress); - rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); + rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); // creates instance of RocketPoolVaultStorage and sets current implemtation to this contract } function deposit(bytes32 _data) external payable override onlyDiamond nonReentrant returns (uint256) { From 0934d396f3e28ecfbc4800727e41f75f28b12cee Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:07:23 -0400 Subject: [PATCH 47/70] add address encoding --- contracts/facets/BidMarketFacet.sol | 2 +- contracts/facets/OptionMarketFacet.sol | 8 ++++++-- contracts/facets/VaultAccountingFacet.sol | 12 ++++++++---- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/contracts/facets/BidMarketFacet.sol b/contracts/facets/BidMarketFacet.sol index 6c537ed..707507b 100644 --- a/contracts/facets/BidMarketFacet.sol +++ b/contracts/facets/BidMarketFacet.sol @@ -152,7 +152,7 @@ contract BidMarketFacet is IBidMarket { // update user balance vs.userVaultBalances[bidInfo.bidder][bidInfo.bidInput.vault] -= bidLPTokenAmount; // withdraw funds from vault - uint256 ethReturned = vault.withdraw(bidLPTokenAmount, payable(this)); + uint256 ethReturned = vault.withdraw(bidLPTokenAmount, payable(this), abi.encode(bidInfo.bidder)); // another safety check to make sure enough ETH was withdrawn require(bidInfo.bidInput.amount <= ethReturned, "Didn't burn enough LP tokens"); diff --git a/contracts/facets/OptionMarketFacet.sol b/contracts/facets/OptionMarketFacet.sol index 9ce63ee..25012ec 100644 --- a/contracts/facets/OptionMarketFacet.sol +++ b/contracts/facets/OptionMarketFacet.sol @@ -156,7 +156,7 @@ contract OptionMarketFacet is IOptionMarket, IERC721Receiver { // update user balance vs.userVaultBalances[option.bidder][option.optionInput.bidInput.vault] -= premiumLPTokenAmount; // withdraw the premium from bidder's vault - uint256 ethReturned = vault.withdraw(premiumLPTokenAmount, payable(this)); + uint256 ethReturned = vault.withdraw(premiumLPTokenAmount, payable(this), abi.encode(option.bidder)); // another safety check to make sure enough ETH was withdrawn require(option.optionInput.premium <= ethReturned, "Didn't burn enough LP tokens"); @@ -244,7 +244,11 @@ contract OptionMarketFacet is IOptionMarket, IERC721Receiver { // update user balance vs.userVaultBalances[option.bidder][option.optionInput.bidInput.vault] -= strikeLPTokenAmount; // withdraw the strike amount from bidder's vault - uint256 ethReturned = vault.withdraw(strikeLPTokenAmount, payable(oms.acceptedOptions[optionId].seller)); + uint256 ethReturned = vault.withdraw( + strikeLPTokenAmount, + payable(oms.acceptedOptions[optionId].seller), + abi.encode(option.bidder) + ); // another safety check to make sure enough ETH was withdrawn require(option.optionInput.bidInput.amount <= ethReturned, "Didn't burn enough LP tokens"); diff --git a/contracts/facets/VaultAccountingFacet.sol b/contracts/facets/VaultAccountingFacet.sol index d4e67bf..e6de9b4 100644 --- a/contracts/facets/VaultAccountingFacet.sol +++ b/contracts/facets/VaultAccountingFacet.sol @@ -51,7 +51,7 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender - uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(); + uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(abi.encode(msg.sender)); vs.userVaultBalances[msg.sender][vaultName] += lpTokensAmount; emit DepositEth(msg.sender, vaultName, msg.value, lpTokensAmount); @@ -70,7 +70,7 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender - IVault(vaultAddress).depositLpToken(amount, msg.sender); + IVault(vaultAddress).depositLpToken(amount, msg.sender, (abi.encode(msg.sender))); vs.userVaultBalances[msg.sender][vaultName] += amount; emit DepositLpToken(msg.sender, vaultName, amount); @@ -93,7 +93,11 @@ contract VaultAccountingFacet is IVaultAccounting { // update user balance vs.userVaultBalances[msg.sender][vaultName] -= lpTokenAmount; // withdraw from vault and send to recipient - uint256 amountWithdrawn = IVault(vaultAddress).withdraw(lpTokenAmount, payable(msg.sender)); + uint256 amountWithdrawn = IVault(vaultAddress).withdraw( + lpTokenAmount, + payable(msg.sender), + (abi.encode(msg.sender)) + ); emit Withdraw(msg.sender, vaultName, amountWithdrawn, lpTokenAmount); } @@ -119,7 +123,7 @@ contract VaultAccountingFacet is IVaultAccounting { // update user balance vs.userVaultBalances[msg.sender][vaultName] -= lpTokenAmount; // withdraw from vault and send to recipient - IVault(vaultAddress).withdrawLpToken(lpTokenAmount, payable(msg.sender)); + IVault(vaultAddress).withdrawLpToken(lpTokenAmount, payable(msg.sender), (abi.encode(msg.sender))); emit WithdrawLpToken(msg.sender, vaultName, lpTokenAmount); } From 51ca0739e13ca15985df7bd3764975103fbf9c35 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:08:20 -0400 Subject: [PATCH 48/70] change bytes32 param to address --- contracts/interfaces/vaults/IRocketPoolVaultStorage.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol index bb2ddb4..a8c1024 100644 --- a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol +++ b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol @@ -6,7 +6,7 @@ interface IRocketPoolVaultStorage { function setNewImplementation(address newImplementation) external; - function setDelegateAddress(bytes32 _data, address delegateAddress) external; + function setDelegateAddress(address user, address delegateAddress) external; - function getDelegateAddress(bytes32 _data) external view returns (address); + function getDelegateAddress(address user) external view returns (address); } From cbcbb499f0e5f5c3c094e01138af30d0fa7d2c09 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:08:54 -0400 Subject: [PATCH 49/70] change bytes32 to bytes --- contracts/interfaces/vaults/IVault.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contracts/interfaces/vaults/IVault.sol b/contracts/interfaces/vaults/IVault.sol index b0b0ef4..24845d0 100644 --- a/contracts/interfaces/vaults/IVault.sol +++ b/contracts/interfaces/vaults/IVault.sol @@ -8,7 +8,7 @@ interface IVault { * @param _data Additional user data if needed * @return The amount of LP tokens received from ETH deposit */ - function deposit(bytes32 _data) external payable returns (uint256); + function deposit(bytes memory _data) external payable returns (uint256); /** * @dev Deposits LP token directly into vault @@ -20,7 +20,7 @@ interface IVault { function depositLpToken( uint256 amount, address user, - bytes32 _data + bytes memory _data ) external; /** @@ -34,7 +34,7 @@ interface IVault { function withdraw( uint256 lpTokenAmount, address payable recipient, - bytes32 _data + bytes memory _data ) external returns (uint256); /** @@ -47,7 +47,7 @@ interface IVault { function withdrawLpToken( uint256 lpTokenAmount, address recipient, - bytes32 _data + bytes memory _data ) external; /** From 756c841da91abe54e8a6160b01487056959c720b Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:09:19 -0400 Subject: [PATCH 50/70] add bytes param --- contracts/test/TestLpTokenVault.sol | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/contracts/test/TestLpTokenVault.sol b/contracts/test/TestLpTokenVault.sol index e0b7b1a..80c5cd2 100644 --- a/contracts/test/TestLpTokenVault.sol +++ b/contracts/test/TestLpTokenVault.sol @@ -12,19 +12,31 @@ contract TestLpTokenVault is BaseVault { lpToken = _lpToken; } - function deposit() external payable override returns (uint256) { + function deposit(bytes memory) external payable override returns (uint256) { revert("Disabled."); } - function depositLpToken(uint256 amount, address user) external override { + function depositLpToken( + uint256 amount, + address user, + bytes memory + ) external override { IERC20(lpToken).transferFrom(user, address(this), amount); } - function withdraw(uint256, address payable) external pure override returns (uint256) { + function withdraw( + uint256, + address payable, + bytes memory + ) external pure override returns (uint256) { revert("Disabled."); } - function withdrawLpToken(uint256 lpTokenAmount, address recipient) external override { + function withdrawLpToken( + uint256 lpTokenAmount, + address recipient, + bytes memory + ) external override { IERC20(lpToken).transfer(recipient, lpTokenAmount); } From 5c224452b8ed284c45c595835acfec71d7f99ae5 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:09:42 -0400 Subject: [PATCH 51/70] add bytes param --- contracts/vaults/EmptyVault.sol | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/contracts/vaults/EmptyVault.sol b/contracts/vaults/EmptyVault.sol index 54ea5c6..fa55dc3 100644 --- a/contracts/vaults/EmptyVault.sol +++ b/contracts/vaults/EmptyVault.sol @@ -6,13 +6,17 @@ import { BaseVault } from "./BaseVault.sol"; contract EmptyVault is BaseVault { constructor(address _diamond) BaseVault(_diamond) {} - function deposit() external payable override onlyDiamond nonReentrant returns (uint256) { + function deposit(bytes memory) external payable override onlyDiamond nonReentrant returns (uint256) { // nothing to do return msg.value; } - function depositLpToken(uint256, address) external override onlyDiamond nonReentrant { + function depositLpToken( + uint256, + address, + bytes memory + ) external override onlyDiamond nonReentrant { revert("Disabled."); } @@ -20,19 +24,21 @@ contract EmptyVault is BaseVault { newVaultAddress.transfer(address(this).balance); } - function withdraw(uint256 lpTokenAmount, address payable recipient) - external - override - onlyDiamond - nonReentrant - returns (uint256) - { + function withdraw( + uint256 lpTokenAmount, + address payable recipient, + bytes memory + ) external override onlyDiamond nonReentrant returns (uint256) { recipient.transfer(lpTokenAmount); return lpTokenAmount; } - function withdrawLpToken(uint256, address) external override onlyDiamond nonReentrant { + function withdrawLpToken( + uint256, + address, + bytes memory + ) external override onlyDiamond nonReentrant { revert("Disabled."); } From d86864f21c8c94a04a105da089758f94354adcbf Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:11:01 -0400 Subject: [PATCH 52/70] add receive function --- contracts/vaults/RocketPoolDelegate.sol | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol index 458352c..da0e459 100644 --- a/contracts/vaults/RocketPoolDelegate.sol +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -94,4 +94,6 @@ contract RocketPoolDelegate is IRocketDelegate, ReentrancyGuard { rETH.transfer(recipient, lpTokenAmount); } + + receive() external payable {} } From 1125200782ff4f78fb95a630328155c989731f63 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:12:59 -0400 Subject: [PATCH 53/70] add bytes decoder and getter for current implementation --- contracts/vaults/RocketPoolVault.sol | 38 +++++++++++++++++----------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index dc159c9..a2050f9 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -18,12 +18,13 @@ contract RocketVault is BaseVault { constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = IRocketStorage(_rocketStorageAddress); - rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); // creates instance of RocketPoolVaultStorage and sets current implemtation to this contract + rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); // creates instance of RocketPoolVaultStorage and sets current implemtation and owner to this contract } - function deposit(bytes32 _data) external payable override onlyDiamond nonReentrant returns (uint256) { + function deposit(bytes memory _data) external payable override onlyDiamond nonReentrant returns (uint256) { // retrive delegate address - address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); // if delegate doesn't exist, create new // if delegate exists, use @@ -33,10 +34,10 @@ contract RocketVault is BaseVault { address(rocketPoolVaultStorage) ); // stores new user's delegate address - rocketPoolVaultStorage.setDelegateAddress(_data, address(newDelegate)); + rocketPoolVaultStorage.setDelegateAddress(userAddress, address(newDelegate)); return newDelegate.deposit{ value: msg.value }(); } else { - RocketPoolDelegate existingDelegate = RocketPoolDelegate(delegateAddress); + RocketPoolDelegate existingDelegate = RocketPoolDelegate(payable(delegateAddress)); return existingDelegate.deposit{ value: msg.value }(); } } @@ -44,7 +45,7 @@ contract RocketVault is BaseVault { function depositLpToken( uint256 amount, address user, - bytes32 _data + bytes memory _data ) external override onlyDiamond nonReentrant { // Load rETH contract address rethAddress = rocketStorage.getAddress( @@ -53,7 +54,8 @@ contract RocketVault is BaseVault { IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); // retrive delegate address - address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(user); // if doesn't exist, create new delegate // if delegate exists, use @@ -63,10 +65,10 @@ contract RocketVault is BaseVault { address(rocketPoolVaultStorage) ); // stores new user's delegate address - rocketPoolVaultStorage.setDelegateAddress(_data, address(newDelegate)); + rocketPoolVaultStorage.setDelegateAddress(userAddress, address(newDelegate)); rETH.transferFrom(user, address(newDelegate), amount); } else { - RocketPoolDelegate existingDelegate = RocketPoolDelegate(delegateAddress); + RocketPoolDelegate existingDelegate = RocketPoolDelegate(payable(delegateAddress)); rETH.transferFrom(user, address(existingDelegate), amount); } } @@ -74,11 +76,12 @@ contract RocketVault is BaseVault { function withdraw( uint256 lpTokenAmount, address payable recipient, - bytes32 _data + bytes memory _data ) external override onlyDiamond nonReentrant returns (uint256) { // retrive delegate address - address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); - RocketPoolDelegate delegate = RocketPoolDelegate(delegateAddress); + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); + RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); return delegate.withdraw(lpTokenAmount, payable(recipient)); } @@ -86,11 +89,12 @@ contract RocketVault is BaseVault { function withdrawLpToken( uint256 lpTokenAmount, address recipient, - bytes32 _data + bytes memory _data ) external override onlyDiamond nonReentrant { // retrive delegate address - address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(_data); - RocketPoolDelegate delegate = RocketPoolDelegate(delegateAddress); + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); + RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); delegate.withdrawLpToken(lpTokenAmount, recipient); } @@ -136,4 +140,8 @@ contract RocketVault is BaseVault { function getLpToken() external view override returns (address) { return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketTokenRETH"))); } + + function getVaultStorage() external view returns (address) { + return address(rocketPoolVaultStorage); + } } From 49dcc9b46f810b186e46a1784bbab85a189a96aa Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:13:40 -0400 Subject: [PATCH 54/70] import interface and change bytes to address --- contracts/vaults/RocketPoolVaultStorage.sol | 25 ++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index 8ff283c..bc7bcf5 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -1,34 +1,33 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.0; -contract RocketPoolVaultStorage { +import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; + +import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; + +contract RocketPoolVaultStorage is IRocketPoolVaultStorage, Ownable { //stores the current vault implementation address currentImplementation; - mapping(bytes32 => address) delegate; - - modifier onlyCurrentImplementation() { - require(msg.sender == currentImplementation, "Only the current implementation can call function"); - _; - } + mapping(address => address) delegate; constructor(address _currentImplemntation) { currentImplementation = _currentImplemntation; } - function getCurrentImplementation() public view returns (address) { + function getCurrentImplementation() public view override returns (address) { return currentImplementation; } - function setNewImplementation(address newImplementation) external onlyCurrentImplementation { + function setNewImplementation(address newImplementation) external override onlyOwner { currentImplementation = newImplementation; } - function setDelegateAddress(bytes32 _data, address delegateAddress) external onlyCurrentImplementation { - delegate[_data] = delegateAddress; + function setDelegateAddress(address user, address delegateAddress) external override onlyOwner { + delegate[user] = delegateAddress; } - function getDelegateAddress(bytes32 _data) external view returns (address) { - return delegate[_data]; + function getDelegateAddress(address user) public view override returns (address) { + return delegate[user]; } } From 538d3068cb98773e1d4eefd6e3435739a5f5217a Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:14:03 -0400 Subject: [PATCH 55/70] add userData for tests to pass --- test/VaultAccounting.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/VaultAccounting.js b/test/VaultAccounting.js index c904b5a..633e434 100644 --- a/test/VaultAccounting.js +++ b/test/VaultAccounting.js @@ -19,6 +19,7 @@ describe("Vault Accounting", async () => { let rewardsDuration; let rewardsAmount; let lpTokenVault; + let userData; beforeEach(async () => { // initialize fixture values @@ -31,6 +32,7 @@ describe("Vault Accounting", async () => { expectedLpTokens = await emptyVault.getAmountLpTokens(amount); rewardsDuration = 604800; // 1 week rewardsAmount = ethers.utils.parseEther("100.0"); + userData = await ethers.utils.defaultAbiCoder.encode(["address"], [user.address]); // create and register vault const LpTokenVault = await ethers.getContractFactory("TestLpTokenVault"); @@ -79,7 +81,7 @@ describe("Vault Accounting", async () => { }); it("should fail to deposit into vault directly", async () => { - await expect(emptyVault.deposit({ value: amount })).to.be.revertedWith("Only diamond can call function"); + await expect(emptyVault.deposit(userData, { value: amount })).to.be.revertedWith("Only diamond can call function"); }); it("should fail to deposit into non-existent vault", async () => { From 059e8c70d0e9d340f35b8fd7f7394f4581394c06 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:14:19 -0400 Subject: [PATCH 56/70] add userData for tests to pass --- test/vaults/Empty.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/vaults/Empty.js b/test/vaults/Empty.js index ad52f53..b598c6c 100644 --- a/test/vaults/Empty.js +++ b/test/vaults/Empty.js @@ -15,6 +15,7 @@ describe("Empty Vault", async () => { let expectedEthAmount; let expectedLpTokenAmount; let emptyVault; + let userData; beforeEach(async () => { // initialize fixture values @@ -27,6 +28,7 @@ describe("Empty Vault", async () => { expectedLpTokenAmount = ethers.utils.parseEther("1.0"); expectedLpToken = "0x0000000000000000000000000000000000000000"; emptyVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.empty)); + userData = await ethers.utils.defaultAbiCoder.encode(["address"], [user.address]); }); it("should reflect correct conversion from ETH to lp tokens", async () => { @@ -105,13 +107,13 @@ describe("Empty Vault", async () => { }); it("should fail to deposit without passing through diamond", async () => { - await expect(emptyVault.connect(user).deposit({ value: depositAmount })).to.be.revertedWith( + await expect(emptyVault.connect(user).deposit(userData, { value: depositAmount })).to.be.revertedWith( "Only diamond can call function" ); }); it("should fail to withdraw without passing through diamond", async () => { - await expect(emptyVault.connect(user).withdraw(depositAmount, user.address)).to.be.revertedWith( + await expect(emptyVault.connect(user).withdraw(depositAmount, user.address, userData)).to.be.revertedWith( "Only diamond can call function" ); }); From 81fee31483b9c081a44cf94dcb31dfb48757c810 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 18:14:35 -0400 Subject: [PATCH 57/70] add delegate testing --- test/vaults/RocketVault.js | 279 +++++++++++++++++++++++++++++-------- 1 file changed, 218 insertions(+), 61 deletions(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index 10a549b..91382bd 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -12,7 +12,7 @@ describe("Rocket Vault", async () => { }); // fixture values - let deployer, user; + let deployer, user, user2; let vaultAccounting; let vaultManagement; let vaultNames; @@ -24,9 +24,14 @@ describe("Rocket Vault", async () => { let expectedLpTokenAmount; let rocketVault; let rETH; - let impersonateAddress; + let impersonateWithdrawAddress; + let impersonateDepositAddress; let depositPoolAddress; let depositPool; + let rocketDirectVault; + let rocketVaultStorageAddress; + let rocketVaultStorage; + let userAddressData; // slippage upon getAmountEth conversion const conversionDelta = ethers.utils.parseEther("0.00000001"); @@ -34,7 +39,7 @@ describe("Rocket Vault", async () => { beforeEach(async () => { // initialize fixture values ({ vaultAccounting, vaultManagement, vaultNames } = await fixture()); - [deployer, user] = await ethers.getSigners(); + [deployer, user, user2] = await ethers.getSigners(); // initialize vault parameters depositAmount = ethers.utils.parseEther("1.0"); @@ -42,25 +47,46 @@ describe("Rocket Vault", async () => { expectedLpToken = "0xae78736Cd615f374D3085123A210448E74Fc6393"; // rETH token address rocketVault = await ethers.getContractAt("IVault", await vaultManagement.getVault(vaultNames.rocketVault)); expectedLpTokenAmount = await rocketVault.getAmountLpTokens(depositAmount); //gets rETH amount equivalent to 1 ETH - rETH = await ethers.getContractAt("RocketTokenRETHInterface", expectedLpToken); - impersonateAddress = "0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"; + rETH = await ethers.getContractAt("IRocketTokenRETH", expectedLpToken); + impersonateWithdrawAddress = "0x7Fe2547Bcb8FCE1D51f2A2C0d9D93174Cd05b3f9"; + impersonateDepositAddress = "0x2FAF487A4414Fe77e2327F0bf4AE2a264a776AD2"; depositPoolAddress = "0x4D05E3d48a938db4b7a9A59A802D5b45011BDe58"; - depositPool = await ethers.getContractAt("RocketDepositPoolInterface", depositPoolAddress); + depositPool = await ethers.getContractAt("IRocketDepositPool", depositPoolAddress); + userAddressData = await ethers.utils.defaultAbiCoder.encode(["address"], [user.address]); + rocketDirectVault = await ethers.getContractAt( + "RocketVault", + await vaultManagement.getVault(vaultNames.rocketVault) + ); + rocketVaultStorageAddress = await rocketDirectVault.getVaultStorage(); + rocketVaultStorage = await ethers.getContractAt("RocketPoolVaultStorage", rocketVaultStorageAddress); + + // // // impersonate user and redeem rETH to make room for testing if needed + // await hre.network.provider.request({ + // method: "hardhat_impersonateAccount", + // params: [impersonateWithdrawAddress], + // }); + // const signer = await ethers.getSigner(impersonateWithdrawAddress); + + // const burnAmount = ethers.utils.parseUnits("1.5"); // 1.5 rETH + + // const approvetx = await rETH.connect(signer).approve(rETH.address, burnAmount); + // await approvetx.wait(); - // impersonate user and redeem rETH to make room for testing + // const burntx = await rETH.connect(signer).burn(burnAmount); + // await burntx.wait(); + // console.log("done"); + + // impersonate user and deposit ETH to make room for withdrawal if needed await hre.network.provider.request({ method: "hardhat_impersonateAccount", - params: [impersonateAddress], + params: [impersonateDepositAddress], }); - const signer = await ethers.getSigner(impersonateAddress); - - const burnAmount = ethers.utils.parseUnits("5.0"); // 5 rETH + const signer = await ethers.getSigner(impersonateDepositAddress); - const approvetx = await rETH.connect(signer).approve(rETH.address, burnAmount); - await approvetx.wait(); + const depositEtherAmount = ethers.utils.parseEther("1000"); - const burntx = await rETH.connect(signer).burn(burnAmount); - await burntx.wait(); + const depositTx = await depositPool.connect(signer).deposit({ value: depositEtherAmount }); + await depositTx.wait(); }); it("Should reflect correct conversion from lp tokens to ETH", async () => { @@ -75,7 +101,7 @@ describe("Rocket Vault", async () => { expect(await rocketVault.getLpToken()).to.equal(expectedLpToken); }); - it("Should deposit specified amount and reflect correct balance", async () => { + it("Should deposit specified amount and reflect correct balance in delegate address", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -92,14 +118,22 @@ describe("Rocket Vault", async () => { expectedEthAmount, conversionDelta ); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); }); - it("Should withdraw specified amount and reflect correct balance", async () => { + it("Should withdraw specified amount and reflect correct balance in delegate address", async () => { + // user info prior to deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + // set up the deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); + // user info after deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( expectedLpTokenAmount ); @@ -108,7 +142,9 @@ describe("Rocket Vault", async () => { conversionDelta ); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); // fast forward through withdrawal time restriction await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks @@ -120,14 +156,19 @@ describe("Rocket Vault", async () => { // user info after withdrawal expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.empty)).to.equal(0); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); + expect(await rETH.balanceOf(delegateAddress)).to.equal(0); }); - it("Should transfer funds to new vault", async () => { + it("Should upgrade vault and set new implementation", async () => { + // user info prior to deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + // set up the deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); + // user info after deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( expectedLpTokenAmount ); @@ -135,25 +176,23 @@ describe("Rocket Vault", async () => { expectedEthAmount, conversionDelta ); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); - // deploy new vault + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // deploy new empty vault const EmptyVault = await ethers.getContractFactory("EmptyVault"); const newEmptyVault = await EmptyVault.deploy(vaultManagement.address); await newEmptyVault.deployed(); - // fast forward through withdrawal time restriction - await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks - // upgrade vault + tx = await vaultManagement.upgradeVault(vaultNames.rocketVault, newEmptyVault.address); await tx.wait(); - //verify old vault is empty - expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); - - //verify new vault balance - expect(await ethers.provider.getBalance(newEmptyVault.address)).to.be.closeTo(expectedEthAmount, conversionDelta); + // check currentImplementation on rocketVaultStorage + expect(await rocketVaultStorage.getCurrentImplementation()).to.equal(newEmptyVault.address); // verify user balance has not changed expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( @@ -162,7 +201,10 @@ describe("Rocket Vault", async () => { expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); }); - it("Should allow LP token deposits", async () => { + it("Should allow LP token deposits and reflect correct balance in delegate address", async () => { + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + // set up the deposit tx = await depositPool.connect(user).deposit({ value: depositAmount }); await tx.wait(); @@ -186,10 +228,13 @@ describe("Rocket Vault", async () => { conversionDelta ); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + // get delegate address and check if rETH was transferred + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); }); it("Should allow LP token withdrawals", async () => { + // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -197,12 +242,18 @@ describe("Rocket Vault", async () => { tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); + // user info after deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( expectedLpTokenAmount ); - expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); //tiny slippage + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); // fast forward through transfer time restriction await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks @@ -215,65 +266,171 @@ describe("Rocket Vault", async () => { expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.empty)).to.equal(0); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(0); + // check delegate address and user address rETH balance after withdrawal + expect(await rETH.balanceOf(delegateAddress)).to.equal(0); expect(await rETH.balanceOf(user.address)).to.equal(expectedLpTokenAmount); }); - it("should fail to deposit without passing through diamond", async () => { - await expect(rocketVault.connect(user).deposit({ value: depositAmount })).to.be.revertedWith( - "Only diamond can call function" - ); - }); + it("should fail to deposit directly to delegate contract", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); - it("should fail to withdraw without passing through diamond", async () => { - await expect(rocketVault.connect(user).withdraw(depositAmount, user.address)).to.be.revertedWith( - "Only diamond can call function" + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // retrive delegate contract + delegateProxy = await ethers.getContractAt("RocketPoolDelegate", delegateAddress); + + await expect(delegateProxy.connect(user).deposit({ value: depositAmount })).to.be.revertedWith( + "Only the current implementation can call function" ); }); - it("should fail to transfer funds without passing through diamond", async () => { - await expect(rocketVault.connect(user).transferFunds(user.address)).to.be.revertedWith( - "Only diamond can call function" + it("should fail to withdraw directly from delegate contract", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // retrive delegate contract + delegateProxy = await ethers.getContractAt("RocketPoolDelegate", delegateAddress); + + await expect(delegateProxy.connect(user).withdraw(depositAmount, user.address)).to.be.revertedWith( + "Only the current implementation can call function" ); }); - it("Should fail to deposit LP tokens without passing through diamond", async () => { - // set up the deposit + it("Should fail to deposit LP tokens directly to delegate contract", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // deposit directly with rocketpool tx = await depositPool.connect(user).deposit({ value: depositAmount }); await tx.wait(); // fast forward through transfer time restriction await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks - // approve rETH to vault - approvetx = await rETH.connect(user).approve(rocketVault.address, expectedLpTokenAmount); + // retrive delegate contract + delegateProxy = await ethers.getContractAt("RocketPoolDelegate", delegateAddress); + + // approve rETH to delegate contract + approvetx = await rETH.connect(user).approve(delegateProxy.address, expectedLpTokenAmount); await approvetx.wait(); - await expect(rocketVault.connect(user).depositLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + await expect(delegateProxy.connect(user).depositLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + "Only the current implementation can call function" + ); + }); + + it("Should fail to withdraw LP tokens directly from delegate contract", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // retrive delegate contract + delegateProxy = await ethers.getContractAt("RocketPoolDelegate", delegateAddress); + + await expect(delegateProxy.connect(user).withdrawLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + "Only the current implementation can call function" + ); + }); + + it("should fail to upgrade current implementation without passing through diamond", async () => { + await expect(rocketVault.connect(user).transferFunds(user.address)).to.be.revertedWith( "Only diamond can call function" ); }); - it("Should fail to withdraw LP tokens without passing through diamond", async () => { - expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); - expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + it("Should fail to upgrade current implementation in vault storage if not owner", async () => { + newVault = "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5"; // random address + await expect(rocketVaultStorage.connect(user).setNewImplementation(newVault)).to.be.revertedWith( + "Ownable: caller is not the owner" + ); + }); - // set up the deposit + it("Should fail to set delegate address if not current implementation", async () => { + // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); - expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( - expectedLpTokenAmount + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // should fail if user2 tries to take user's delegate address + await expect( + rocketVaultStorage.connect(user2).setDelegateAddress(user2.address, delegateAddress) + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + + it("Should fail to deposit directly without passing through diamond", async () => { + await expect(rocketVault.connect(user).deposit(userAddressData, { value: depositAmount })).to.be.revertedWith( + "Only diamond can call function" ); - expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); //tiny slippage + }); - expect(await rETH.balanceOf(rocketVault.address)).to.equal(expectedLpTokenAmount); + it("Should fail to withdraw directly without passing through diamond", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); // fast forward through transfer time restriction await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks - await expect(rocketVault.connect(user).withdrawLpToken(expectedLpTokenAmount, user.address)).to.be.revertedWith( + await expect(rocketVault.connect(user).withdraw(depositAmount, user.address, userAddressData)).to.be.revertedWith( "Only diamond can call function" ); }); + + it("Should fail to deposit LP tokens directly without passing through diamond", async () => { + // deposit directly with rocketpool + tx = await depositPool.connect(user).deposit({ value: depositAmount }); + await tx.wait(); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + await expect( + rocketVault.connect(user).depositLpToken(expectedLpTokenAmount, user.address, userAddressData) + ).to.be.revertedWith("Only diamond can call function"); + }); + + it("Should fail to withdraw LP tokens directly without passing through diamond", async () => { + // set up initial deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // fast forward through transfer time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + await expect( + rocketVault.connect(user).withdrawLpToken(expectedLpToken, user.address, userAddressData) + ).to.be.revertedWith("Only diamond can call function"); + }); }); From 1ea26d2a68d4cf858adc8d22dfef00bb0a6be8d3 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 18 May 2022 20:18:47 -0400 Subject: [PATCH 58/70] more tests --- test/vaults/RocketVault.js | 62 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index 91382bd..197f2c4 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -271,6 +271,68 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(user.address)).to.equal(expectedLpTokenAmount); }); + it("Should record additional deposits by same user to same delegate address", async () => { + // user info prior to deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); + + // set up the deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // user info after deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( + expectedLpTokenAmount + ); + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmount, + conversionDelta + ); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // set up second deposit + tx2 = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx2.wait(); + + expectedEthAmountAfter = await ethers.utils.parseEther("2.0"); + expectedLpTokenAmountAfter = await rocketVault.getAmountLpTokens(expectedEthAmountAfter); + + // user info after deposit + expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedLpTokenAmountAfter, + conversionDelta + ); // rETH value may have increased + expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.closeTo( + expectedEthAmountAfter, + conversionDelta + ); + + // check delegate address rETH balance + expect(await rETH.balanceOf(delegateAddress)).to.be.closeTo(expectedLpTokenAmountAfter, conversionDelta); + }); + + it("Should create different delegate proxies for different users", async () => { + // set up first user deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // set up second user deposit + tx2 = await vaultAccounting.connect(user2).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx2.wait(); + + delegateAddress2 = await rocketVaultStorage.getDelegateAddress(user2.address); + expect(await rETH.balanceOf(delegateAddress2)).to.equal(expectedLpTokenAmount); + + expect(delegateAddress).to.not.equal(delegateAddress2); + }); + it("should fail to deposit directly to delegate contract", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); From 3ba47461d4f371dfa996d4d26de4086d131a6438 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Thu, 19 May 2022 12:49:23 +0100 Subject: [PATCH 59/70] Refactor suggestions --- contracts/facets/VaultAccountingFacet.sol | 3 +++ contracts/libraries/LibVaultUtils.sol | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/contracts/facets/VaultAccountingFacet.sol b/contracts/facets/VaultAccountingFacet.sol index e6de9b4..caf946a 100644 --- a/contracts/facets/VaultAccountingFacet.sol +++ b/contracts/facets/VaultAccountingFacet.sol @@ -51,6 +51,9 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender + // Hendrik: + // bytes optionalData = LibVaultUtils.getVaultOptionalData(vaultName); + // uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(optionalData); uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(abi.encode(msg.sender)); vs.userVaultBalances[msg.sender][vaultName] += lpTokensAmount; diff --git a/contracts/libraries/LibVaultUtils.sol b/contracts/libraries/LibVaultUtils.sol index 8a19f7f..9aeae73 100644 --- a/contracts/libraries/LibVaultUtils.sol +++ b/contracts/libraries/LibVaultUtils.sol @@ -30,4 +30,11 @@ library LibVaultUtils { return IERC20(vaultLpToken).balanceOf(vault); } } + + // Hendrik: + // function getVaultOptionalData(bytes32 vaultName) internal view returns (bytes memory optionalData) { + // if (vaultName == empty_vault_name) { + // optionalData = abi.encode(msg.sender); + // } + // } } From 418e5fb8561fdef876919ddc14c02662cb64868e Mon Sep 17 00:00:00 2001 From: ez7212 Date: Thu, 19 May 2022 10:06:34 -0400 Subject: [PATCH 60/70] remove unneeded functions --- contracts/interfaces/vaults/IRocketPoolVaultStorage.sol | 4 ---- 1 file changed, 4 deletions(-) diff --git a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol index a8c1024..9b197c3 100644 --- a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol +++ b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol @@ -2,10 +2,6 @@ pragma solidity ^0.8.0; interface IRocketPoolVaultStorage { - function getCurrentImplementation() external view returns (address); - - function setNewImplementation(address newImplementation) external; - function setDelegateAddress(address user, address delegateAddress) external; function getDelegateAddress(address user) external view returns (address); From d8ce179ec9a10b8f1bd11776e3831471b8151a92 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Thu, 19 May 2022 10:07:38 -0400 Subject: [PATCH 61/70] change implementation functions to ownership --- contracts/vaults/RocketPoolDelegate.sol | 8 ++++---- contracts/vaults/RocketPoolVault.sol | 4 ++-- contracts/vaults/RocketPoolVaultStorage.sol | 15 --------------- test/vaults/RocketVault.js | 4 ++-- 4 files changed, 8 insertions(+), 23 deletions(-) diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol index da0e459..2fec306 100644 --- a/contracts/vaults/RocketPoolDelegate.sol +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -5,23 +5,23 @@ import { IRocketDepositPool } from "../interfaces/vaults/IRocketDepositPool.sol" import { IRocketTokenRETH } from "../interfaces/vaults/IRocketTokenRETH.sol"; import { IRocketDelegate } from "../interfaces/vaults/IRocketDelegate.sol"; import { IRocketStorage } from "../interfaces/vaults/IRocketStorage.sol"; -import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; +import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract RocketPoolDelegate is IRocketDelegate, ReentrancyGuard { IRocketStorage rocketStorage; - IRocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation + RocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation modifier onlyCurrentImplementation() { - address currentVaultImplementation = rocketPoolVaultStorage.getCurrentImplementation(); + address currentVaultImplementation = rocketPoolVaultStorage.owner(); require(msg.sender == currentVaultImplementation, "Only the current implementation can call function"); _; } constructor(address _rocketStorageAddress, address _rocketPoolVaultStorageAddress) { rocketStorage = IRocketStorage(_rocketStorageAddress); - rocketPoolVaultStorage = IRocketPoolVaultStorage(_rocketPoolVaultStorageAddress); + rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketPoolVaultStorageAddress); } function deposit() external payable override onlyCurrentImplementation nonReentrant returns (uint256) { diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index a2050f9..7933601 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -18,7 +18,7 @@ contract RocketVault is BaseVault { constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = IRocketStorage(_rocketStorageAddress); - rocketPoolVaultStorage = new RocketPoolVaultStorage(address(this)); // creates instance of RocketPoolVaultStorage and sets current implemtation and owner to this contract + rocketPoolVaultStorage = new RocketPoolVaultStorage(); // creates instance of RocketPoolVaultStorage and sets owner to this contract } function deposit(bytes memory _data) external payable override onlyDiamond nonReentrant returns (uint256) { @@ -102,7 +102,7 @@ contract RocketVault is BaseVault { function transferFunds(address payable newImplementation) external override onlyDiamond { // upgrade current implementation to new implementation? // if so, just need to update in RocketPoolVaultStorage - rocketPoolVaultStorage.setNewImplementation(newImplementation); + rocketPoolVaultStorage.transferOwnership(newImplementation); } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index bc7bcf5..b820f70 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -6,23 +6,8 @@ import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultSt import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; contract RocketPoolVaultStorage is IRocketPoolVaultStorage, Ownable { - //stores the current vault implementation - address currentImplementation; - mapping(address => address) delegate; - constructor(address _currentImplemntation) { - currentImplementation = _currentImplemntation; - } - - function getCurrentImplementation() public view override returns (address) { - return currentImplementation; - } - - function setNewImplementation(address newImplementation) external override onlyOwner { - currentImplementation = newImplementation; - } - function setDelegateAddress(address user, address delegateAddress) external override onlyOwner { delegate[user] = delegateAddress; } diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index 197f2c4..bff4da5 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -192,7 +192,7 @@ describe("Rocket Vault", async () => { await tx.wait(); // check currentImplementation on rocketVaultStorage - expect(await rocketVaultStorage.getCurrentImplementation()).to.equal(newEmptyVault.address); + expect(await rocketVaultStorage.owner()).to.equal(newEmptyVault.address); // verify user balance has not changed expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( @@ -423,7 +423,7 @@ describe("Rocket Vault", async () => { it("Should fail to upgrade current implementation in vault storage if not owner", async () => { newVault = "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5"; // random address - await expect(rocketVaultStorage.connect(user).setNewImplementation(newVault)).to.be.revertedWith( + await expect(rocketVaultStorage.connect(user).transferOwnership(newVault)).to.be.revertedWith( "Ownable: caller is not the owner" ); }); From d4bac303e75a1babbebd2c259a1a6e5b4b56446d Mon Sep 17 00:00:00 2001 From: ez7212 Date: Mon, 23 May 2022 13:40:34 -0400 Subject: [PATCH 62/70] add getVaultOptionalData helper --- contracts/facets/VaultAccountingFacet.sol | 19 ++++++++----------- contracts/libraries/LibVaultUtils.sol | 19 +++++++++++++------ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/contracts/facets/VaultAccountingFacet.sol b/contracts/facets/VaultAccountingFacet.sol index caf946a..0b31779 100644 --- a/contracts/facets/VaultAccountingFacet.sol +++ b/contracts/facets/VaultAccountingFacet.sol @@ -51,10 +51,8 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender - // Hendrik: - // bytes optionalData = LibVaultUtils.getVaultOptionalData(vaultName); - // uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(optionalData); - uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(abi.encode(msg.sender)); + bytes memory optionalData = LibVaultUtils.getVaultOptionalData(vaultName); + uint256 lpTokensAmount = IVault(vaultAddress).deposit{ value: msg.value }(optionalData); vs.userVaultBalances[msg.sender][vaultName] += lpTokensAmount; emit DepositEth(msg.sender, vaultName, msg.value, lpTokensAmount); @@ -73,7 +71,8 @@ contract VaultAccountingFacet is IVaultAccounting { address vaultAddress = vs.vaultAddresses[vaultName]; // deposit into vault on behalf of sender - IVault(vaultAddress).depositLpToken(amount, msg.sender, (abi.encode(msg.sender))); + bytes memory optionalData = LibVaultUtils.getVaultOptionalData(vaultName); + IVault(vaultAddress).depositLpToken(amount, msg.sender, optionalData); vs.userVaultBalances[msg.sender][vaultName] += amount; emit DepositLpToken(msg.sender, vaultName, amount); @@ -96,11 +95,8 @@ contract VaultAccountingFacet is IVaultAccounting { // update user balance vs.userVaultBalances[msg.sender][vaultName] -= lpTokenAmount; // withdraw from vault and send to recipient - uint256 amountWithdrawn = IVault(vaultAddress).withdraw( - lpTokenAmount, - payable(msg.sender), - (abi.encode(msg.sender)) - ); + bytes memory optionalData = LibVaultUtils.getVaultOptionalData(vaultName); + uint256 amountWithdrawn = IVault(vaultAddress).withdraw(lpTokenAmount, payable(msg.sender), optionalData); emit Withdraw(msg.sender, vaultName, amountWithdrawn, lpTokenAmount); } @@ -126,7 +122,8 @@ contract VaultAccountingFacet is IVaultAccounting { // update user balance vs.userVaultBalances[msg.sender][vaultName] -= lpTokenAmount; // withdraw from vault and send to recipient - IVault(vaultAddress).withdrawLpToken(lpTokenAmount, payable(msg.sender), (abi.encode(msg.sender))); + bytes memory optionalData = LibVaultUtils.getVaultOptionalData(vaultName); + IVault(vaultAddress).withdrawLpToken(lpTokenAmount, payable(msg.sender), optionalData); emit WithdrawLpToken(msg.sender, vaultName, lpTokenAmount); } diff --git a/contracts/libraries/LibVaultUtils.sol b/contracts/libraries/LibVaultUtils.sol index 9aeae73..a6dbfc4 100644 --- a/contracts/libraries/LibVaultUtils.sol +++ b/contracts/libraries/LibVaultUtils.sol @@ -31,10 +31,17 @@ library LibVaultUtils { } } - // Hendrik: - // function getVaultOptionalData(bytes32 vaultName) internal view returns (bytes memory optionalData) { - // if (vaultName == empty_vault_name) { - // optionalData = abi.encode(msg.sender); - // } - // } + function getVaultOptionalData(bytes12 vaultName) internal view returns (bytes memory optionalData) { + bytes12 empty_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeee; + bytes12 rocket_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeed; + bytes12 test_lp_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeff; + + if (vaultName == empty_vault_name) { + optionalData = abi.encode(msg.sender); + } else if (vaultName == rocket_vault_name) { + optionalData = abi.encode(msg.sender); + } else if (vaultName == test_lp_vault_name) { + optionalData = abi.encode(msg.sender); + } + } } From 953ab23b948dc43c6c5368e456e3d86b2486e032 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Mon, 23 May 2022 13:54:06 -0400 Subject: [PATCH 63/70] multiple user withdraw tests --- test/vaults/RocketVault.js | 68 +++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index bff4da5..15231a4 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -149,7 +149,7 @@ describe("Rocket Vault", async () => { // fast forward through withdrawal time restriction await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks - // initial withdrawal + // initiate withdrawal tx = await vaultAccounting.connect(user).withdraw(expectedLpTokenAmount, vaultNames.rocketVault); await tx.wait(); @@ -333,6 +333,72 @@ describe("Rocket Vault", async () => { expect(delegateAddress).to.not.equal(delegateAddress2); }); + it("Should allow multiple users to withdraw simultaneously", async () => { + // set up first user deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // set up second user deposit + tx2 = await vaultAccounting.connect(user2).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx2.wait(); + + delegateAddress2 = await rocketVaultStorage.getDelegateAddress(user2.address); + expect(await rETH.balanceOf(delegateAddress2)).to.equal(expectedLpTokenAmount); + + // fast forward through withdrawal time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // initiate user 2 withdrawal + withdrawtx = await vaultAccounting.connect(user2).withdraw(expectedLpTokenAmount, vaultNames.rocketVault); + await withdrawtx.wait(); + + // check if user 2 delegate address rETH balance is correct + expect(await rETH.balanceOf(delegateAddress2)).to.equal(0); + + // initiate user 1 withdrawal + withdrawtx2 = await vaultAccounting.connect(user).withdraw(expectedLpTokenAmount, vaultNames.rocketVault); + + // check if user 1 delegate address rETH balance is correct + expect(await rETH.balanceOf(delegateAddress)).to.equal(0); + }); + + it("Should allow multiple users to withdraw LP tokens simultaneously", async () => { + // set up first user deposit + tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx.wait(); + + // get delegate address and check if rETH was minted + delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); + expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); + + // set up second user deposit + tx2 = await vaultAccounting.connect(user2).deposit(vaultNames.rocketVault, { value: depositAmount }); + await tx2.wait(); + + delegateAddress2 = await rocketVaultStorage.getDelegateAddress(user2.address); + expect(await rETH.balanceOf(delegateAddress2)).to.equal(expectedLpTokenAmount); + + // fast forward through withdrawal time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // set up user 2 LP token withdrawal + withdrawtx = await vaultAccounting.connect(user2).withdrawLpToken(expectedLpTokenAmount, vaultNames.rocketVault); + await withdrawtx.wait(); + + // check if user 2 delegate address rETH balance is correct + expect(await rETH.balanceOf(delegateAddress2)).to.equal(0); + + // initiate user 1 withdrawal + withdrawtx2 = await vaultAccounting.connect(user).withdrawLpToken(expectedLpTokenAmount, vaultNames.rocketVault); + + // check if user 1 delegate address rETH balance is correct + expect(await rETH.balanceOf(delegateAddress)).to.equal(0); + }); + it("should fail to deposit directly to delegate contract", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); From 2da5cac08c84f87e1128ae565d38c10f043ae217 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Tue, 24 May 2022 16:57:33 +0100 Subject: [PATCH 64/70] minor changes --- contracts/libraries/LibVaultUtils.sol | 9 ++--- contracts/vaults/RocketPoolDelegate.sol | 4 +-- contracts/vaults/RocketPoolVault.sol | 17 ++++----- contracts/vaults/RocketPoolVaultStorage.sol | 4 +-- test/vaults/RocketVault.js | 40 ++++++++++----------- 5 files changed, 35 insertions(+), 39 deletions(-) diff --git a/contracts/libraries/LibVaultUtils.sol b/contracts/libraries/LibVaultUtils.sol index a6dbfc4..edf60c1 100644 --- a/contracts/libraries/LibVaultUtils.sol +++ b/contracts/libraries/LibVaultUtils.sol @@ -32,15 +32,10 @@ library LibVaultUtils { } function getVaultOptionalData(bytes12 vaultName) internal view returns (bytes memory optionalData) { - bytes12 empty_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeee; + // todo: find alternative solution instead of hardcoding bytes12 rocket_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeed; - bytes12 test_lp_vault_name = 0xeeeeeeeeeeeeeeeeeeeeeeff; - if (vaultName == empty_vault_name) { - optionalData = abi.encode(msg.sender); - } else if (vaultName == rocket_vault_name) { - optionalData = abi.encode(msg.sender); - } else if (vaultName == test_lp_vault_name) { + if (vaultName == rocket_vault_name) { optionalData = abi.encode(msg.sender); } } diff --git a/contracts/vaults/RocketPoolDelegate.sol b/contracts/vaults/RocketPoolDelegate.sol index 2fec306..b40db74 100644 --- a/contracts/vaults/RocketPoolDelegate.sol +++ b/contracts/vaults/RocketPoolDelegate.sol @@ -10,8 +10,8 @@ import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; contract RocketPoolDelegate is IRocketDelegate, ReentrancyGuard { - IRocketStorage rocketStorage; - RocketPoolVaultStorage rocketPoolVaultStorage; // holds the current vault implementation + IRocketStorage private rocketStorage; + RocketPoolVaultStorage private rocketPoolVaultStorage; // holds the current vault implementation modifier onlyCurrentImplementation() { address currentVaultImplementation = rocketPoolVaultStorage.owner(); diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index 7933601..786411b 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -11,10 +11,10 @@ import { RocketPoolDelegate } from "./RocketPoolDelegate.sol"; contract RocketVault is BaseVault { // stores state for Rocket Protocol - IRocketStorage rocketStorage; + IRocketStorage private rocketStorage; // storage for Rocket Vault - RocketPoolVaultStorage rocketPoolVaultStorage; + RocketPoolVaultStorage private rocketPoolVaultStorage; constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = IRocketStorage(_rocketStorageAddress); @@ -22,7 +22,7 @@ contract RocketVault is BaseVault { } function deposit(bytes memory _data) external payable override onlyDiamond nonReentrant returns (uint256) { - // retrive delegate address + // retrieve delegate address address userAddress = abi.decode(_data, (address)); address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); @@ -53,7 +53,7 @@ contract RocketVault is BaseVault { ); IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); - // retrive delegate address + // retrieve delegate address address userAddress = abi.decode(_data, (address)); address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(user); @@ -78,7 +78,7 @@ contract RocketVault is BaseVault { address payable recipient, bytes memory _data ) external override onlyDiamond nonReentrant returns (uint256) { - // retrive delegate address + // retrieve delegate address address userAddress = abi.decode(_data, (address)); address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); @@ -91,7 +91,7 @@ contract RocketVault is BaseVault { address recipient, bytes memory _data ) external override onlyDiamond nonReentrant { - // retrive delegate address + // retrieve delegate address address userAddress = abi.decode(_data, (address)); address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); @@ -100,9 +100,10 @@ contract RocketVault is BaseVault { } function transferFunds(address payable newImplementation) external override onlyDiamond { - // upgrade current implementation to new implementation? - // if so, just need to update in RocketPoolVaultStorage + // upgrade current implementation to new implementation rocketPoolVaultStorage.transferOwnership(newImplementation); + + // todo: should have a rescue plan if the delegate contracts are no longer compatible } function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index b820f70..b9c4236 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -6,13 +6,13 @@ import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultSt import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; contract RocketPoolVaultStorage is IRocketPoolVaultStorage, Ownable { - mapping(address => address) delegate; + mapping(address => address) private delegate; function setDelegateAddress(address user, address delegateAddress) external override onlyOwner { delegate[user] = delegateAddress; } - function getDelegateAddress(address user) public view override returns (address) { + function getDelegateAddress(address user) external view override returns (address) { return delegate[user]; } } diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index 15231a4..db79ad5 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -89,19 +89,19 @@ describe("Rocket Vault", async () => { await depositTx.wait(); }); - it("Should reflect correct conversion from lp tokens to ETH", async () => { + it("should reflect correct conversion from lp tokens to ETH", async () => { expect(await rocketVault.getAmountETH(expectedLpTokenAmount)).to.be.closeTo(expectedEthAmount, conversionDelta); }); - it("Should reflect correct conversion from ETH to lp tokens", async () => { + it("should reflect correct conversion from ETH to lp tokens", async () => { expect(await rocketVault.getAmountLpTokens(expectedEthAmount)).to.be.equal(expectedLpTokenAmount); }); - it("Should return the correct lp token", async () => { + it("should return the correct lp token", async () => { expect(await rocketVault.getLpToken()).to.equal(expectedLpToken); }); - it("Should deposit specified amount and reflect correct balance in delegate address", async () => { + it("should deposit specified amount and reflect correct balance in delegate address", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -124,7 +124,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); }); - it("Should withdraw specified amount and reflect correct balance in delegate address", async () => { + it("should withdraw specified amount and reflect correct balance in delegate address", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -159,7 +159,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(delegateAddress)).to.equal(0); }); - it("Should upgrade vault and set new implementation", async () => { + it("should upgrade vault and set new implementation", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -201,7 +201,7 @@ describe("Rocket Vault", async () => { expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); }); - it("Should allow LP token deposits and reflect correct balance in delegate address", async () => { + it("should allow LP token deposits and reflect correct balance in delegate address", async () => { expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -233,7 +233,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); }); - it("Should allow LP token withdrawals", async () => { + it("should allow LP token withdrawals", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -271,7 +271,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(user.address)).to.equal(expectedLpTokenAmount); }); - it("Should record additional deposits by same user to same delegate address", async () => { + it("should record additional deposits by same user to same delegate address", async () => { // user info prior to deposit expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal(0); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.equal(0); @@ -314,7 +314,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(delegateAddress)).to.be.closeTo(expectedLpTokenAmountAfter, conversionDelta); }); - it("Should create different delegate proxies for different users", async () => { + it("should create different delegate proxies for different users", async () => { // set up first user deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -333,7 +333,7 @@ describe("Rocket Vault", async () => { expect(delegateAddress).to.not.equal(delegateAddress2); }); - it("Should allow multiple users to withdraw simultaneously", async () => { + it("should allow multiple users to withdraw simultaneously", async () => { // set up first user deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -366,7 +366,7 @@ describe("Rocket Vault", async () => { expect(await rETH.balanceOf(delegateAddress)).to.equal(0); }); - it("Should allow multiple users to withdraw LP tokens simultaneously", async () => { + it("should allow multiple users to withdraw LP tokens simultaneously", async () => { // set up first user deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -433,7 +433,7 @@ describe("Rocket Vault", async () => { ); }); - it("Should fail to deposit LP tokens directly to delegate contract", async () => { + it("should fail to deposit LP tokens directly to delegate contract", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -461,7 +461,7 @@ describe("Rocket Vault", async () => { ); }); - it("Should fail to withdraw LP tokens directly from delegate contract", async () => { + it("should fail to withdraw LP tokens directly from delegate contract", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -487,14 +487,14 @@ describe("Rocket Vault", async () => { ); }); - it("Should fail to upgrade current implementation in vault storage if not owner", async () => { + it("should fail to upgrade current implementation in vault storage if not owner", async () => { newVault = "0x283Af0B28c62C092C9727F1Ee09c02CA627EB7F5"; // random address await expect(rocketVaultStorage.connect(user).transferOwnership(newVault)).to.be.revertedWith( "Ownable: caller is not the owner" ); }); - it("Should fail to set delegate address if not current implementation", async () => { + it("should fail to set delegate address if not current implementation", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -509,13 +509,13 @@ describe("Rocket Vault", async () => { ).to.be.revertedWith("Ownable: caller is not the owner"); }); - it("Should fail to deposit directly without passing through diamond", async () => { + it("should fail to deposit directly without passing through diamond", async () => { await expect(rocketVault.connect(user).deposit(userAddressData, { value: depositAmount })).to.be.revertedWith( "Only diamond can call function" ); }); - it("Should fail to withdraw directly without passing through diamond", async () => { + it("should fail to withdraw directly without passing through diamond", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); @@ -532,7 +532,7 @@ describe("Rocket Vault", async () => { ); }); - it("Should fail to deposit LP tokens directly without passing through diamond", async () => { + it("should fail to deposit LP tokens directly without passing through diamond", async () => { // deposit directly with rocketpool tx = await depositPool.connect(user).deposit({ value: depositAmount }); await tx.wait(); @@ -545,7 +545,7 @@ describe("Rocket Vault", async () => { ).to.be.revertedWith("Only diamond can call function"); }); - it("Should fail to withdraw LP tokens directly without passing through diamond", async () => { + it("should fail to withdraw LP tokens directly without passing through diamond", async () => { // set up initial deposit tx = await vaultAccounting.connect(user).deposit(vaultNames.rocketVault, { value: depositAmount }); await tx.wait(); From 9d25759aacdb8baaaafb2201c4f887f20ad11508 Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 24 May 2022 15:37:05 -0400 Subject: [PATCH 65/70] rocket vault without creating new vault storage --- contracts/test/TestRocketVault.sol | 152 +++++++++++++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 contracts/test/TestRocketVault.sol diff --git a/contracts/test/TestRocketVault.sol b/contracts/test/TestRocketVault.sol new file mode 100644 index 0000000..b0ed470 --- /dev/null +++ b/contracts/test/TestRocketVault.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.0; + +import { BaseVault } from "../vaults/BaseVault.sol"; +import { IRocketDepositPool } from "../interfaces/vaults/IRocketDepositPool.sol"; +import { IRocketTokenRETH } from "../interfaces/vaults/IRocketTokenRETH.sol"; +import { IRocketStorage } from "../interfaces/vaults/IRocketStorage.sol"; +import { IRocketVault } from "../interfaces/vaults/IRocketVault.sol"; +import { RocketPoolVaultStorage } from "../vaults/RocketPoolVaultStorage.sol"; +import { RocketPoolDelegate } from "../vaults/RocketPoolDelegate.sol"; + +contract TestRocketVault is BaseVault { + // stores state for Rocket Protocol + IRocketStorage private rocketStorage; + + // storage for Rocket Vault + RocketPoolVaultStorage private rocketPoolVaultStorage; + + constructor( + address _diamond, + address _rocketStorageAddress, + address _rocketVaultStorageAddress + ) BaseVault(_diamond) { + rocketStorage = IRocketStorage(_rocketStorageAddress); + rocketPoolVaultStorage = RocketPoolVaultStorage(_rocketVaultStorageAddress); + } + + function deposit(bytes memory _data) external payable override onlyDiamond nonReentrant returns (uint256) { + // retrieve delegate address + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); + + // if delegate doesn't exist, create new + // if delegate exists, use + if (delegateAddress == address(0)) { + RocketPoolDelegate newDelegate = new RocketPoolDelegate( + address(rocketStorage), + address(rocketPoolVaultStorage) + ); + // stores new user's delegate address + rocketPoolVaultStorage.setDelegateAddress(userAddress, address(newDelegate)); + return newDelegate.deposit{ value: msg.value }(); + } else { + RocketPoolDelegate existingDelegate = RocketPoolDelegate(payable(delegateAddress)); + return existingDelegate.deposit{ value: msg.value }(); + } + } + + function depositLpToken( + uint256 amount, + address user, + bytes memory _data + ) external override onlyDiamond nonReentrant { + // Load rETH contract + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); + + // retrieve delegate address + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(user); + + // if doesn't exist, create new delegate + // if delegate exists, use + if (delegateAddress == address(0)) { + RocketPoolDelegate newDelegate = new RocketPoolDelegate( + address(rocketStorage), + address(rocketPoolVaultStorage) + ); + // stores new user's delegate address + rocketPoolVaultStorage.setDelegateAddress(userAddress, address(newDelegate)); + rETH.transferFrom(user, address(newDelegate), amount); + } else { + RocketPoolDelegate existingDelegate = RocketPoolDelegate(payable(delegateAddress)); + rETH.transferFrom(user, address(existingDelegate), amount); + } + } + + function withdraw( + uint256 lpTokenAmount, + address payable recipient, + bytes memory _data + ) external override onlyDiamond nonReentrant returns (uint256) { + // retrieve delegate address + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); + RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); + + return delegate.withdraw(lpTokenAmount, payable(recipient)); + } + + function withdrawLpToken( + uint256 lpTokenAmount, + address recipient, + bytes memory _data + ) external override onlyDiamond nonReentrant { + // retrieve delegate address + address userAddress = abi.decode(_data, (address)); + address delegateAddress = rocketPoolVaultStorage.getDelegateAddress(userAddress); + RocketPoolDelegate delegate = RocketPoolDelegate(payable(delegateAddress)); + + delegate.withdrawLpToken(lpTokenAmount, recipient); + } + + function transferFunds(address payable newImplementation) external override onlyDiamond { + // upgrade current implementation to new implementation + rocketPoolVaultStorage.transferOwnership(newImplementation); + + // todo: should have a rescue plan if the delegate contracts are no longer compatible + } + + function getAmountETH(uint256 lpTokenAmount) external view override returns (uint256) { + // Load rocketVault and rETH contracts + address rocketVaultAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketVault")) + ); + IRocketVault rocketVault = IRocketVault(rocketVaultAddress); + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); + + uint256 ethAmount = rETH.getEthValue(lpTokenAmount); + + // check if rocket vault has enough to withdraw, if not return rocket vault balance + uint256 vaultBalance = rocketVault.balanceOf("rocketDepositPool"); + if (vaultBalance >= ethAmount) { + return ethAmount; + } else { + return vaultBalance; + } + } + + function getAmountLpTokens(uint256 ethAmount) external view override returns (uint256) { + // Load rETH contract + address rethAddress = rocketStorage.getAddress( + keccak256(abi.encodePacked("contract.address", "rocketTokenRETH")) + ); + IRocketTokenRETH rETH = IRocketTokenRETH(rethAddress); + + return rETH.getRethValue(ethAmount); + } + + function getLpToken() external view override returns (address) { + return rocketStorage.getAddress(keccak256(abi.encodePacked("contract.address", "rocketTokenRETH"))); + } + + function getVaultStorage() external view returns (address) { + return address(rocketPoolVaultStorage); + } +} From d1d78a964692b930e09f6e4751e0a8cc632bb8ce Mon Sep 17 00:00:00 2001 From: ez7212 Date: Tue, 24 May 2022 15:37:39 -0400 Subject: [PATCH 66/70] upgrade rocket vault and ensure withdrawals work --- test/vaults/RocketVault.js | 29 ++++++++++++++++++++++------- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index db79ad5..ef4c604 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -29,6 +29,7 @@ describe("Rocket Vault", async () => { let depositPoolAddress; let depositPool; let rocketDirectVault; + let rocketPoolStorageAddress; let rocketVaultStorageAddress; let rocketVaultStorage; let userAddressData; @@ -57,6 +58,7 @@ describe("Rocket Vault", async () => { "RocketVault", await vaultManagement.getVault(vaultNames.rocketVault) ); + rocketPoolStorageAddress = "0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46"; rocketVaultStorageAddress = await rocketDirectVault.getVaultStorage(); rocketVaultStorage = await ethers.getContractAt("RocketPoolVaultStorage", rocketVaultStorageAddress); @@ -181,24 +183,37 @@ describe("Rocket Vault", async () => { delegateAddress = await rocketVaultStorage.getDelegateAddress(user.address); expect(await rETH.balanceOf(delegateAddress)).to.equal(expectedLpTokenAmount); - // deploy new empty vault - const EmptyVault = await ethers.getContractFactory("EmptyVault"); - const newEmptyVault = await EmptyVault.deploy(vaultManagement.address); - await newEmptyVault.deployed(); + // deploy new rocket vault + const NewRocketVault = await ethers.getContractFactory("TestRocketVault"); + const newRocketVault = await NewRocketVault.deploy( + vaultManagement.address, + rocketPoolStorageAddress, + rocketVaultStorageAddress + ); + await newRocketVault.deployed(); // upgrade vault - - tx = await vaultManagement.upgradeVault(vaultNames.rocketVault, newEmptyVault.address); + tx = await vaultManagement.upgradeVault(vaultNames.rocketVault, newRocketVault.address); await tx.wait(); // check currentImplementation on rocketVaultStorage - expect(await rocketVaultStorage.owner()).to.equal(newEmptyVault.address); + expect(await rocketVaultStorage.owner()).to.equal(newRocketVault.address); // verify user balance has not changed expect(await vaultAccounting.userLPTokenBalance(user.address, vaultNames.rocketVault)).to.equal( expectedLpTokenAmount ); expect(await vaultAccounting.userETHBalance(user.address, vaultNames.rocketVault)).to.be.lte(expectedEthAmount); + + // fast forward through withdrawal time restriction + await hre.network.provider.send("hardhat_mine", ["0x2710"]); // 10000 blocks + + // verify that user can still withdraw + tx = await vaultAccounting.connect(user).withdraw(expectedLpTokenAmount, vaultNames.rocketVault); + await tx.wait(); + + // verify that delegate address rETH balance is correct + expect(await rETH.balanceOf(delegateAddress)).to.equal(0); }); it("should allow LP token deposits and reflect correct balance in delegate address", async () => { From 0c89801a2b1917e4d33eb12cc529a66a0fb82b75 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Wed, 25 May 2022 13:41:45 +0100 Subject: [PATCH 67/70] Use fixture constant --- test/fixture.js | 1 + test/vaults/RocketVault.js | 7 +++---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/fixture.js b/test/fixture.js index f100f14..4f12721 100644 --- a/test/fixture.js +++ b/test/fixture.js @@ -142,6 +142,7 @@ const fixture = deployments.createFixture(async () => { rootHash, deployerProof, userProof, + rocketAddresses, }; }); diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index ef4c604..e25687a 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -16,6 +16,7 @@ describe("Rocket Vault", async () => { let vaultAccounting; let vaultManagement; let vaultNames; + let rocketAddresses; // vault parameters let depositAmount; @@ -29,7 +30,6 @@ describe("Rocket Vault", async () => { let depositPoolAddress; let depositPool; let rocketDirectVault; - let rocketPoolStorageAddress; let rocketVaultStorageAddress; let rocketVaultStorage; let userAddressData; @@ -39,7 +39,7 @@ describe("Rocket Vault", async () => { beforeEach(async () => { // initialize fixture values - ({ vaultAccounting, vaultManagement, vaultNames } = await fixture()); + ({ vaultAccounting, vaultManagement, vaultNames, rocketAddresses } = await fixture()); [deployer, user, user2] = await ethers.getSigners(); // initialize vault parameters @@ -58,7 +58,6 @@ describe("Rocket Vault", async () => { "RocketVault", await vaultManagement.getVault(vaultNames.rocketVault) ); - rocketPoolStorageAddress = "0x1d8f8f00cfa6758d7bE78336684788Fb0ee0Fa46"; rocketVaultStorageAddress = await rocketDirectVault.getVaultStorage(); rocketVaultStorage = await ethers.getContractAt("RocketPoolVaultStorage", rocketVaultStorageAddress); @@ -187,7 +186,7 @@ describe("Rocket Vault", async () => { const NewRocketVault = await ethers.getContractFactory("TestRocketVault"); const newRocketVault = await NewRocketVault.deploy( vaultManagement.address, - rocketPoolStorageAddress, + rocketAddresses.rocketStorage, rocketVaultStorageAddress ); await newRocketVault.deployed(); From 0ca165feff07977a5b506f09c15e871a53d8d1bc Mon Sep 17 00:00:00 2001 From: ez7212 Date: Wed, 25 May 2022 09:56:36 -0400 Subject: [PATCH 68/70] change impersonate deposit amt --- test/vaults/RocketVault.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/vaults/RocketVault.js b/test/vaults/RocketVault.js index e25687a..9e6b0c7 100644 --- a/test/vaults/RocketVault.js +++ b/test/vaults/RocketVault.js @@ -84,7 +84,7 @@ describe("Rocket Vault", async () => { }); const signer = await ethers.getSigner(impersonateDepositAddress); - const depositEtherAmount = ethers.utils.parseEther("1000"); + const depositEtherAmount = ethers.utils.parseEther("1900"); const depositTx = await depositPool.connect(signer).deposit({ value: depositEtherAmount }); await depositTx.wait(); From c3835769f33c0b58a9c067ebcf3c450d801fc249 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Wed, 25 May 2022 15:18:40 +0100 Subject: [PATCH 69/70] Use RocketPoolVaultStorage interface --- contracts/interfaces/vaults/IRocketPoolVaultStorage.sol | 4 ++++ contracts/vaults/RocketPoolVault.sol | 6 ++++-- contracts/vaults/RocketPoolVaultStorage.sol | 8 ++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol index 9b197c3..7efd9ae 100644 --- a/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol +++ b/contracts/interfaces/vaults/IRocketPoolVaultStorage.sol @@ -5,4 +5,8 @@ interface IRocketPoolVaultStorage { function setDelegateAddress(address user, address delegateAddress) external; function getDelegateAddress(address user) external view returns (address); + + function transferOwnership(address newOwner) external; + + function owner() external view returns (address); } diff --git a/contracts/vaults/RocketPoolVault.sol b/contracts/vaults/RocketPoolVault.sol index 786411b..b57e4c4 100644 --- a/contracts/vaults/RocketPoolVault.sol +++ b/contracts/vaults/RocketPoolVault.sol @@ -6,6 +6,7 @@ import { IRocketDepositPool } from "../interfaces/vaults/IRocketDepositPool.sol" import { IRocketTokenRETH } from "../interfaces/vaults/IRocketTokenRETH.sol"; import { IRocketStorage } from "../interfaces/vaults/IRocketStorage.sol"; import { IRocketVault } from "../interfaces/vaults/IRocketVault.sol"; +import { IRocketPoolVaultStorage } from "../interfaces/vaults/IRocketPoolVaultStorage.sol"; import { RocketPoolVaultStorage } from "./RocketPoolVaultStorage.sol"; import { RocketPoolDelegate } from "./RocketPoolDelegate.sol"; @@ -14,11 +15,12 @@ contract RocketVault is BaseVault { IRocketStorage private rocketStorage; // storage for Rocket Vault - RocketPoolVaultStorage private rocketPoolVaultStorage; + IRocketPoolVaultStorage private rocketPoolVaultStorage; constructor(address _diamond, address _rocketStorageAddress) BaseVault(_diamond) { rocketStorage = IRocketStorage(_rocketStorageAddress); - rocketPoolVaultStorage = new RocketPoolVaultStorage(); // creates instance of RocketPoolVaultStorage and sets owner to this contract + // creates instance of RocketPoolVaultStorage and sets owner to this contract + rocketPoolVaultStorage = new RocketPoolVaultStorage(); } function deposit(bytes memory _data) external payable override onlyDiamond nonReentrant returns (uint256) { diff --git a/contracts/vaults/RocketPoolVaultStorage.sol b/contracts/vaults/RocketPoolVaultStorage.sol index b9c4236..7aca49d 100644 --- a/contracts/vaults/RocketPoolVaultStorage.sol +++ b/contracts/vaults/RocketPoolVaultStorage.sol @@ -12,7 +12,15 @@ contract RocketPoolVaultStorage is IRocketPoolVaultStorage, Ownable { delegate[user] = delegateAddress; } + function transferOwnership(address newOwner) public override(IRocketPoolVaultStorage, Ownable) onlyOwner { + super.transferOwnership(newOwner); + } + function getDelegateAddress(address user) external view override returns (address) { return delegate[user]; } + + function owner() public view override(IRocketPoolVaultStorage, Ownable) returns (address) { + return super.owner(); + } } From beb594b192db2d376ab2470dea2239505ee57038 Mon Sep 17 00:00:00 2001 From: Oozyx Date: Wed, 25 May 2022 15:19:14 +0100 Subject: [PATCH 70/70] Remove text file --- architectural_changes.txt | 22 ---------------------- 1 file changed, 22 deletions(-) delete mode 100644 architectural_changes.txt diff --git a/architectural_changes.txt b/architectural_changes.txt deleted file mode 100644 index 75ee4a9..0000000 --- a/architectural_changes.txt +++ /dev/null @@ -1,22 +0,0 @@ -Architectural changes: - -- Add new parameter to IVault functions: bytes32 _data, optional to be used by the vault. In this case, pass user - address. -- When depositing, create new proxy contract from vault associated with each depositor. This proxy contract will deposit - into rocket pool. -- Create a mapping of user address to proxy contract. -- When withdrawing, withdraw from user's proxy contract. -- If user has an existing proxy contract associated, then reuse. -- Problem: how to upgrade vault? -- Possible solution - 3 contracts: - - RocketPoolVault (implements IVault) - Sets storage variable in RocketPoolVaultStorage (currentImplementation - - RocketPoolVaultStorage - Holds storage variable currentImplementation - Holds mapping of user to delegate contracts - - RocketPoolDelegate (2 functions, deposit, withdraw) - Holds instance of RocketPoolVaultStorage - Function modifier on both functions currentVaultImplementation - currentVaultImplementation checks value stored in RocketPoolVaultStorage - When upgrading, currentImplementation in RocketPoolVaultStorage gets set to new vault implementation