From 53f90b4d80e47182839a105be8816c4d30d1d5ee Mon Sep 17 00:00:00 2001 From: coolnft Date: Tue, 13 Dec 2022 10:11:54 -0500 Subject: [PATCH 01/22] Feat add erc20 wrapping delegate --- contracts/midas/CErc20WrappingDelegate.sol | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 contracts/midas/CErc20WrappingDelegate.sol diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol new file mode 100644 index 000000000..78fad02af --- /dev/null +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import { CErc20Delegate } from "../compound/CErc20Delegate.sol"; +import { EIP20Interface } from "../compound/EIP20Interface.sol"; +import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; + +contract CErc20WrappingDelegate is CErc20Delegate { + ERC20Wrapper public wrappedUnderlying; + + function _becomeImplementation(bytes memory data) public virtual override { + require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); + + address _wrappingUnderlying = abi.decode(data, (address)); + + if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(_wrappingUnderlying)) { + wrappedUnderlying = ERC20Wrapper(_wrappingUnderlying); + } + } + + function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { + require(EIP20Interface(underlying).transferFrom(from, address(this), amount), "send"); + + wrappedUnderlying.depositFor(address(this), amount); + return amount; + } + + function doTransferOut(address to, uint256 amount) internal virtual override { + wrappedUnderlying.withdrawTo(to, amount); + } + + function contractType() external pure virtual override returns (string memory) { + return "CErc20WrappingDelegate"; + } +} From ed65271d3bbabdec64aae2b2dd77e6664605ab70 Mon Sep 17 00:00:00 2001 From: coolnft Date: Tue, 13 Dec 2022 10:21:13 -0500 Subject: [PATCH 02/22] add infinite approval --- contracts/midas/CErc20WrappingDelegate.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 78fad02af..52b5f4491 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -15,7 +15,8 @@ contract CErc20WrappingDelegate is CErc20Delegate { if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(_wrappingUnderlying)) { wrappedUnderlying = ERC20Wrapper(_wrappingUnderlying); - } + ERC20Wrapper(underlying).approve(_wrappingUnderlying, type(uint256).max); + } } function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { From 53969e5dfd1317c7ceb69e2db617657689cfcd2a Mon Sep 17 00:00:00 2001 From: coolnft Date: Wed, 14 Dec 2022 07:00:46 -0500 Subject: [PATCH 03/22] prettier issue fixed. --- contracts/midas/CErc20WrappingDelegate.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 52b5f4491..616b01e58 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -16,7 +16,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(_wrappingUnderlying)) { wrappedUnderlying = ERC20Wrapper(_wrappingUnderlying); ERC20Wrapper(underlying).approve(_wrappingUnderlying, type(uint256).max); - } + } } function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { From 1cbab65b4ff1d382c3f9d53c76459f2c0b4fbf7b Mon Sep 17 00:00:00 2001 From: coolnft Date: Thu, 15 Dec 2022 07:09:35 -0500 Subject: [PATCH 04/22] update CErc20Wrapping implementation --- contracts/FuseFeeDistributor.sol | 47 ++++++++++++++++++++++ contracts/compound/IFuseFeeDistributor.sol | 7 ++++ contracts/midas/CErc20WrappingDelegate.sol | 46 ++++++++++++++++++--- 3 files changed, 94 insertions(+), 6 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 279642a5d..ce5611071 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -10,6 +10,7 @@ import "./compound/ErrorReporter.sol"; import "./external/compound/IComptroller.sol"; import "./compound/CErc20Delegator.sol"; import "./compound/CErc20PluginDelegate.sol"; +import "./midas/CErc20WrappingDelegate.sol"; import "./midas/SafeOwnableUpgradeable.sol"; import "./utils/PatchedStorage.sol"; import "./oracles/BasePriceOracle.sol"; @@ -319,11 +320,21 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => address) public _latestPluginImplementation; + /** + * @dev Latest erc20Wrapping implementation for each existing implementation. + */ + mapping(address => address) public _latestERC20WrappingImplementation; + /** * @dev Whitelisted Plugin implementation contract addresses for each existing implementation. */ mapping(address => mapping(address => bool)) public pluginImplementationWhitelist; + /** + * @dev Whitelisted erc20Wrapping implementation contract addresses for each existing implementation. + */ + mapping(address => mapping(address => bool)) public erc20WrappingImplementationWhitelist; + /** * @dev Adds/removes plugin implementations to the whitelist. * @param oldImplementations The old plugin implementation addresses to upgrade from for each `newImplementations` to upgrade to. @@ -345,6 +356,21 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { pluginImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; } + function _editERC20WrappingImplementationWhitelist( + address[] calldata oldImplementations, + address[] calldata newImplementations, + bool[] calldata statuses + ) external onlyOwner { + require( + newImplementations.length > 0 && + newImplementations.length == oldImplementations.length && + newImplementations.length == statuses.length, + "No erc20Wrapping implementations supplied or array lengths not equal." + ); + for (uint256 i = 0; i < newImplementations.length; i++) + erc20WrappingImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; + } + /** * @dev Latest Plugin implementation for each existing implementation. */ @@ -364,6 +390,17 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { _latestPluginImplementation[oldImplementation] = newImplementation; } + function latestERC20WrappingImplementation(address oldImplementation) external view returns (address) { + return + _latestERC20WrappingImplementation[oldImplementation] != address(0) + ? _latestERC20WrappingImplementation[oldImplementation] + : oldImplementation; + } + + function _setLatestERC20WrappingImplementation(address oldImplementation, address newImplementation) external onlyOwner { + _latestERC20WrappingImplementation[oldImplementation] = newImplementation; + } + /** * @dev Upgrades a plugin of a CErc20PluginDelegate market to the latest implementation * @param cDelegator the proxy address @@ -379,6 +416,16 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { return newPluginAddress != oldPluginAddress; } + function _upgradeERC20WrappingToLatestImplementation(address cDelegator) external onlyOwner returns (bool) { + CErc20WrappingDelegate market = CErc20WrappingDelegate(cDelegator); + + address oldDelegator = address(market.wrappingUnderlying()); + market._updateUnderlying(_latestERC20WrappingImplementation[oldDelegator]); + address newDelegator = address(market.wrappingUnderlying()); + + return newDelegator != oldDelegator; + } + /** * @notice Returns the proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). */ diff --git a/contracts/compound/IFuseFeeDistributor.sol b/contracts/compound/IFuseFeeDistributor.sol index 63d5344f7..bb21c5e78 100644 --- a/contracts/compound/IFuseFeeDistributor.sol +++ b/contracts/compound/IFuseFeeDistributor.sol @@ -20,6 +20,11 @@ interface IFuseFeeDistributor { view returns (bool); + function erc20WrappingImplementationWhitelist(address oldImplementation, address newImplementation) + external + view + returns (bool); + function cErc20DelegateWhitelist( address oldImplementation, address newImplementation, @@ -39,6 +44,8 @@ interface IFuseFeeDistributor { function latestPluginImplementation(address oldImplementation) external view returns (address); + function latestERC20WrappingImplementation(address oldImplementation) external view returns (address); + function getComptrollerExtensions(address comptroller) external view returns (address[] memory); function getCErc20DelegateExtensions(address cErc20Delegate) external view returns (address[] memory); diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 616b01e58..896e2fad7 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -3,31 +3,65 @@ pragma solidity >=0.8.0; import { CErc20Delegate } from "../compound/CErc20Delegate.sol"; import { EIP20Interface } from "../compound/EIP20Interface.sol"; +import { IFuseFeeDistributor } from "../compound/IFuseFeeDistributor.sol"; + import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; contract CErc20WrappingDelegate is CErc20Delegate { - ERC20Wrapper public wrappedUnderlying; + event NewErc20WrappingImplementation(address oldImpl, address newImpl); + + ERC20Wrapper public wrappingUnderlying; function _becomeImplementation(bytes memory data) public virtual override { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); address _wrappingUnderlying = abi.decode(data, (address)); - if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(_wrappingUnderlying)) { - wrappedUnderlying = ERC20Wrapper(_wrappingUnderlying); - ERC20Wrapper(underlying).approve(_wrappingUnderlying, type(uint256).max); + if (_wrappingUnderlying == address(0) && address(wrappingUnderlying) != address(0)) { + _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrappingImplementation(address(wrappingUnderlying)); + } + + if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(wrappingUnderlying)) { + _updateUnderlying(_wrappingUnderlying); } } + function _updateUnderlying(address _wrappingUnderlying) public { + require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateUnderlying"); + + address oldImplementation = address(wrappingUnderlying) != address(0) ? address(wrappingUnderlying) : _wrappingUnderlying; + + require( + IFuseFeeDistributor(fuseAdmin).erc20WrappingImplementationWhitelist(oldImplementation, _wrappingUnderlying), + "erc20Wrapping implementation not whitelisted" + ); + + if (address(wrappingUnderlying) != address(0) && wrappingUnderlying.balanceOf(address(this)) != 0) { + doTransferOut(address(this), wrappingUnderlying.balanceOf(address(this))); + } + + wrappingUnderlying = ERC20Wrapper(_wrappingUnderlying); + + EIP20Interface(underlying).approve(_wrappingUnderlying, type(uint256).max); + + uint256 amount = EIP20Interface(underlying).balanceOf(address(this)); + + if (amount != 0) { + doTransferIn(address(this), amount); + } + + emit NewErc20WrappingImplementation(address(wrappingUnderlying), _wrappingUnderlying); + } + function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { require(EIP20Interface(underlying).transferFrom(from, address(this), amount), "send"); - wrappedUnderlying.depositFor(address(this), amount); + wrappingUnderlying.depositFor(address(this), amount); return amount; } function doTransferOut(address to, uint256 amount) internal virtual override { - wrappedUnderlying.withdrawTo(to, amount); + wrappingUnderlying.withdrawTo(to, amount); } function contractType() external pure virtual override returns (string memory) { From 8904e21ad96dbff742c0e57b9107efe2bf6a4c4e Mon Sep 17 00:00:00 2001 From: coolnft Date: Thu, 15 Dec 2022 07:12:15 -0500 Subject: [PATCH 05/22] prettier issue fixed. --- contracts/FuseFeeDistributor.sol | 5 ++++- contracts/midas/CErc20WrappingDelegate.sol | 10 +++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index ce5611071..09225eb6e 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -397,7 +397,10 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { : oldImplementation; } - function _setLatestERC20WrappingImplementation(address oldImplementation, address newImplementation) external onlyOwner { + function _setLatestERC20WrappingImplementation(address oldImplementation, address newImplementation) + external + onlyOwner + { _latestERC20WrappingImplementation[oldImplementation] = newImplementation; } diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 896e2fad7..6da00bd13 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -9,7 +9,7 @@ import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC2 contract CErc20WrappingDelegate is CErc20Delegate { event NewErc20WrappingImplementation(address oldImpl, address newImpl); - + ERC20Wrapper public wrappingUnderlying; function _becomeImplementation(bytes memory data) public virtual override { @@ -18,7 +18,9 @@ contract CErc20WrappingDelegate is CErc20Delegate { address _wrappingUnderlying = abi.decode(data, (address)); if (_wrappingUnderlying == address(0) && address(wrappingUnderlying) != address(0)) { - _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrappingImplementation(address(wrappingUnderlying)); + _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrappingImplementation( + address(wrappingUnderlying) + ); } if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(wrappingUnderlying)) { @@ -29,7 +31,9 @@ contract CErc20WrappingDelegate is CErc20Delegate { function _updateUnderlying(address _wrappingUnderlying) public { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateUnderlying"); - address oldImplementation = address(wrappingUnderlying) != address(0) ? address(wrappingUnderlying) : _wrappingUnderlying; + address oldImplementation = address(wrappingUnderlying) != address(0) + ? address(wrappingUnderlying) + : _wrappingUnderlying; require( IFuseFeeDistributor(fuseAdmin).erc20WrappingImplementationWhitelist(oldImplementation, _wrappingUnderlying), From 5d4a332e9115ad8c7a769de247ff06d25e4a1753 Mon Sep 17 00:00:00 2001 From: coolnft Date: Thu, 15 Dec 2022 07:27:25 -0500 Subject: [PATCH 06/22] variable order updated. --- contracts/FuseFeeDistributor.sol | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 09225eb6e..5cf316795 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -320,10 +320,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => address) public _latestPluginImplementation; - /** - * @dev Latest erc20Wrapping implementation for each existing implementation. - */ - mapping(address => address) public _latestERC20WrappingImplementation; /** * @dev Whitelisted Plugin implementation contract addresses for each existing implementation. @@ -335,6 +331,11 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => mapping(address => bool)) public erc20WrappingImplementationWhitelist; + /** + * @dev Latest erc20Wrapping implementation for each existing implementation. + */ + mapping(address => address) public _latestERC20WrappingImplementation; + /** * @dev Adds/removes plugin implementations to the whitelist. * @param oldImplementations The old plugin implementation addresses to upgrade from for each `newImplementations` to upgrade to. From 50e4bd6e53944d4c3a1cf68b32e7844dc845b10f Mon Sep 17 00:00:00 2001 From: coolnft Date: Thu, 15 Dec 2022 07:30:18 -0500 Subject: [PATCH 07/22] prettier issue fixed. --- contracts/FuseFeeDistributor.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 5cf316795..c3017a30f 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -320,7 +320,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => address) public _latestPluginImplementation; - /** * @dev Whitelisted Plugin implementation contract addresses for each existing implementation. */ From 7fac66b664b555cd1762c63752e94e0009db84c5 Mon Sep 17 00:00:00 2001 From: coolnft Date: Fri, 16 Dec 2022 07:56:42 -0500 Subject: [PATCH 08/22] fuse fee distributor variable naming changes --- contracts/FuseFeeDistributor.sol | 41 +++++++++++----------- contracts/compound/IFuseFeeDistributor.sol | 4 +-- contracts/midas/CErc20WrappingDelegate.sol | 4 +-- 3 files changed, 25 insertions(+), 24 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index c3017a30f..04a644d48 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -325,15 +325,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => mapping(address => bool)) public pluginImplementationWhitelist; - /** - * @dev Whitelisted erc20Wrapping implementation contract addresses for each existing implementation. - */ - mapping(address => mapping(address => bool)) public erc20WrappingImplementationWhitelist; - - /** - * @dev Latest erc20Wrapping implementation for each existing implementation. - */ - mapping(address => address) public _latestERC20WrappingImplementation; /** * @dev Adds/removes plugin implementations to the whitelist. @@ -356,7 +347,7 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { pluginImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; } - function _editERC20WrappingImplementationWhitelist( + function _editERC20WrapperUpgradeWhitelist( address[] calldata oldImplementations, address[] calldata newImplementations, bool[] calldata statuses @@ -368,7 +359,7 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { "No erc20Wrapping implementations supplied or array lengths not equal." ); for (uint256 i = 0; i < newImplementations.length; i++) - erc20WrappingImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; + erc20WrapperUpgradeWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; } /** @@ -390,18 +381,18 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { _latestPluginImplementation[oldImplementation] = newImplementation; } - function latestERC20WrappingImplementation(address oldImplementation) external view returns (address) { + function latestERC20WrapperForUnderlying(address oldImplementation) external view returns (address) { return - _latestERC20WrappingImplementation[oldImplementation] != address(0) - ? _latestERC20WrappingImplementation[oldImplementation] + _latestERC20WrapperForUnderlying[oldImplementation] != address(0) + ? _latestERC20WrapperForUnderlying[oldImplementation] : oldImplementation; } - function _setLatestERC20WrappingImplementation(address oldImplementation, address newImplementation) + function _setLatestERC20WrapperForUnderlying(address oldImplementation, address newImplementation) external onlyOwner { - _latestERC20WrappingImplementation[oldImplementation] = newImplementation; + _latestERC20WrapperForUnderlying[oldImplementation] = newImplementation; } /** @@ -422,11 +413,11 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { function _upgradeERC20WrappingToLatestImplementation(address cDelegator) external onlyOwner returns (bool) { CErc20WrappingDelegate market = CErc20WrappingDelegate(cDelegator); - address oldDelegator = address(market.wrappingUnderlying()); - market._updateUnderlying(_latestERC20WrappingImplementation[oldDelegator]); - address newDelegator = address(market.wrappingUnderlying()); + address oldWrapper = address(market.wrappingUnderlying()); + market._updateUnderlying(_latestERC20WrapperForUnderlying[oldWrapper]); + address newWrapper = address(market.wrappingUnderlying()); - return newDelegator != oldDelegator; + return newWrapper != oldWrapper; } /** @@ -475,6 +466,16 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { mapping(address => DiamondExtension[]) public cErc20DelegateExtensions; + /** + * @dev Whitelisted erc20Wrapping implementation contract addresses for each existing implementation. + */ + mapping(address => mapping(address => bool)) public erc20WrapperUpgradeWhitelist; + + /** + * @dev Latest erc20Wrapping implementation for each existing implementation. + */ + mapping(address => address) public _latestERC20WrapperForUnderlying; + function getCErc20DelegateExtensions(address cErc20Delegate) external view returns (DiamondExtension[] memory) { return cErc20DelegateExtensions[cErc20Delegate]; } diff --git a/contracts/compound/IFuseFeeDistributor.sol b/contracts/compound/IFuseFeeDistributor.sol index bb21c5e78..37b41c7a2 100644 --- a/contracts/compound/IFuseFeeDistributor.sol +++ b/contracts/compound/IFuseFeeDistributor.sol @@ -20,7 +20,7 @@ interface IFuseFeeDistributor { view returns (bool); - function erc20WrappingImplementationWhitelist(address oldImplementation, address newImplementation) + function erc20WrapperUpgradeWhitelist(address oldImplementation, address newImplementation) external view returns (bool); @@ -44,7 +44,7 @@ interface IFuseFeeDistributor { function latestPluginImplementation(address oldImplementation) external view returns (address); - function latestERC20WrappingImplementation(address oldImplementation) external view returns (address); + function latestERC20WrapperForUnderlying(address oldImplementation) external view returns (address); function getComptrollerExtensions(address comptroller) external view returns (address[] memory); diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 6da00bd13..7aafbe731 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -18,7 +18,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { address _wrappingUnderlying = abi.decode(data, (address)); if (_wrappingUnderlying == address(0) && address(wrappingUnderlying) != address(0)) { - _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrappingImplementation( + _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying( address(wrappingUnderlying) ); } @@ -36,7 +36,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { : _wrappingUnderlying; require( - IFuseFeeDistributor(fuseAdmin).erc20WrappingImplementationWhitelist(oldImplementation, _wrappingUnderlying), + IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _wrappingUnderlying), "erc20Wrapping implementation not whitelisted" ); From 961fdb6b470fb8b01b90632322d3946868b1bca4 Mon Sep 17 00:00:00 2001 From: coolnft Date: Fri, 16 Dec 2022 08:00:28 -0500 Subject: [PATCH 09/22] prettier issues fixed. --- contracts/FuseFeeDistributor.sol | 1 - contracts/midas/CErc20WrappingDelegate.sol | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 04a644d48..7427f56f8 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -325,7 +325,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { */ mapping(address => mapping(address => bool)) public pluginImplementationWhitelist; - /** * @dev Adds/removes plugin implementations to the whitelist. * @param oldImplementations The old plugin implementation addresses to upgrade from for each `newImplementations` to upgrade to. diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 7aafbe731..ac3dcf8c7 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -18,9 +18,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { address _wrappingUnderlying = abi.decode(data, (address)); if (_wrappingUnderlying == address(0) && address(wrappingUnderlying) != address(0)) { - _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying( - address(wrappingUnderlying) - ); + _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(wrappingUnderlying)); } if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(wrappingUnderlying)) { From e41001aa2434dba76287dfed5ce75aab1470425d Mon Sep 17 00:00:00 2001 From: coolnft Date: Fri, 16 Dec 2022 08:18:24 -0500 Subject: [PATCH 10/22] naming changes --- contracts/FuseFeeDistributor.sol | 6 ++-- contracts/midas/CErc20WrappingDelegate.sol | 38 +++++++++++----------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 7427f56f8..3c1ae8bf4 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -412,9 +412,9 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { function _upgradeERC20WrappingToLatestImplementation(address cDelegator) external onlyOwner returns (bool) { CErc20WrappingDelegate market = CErc20WrappingDelegate(cDelegator); - address oldWrapper = address(market.wrappingUnderlying()); - market._updateUnderlying(_latestERC20WrapperForUnderlying[oldWrapper]); - address newWrapper = address(market.wrappingUnderlying()); + address oldWrapper = address(market.underlyingWrapper()); + market._updateWrapper(_latestERC20WrapperForUnderlying[oldWrapper]); + address newWrapper = address(market.underlyingWrapper()); return newWrapper != oldWrapper; } diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index ac3dcf8c7..ef24baae5 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -10,41 +10,41 @@ import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC2 contract CErc20WrappingDelegate is CErc20Delegate { event NewErc20WrappingImplementation(address oldImpl, address newImpl); - ERC20Wrapper public wrappingUnderlying; + ERC20Wrapper public underlyingWrapper; function _becomeImplementation(bytes memory data) public virtual override { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); - address _wrappingUnderlying = abi.decode(data, (address)); + address _underlyingWrapper = abi.decode(data, (address)); - if (_wrappingUnderlying == address(0) && address(wrappingUnderlying) != address(0)) { - _wrappingUnderlying = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(wrappingUnderlying)); + if (_underlyingWrapper == address(0) && address(underlyingWrapper) != address(0)) { + _underlyingWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); } - if (_wrappingUnderlying != address(0) && _wrappingUnderlying != address(wrappingUnderlying)) { - _updateUnderlying(_wrappingUnderlying); + if (_underlyingWrapper != address(0) && _underlyingWrapper != address(underlyingWrapper)) { + _updateWrapper(_underlyingWrapper); } } - function _updateUnderlying(address _wrappingUnderlying) public { - require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateUnderlying"); + function _updateWrapper(address _underlyingWrapper) public { + require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateWrapper"); - address oldImplementation = address(wrappingUnderlying) != address(0) - ? address(wrappingUnderlying) - : _wrappingUnderlying; + address oldImplementation = address(underlyingWrapper) != address(0) + ? address(underlyingWrapper) + : _underlyingWrapper; require( - IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _wrappingUnderlying), + IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _underlyingWrapper), "erc20Wrapping implementation not whitelisted" ); - if (address(wrappingUnderlying) != address(0) && wrappingUnderlying.balanceOf(address(this)) != 0) { - doTransferOut(address(this), wrappingUnderlying.balanceOf(address(this))); + if (address(underlyingWrapper) != address(0) && underlyingWrapper.balanceOf(address(this)) != 0) { + doTransferOut(address(this), underlyingWrapper.balanceOf(address(this))); } - wrappingUnderlying = ERC20Wrapper(_wrappingUnderlying); + underlyingWrapper = ERC20Wrapper(_underlyingWrapper); - EIP20Interface(underlying).approve(_wrappingUnderlying, type(uint256).max); + EIP20Interface(underlying).approve(_underlyingWrapper, type(uint256).max); uint256 amount = EIP20Interface(underlying).balanceOf(address(this)); @@ -52,18 +52,18 @@ contract CErc20WrappingDelegate is CErc20Delegate { doTransferIn(address(this), amount); } - emit NewErc20WrappingImplementation(address(wrappingUnderlying), _wrappingUnderlying); + emit NewErc20WrappingImplementation(address(underlyingWrapper), _underlyingWrapper); } function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { require(EIP20Interface(underlying).transferFrom(from, address(this), amount), "send"); - wrappingUnderlying.depositFor(address(this), amount); + underlyingWrapper.depositFor(address(this), amount); return amount; } function doTransferOut(address to, uint256 amount) internal virtual override { - wrappingUnderlying.withdrawTo(to, amount); + underlyingWrapper.withdrawTo(to, amount); } function contractType() external pure virtual override returns (string memory) { From 4a43e3bfe9635315cbe1a121b915a8aff42ec7e8 Mon Sep 17 00:00:00 2001 From: coolnft Date: Fri, 16 Dec 2022 08:45:53 -0500 Subject: [PATCH 11/22] naming changes --- contracts/midas/CErc20WrappingDelegate.sol | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index ef24baae5..5d7b0eb9a 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -15,26 +15,26 @@ contract CErc20WrappingDelegate is CErc20Delegate { function _becomeImplementation(bytes memory data) public virtual override { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); - address _underlyingWrapper = abi.decode(data, (address)); + address _newWrapper = abi.decode(data, (address)); - if (_underlyingWrapper == address(0) && address(underlyingWrapper) != address(0)) { - _underlyingWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); + if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { + _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); } - if (_underlyingWrapper != address(0) && _underlyingWrapper != address(underlyingWrapper)) { - _updateWrapper(_underlyingWrapper); + if (_newWrapper != address(0) && _newWrapper != address(underlyingWrapper)) { + _updateWrapper(_newWrapper); } } - function _updateWrapper(address _underlyingWrapper) public { + function _updateWrapper(address _newWrapper) public { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateWrapper"); address oldImplementation = address(underlyingWrapper) != address(0) ? address(underlyingWrapper) - : _underlyingWrapper; + : _newWrapper; require( - IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _underlyingWrapper), + IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _newWrapper), "erc20Wrapping implementation not whitelisted" ); @@ -42,9 +42,9 @@ contract CErc20WrappingDelegate is CErc20Delegate { doTransferOut(address(this), underlyingWrapper.balanceOf(address(this))); } - underlyingWrapper = ERC20Wrapper(_underlyingWrapper); + underlyingWrapper = ERC20Wrapper(_newWrapper); - EIP20Interface(underlying).approve(_underlyingWrapper, type(uint256).max); + EIP20Interface(underlying).approve(_newWrapper, type(uint256).max); uint256 amount = EIP20Interface(underlying).balanceOf(address(this)); @@ -52,7 +52,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { doTransferIn(address(this), amount); } - emit NewErc20WrappingImplementation(address(underlyingWrapper), _underlyingWrapper); + emit NewErc20WrappingImplementation(address(underlyingWrapper), _newWrapper); } function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { From 7e301e85cf78f6139c2dac06eb44e204aa42fba7 Mon Sep 17 00:00:00 2001 From: coolnft Date: Fri, 16 Dec 2022 08:47:43 -0500 Subject: [PATCH 12/22] prettier issue fixed. --- contracts/midas/CErc20WrappingDelegate.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 5d7b0eb9a..2ce1ca66c 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -29,9 +29,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { function _updateWrapper(address _newWrapper) public { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _updateWrapper"); - address oldImplementation = address(underlyingWrapper) != address(0) - ? address(underlyingWrapper) - : _newWrapper; + address oldImplementation = address(underlyingWrapper) != address(0) ? address(underlyingWrapper) : _newWrapper; require( IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _newWrapper), From d5d054cf4bd56f69197d3330ed25609f24425b93 Mon Sep 17 00:00:00 2001 From: coolnft Date: Mon, 19 Dec 2022 10:31:57 -0500 Subject: [PATCH 13/22] MidasERC20wrapper added. --- contracts/midas/MidasERC20Wrapper.sol | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 contracts/midas/MidasERC20Wrapper.sol diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol new file mode 100644 index 000000000..27f84cf68 --- /dev/null +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity >=0.8.0; + +import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; +import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; + +contract MidasERC20Wrapper is ERC20Wrapper { + + address private owner; + + constructor(IERC20 underlyingToken) ERC20Wrapper(underlyingToken) { + owner = msg.sender; + } + + function recover() public returns (uint256) { + return _recover(msg.sender); + } +} From 37023e9537ab47111ee8845f929d061044a03583 Mon Sep 17 00:00:00 2001 From: coolnft Date: Tue, 20 Dec 2022 07:19:59 -0500 Subject: [PATCH 14/22] update minor issue --- contracts/midas/MidasERC20Wrapper.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index 27f84cf68..0b172f70e 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -13,6 +13,6 @@ contract MidasERC20Wrapper is ERC20Wrapper { } function recover() public returns (uint256) { - return _recover(msg.sender); + return _recover(owner); } } From a6c70b11268fa4d0e3aec233a0f966fb29d0ca9a Mon Sep 17 00:00:00 2001 From: coolnft Date: Tue, 20 Dec 2022 07:28:22 -0500 Subject: [PATCH 15/22] prettier issue fixed. --- contracts/midas/MidasERC20Wrapper.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index 0b172f70e..be439cbf9 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -5,7 +5,6 @@ import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC2 import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; contract MidasERC20Wrapper is ERC20Wrapper { - address private owner; constructor(IERC20 underlyingToken) ERC20Wrapper(underlyingToken) { From b8b2beb9ad46849744b83d44bdec3a61ca102a2c Mon Sep 17 00:00:00 2001 From: coolnft Date: Tue, 20 Dec 2022 08:43:48 -0500 Subject: [PATCH 16/22] erc20 wrapping delegate updated. --- contracts/midas/CErc20WrappingDelegate.sol | 21 ++++++++++++--------- contracts/midas/MidasERC20Wrapper.sol | 13 +++++++++++-- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/midas/CErc20WrappingDelegate.sol index 2ce1ca66c..1f0e53ca3 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/midas/CErc20WrappingDelegate.sol @@ -4,25 +4,28 @@ pragma solidity >=0.8.0; import { CErc20Delegate } from "../compound/CErc20Delegate.sol"; import { EIP20Interface } from "../compound/EIP20Interface.sol"; import { IFuseFeeDistributor } from "../compound/IFuseFeeDistributor.sol"; - -import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; +import { MidasERC20Wrapper, IERC20 } from "./MidasERC20Wrapper.sol"; contract CErc20WrappingDelegate is CErc20Delegate { event NewErc20WrappingImplementation(address oldImpl, address newImpl); - ERC20Wrapper public underlyingWrapper; + MidasERC20Wrapper public underlyingWrapper; function _becomeImplementation(bytes memory data) public virtual override { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); address _newWrapper = abi.decode(data, (address)); - if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { - _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); - } + if (address(underlyingWrapper) == address(0)) { + underlyingWrapper = new MidasERC20Wrapper(IERC20(underlying)); + } else { + if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { + _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); + } - if (_newWrapper != address(0) && _newWrapper != address(underlyingWrapper)) { - _updateWrapper(_newWrapper); + if (_newWrapper != address(0) && _newWrapper != address(underlyingWrapper)) { + _updateWrapper(_newWrapper); + } } } @@ -40,7 +43,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { doTransferOut(address(this), underlyingWrapper.balanceOf(address(this))); } - underlyingWrapper = ERC20Wrapper(_newWrapper); + underlyingWrapper = MidasERC20Wrapper(_newWrapper); EIP20Interface(underlying).approve(_newWrapper, type(uint256).max); diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index be439cbf9..9c3ebedff 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -1,13 +1,22 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.0; -import { ERC20Wrapper } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; +import { ERC20Wrapper, ERC20 } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; +interface IERC20Wrapper is IERC20 { + function name() external view returns (string memory); + + function symbol() external view returns (string memory); +} + contract MidasERC20Wrapper is ERC20Wrapper { address private owner; - constructor(IERC20 underlyingToken) ERC20Wrapper(underlyingToken) { + constructor(IERC20 underlyingToken) + ERC20(IERC20Wrapper(address(underlyingToken)).name(), IERC20Wrapper(address(underlyingToken)).symbol()) + ERC20Wrapper(underlyingToken) + { owner = msg.sender; } From 86ae4acadb26db1653b56ae624031f028ea58111 Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Wed, 4 Jan 2023 13:41:03 +0200 Subject: [PATCH 17/22] refactoring the wrapping delegate --- contracts/FuseFeeDistributor.sol | 235 +++++++++--------- contracts/compound/CErc20PluginDelegate.sol | 4 +- .../CErc20WrappingDelegate.sol | 18 +- contracts/midas/MidasERC20Wrapper.sol | 24 +- contracts/test/ContractsUpgradesTest.t.sol | 11 +- 5 files changed, 149 insertions(+), 143 deletions(-) rename contracts/{midas => compound}/CErc20WrappingDelegate.sol (86%) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 3c1ae8bf4..9ac1e9be5 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -10,18 +10,116 @@ import "./compound/ErrorReporter.sol"; import "./external/compound/IComptroller.sol"; import "./compound/CErc20Delegator.sol"; import "./compound/CErc20PluginDelegate.sol"; -import "./midas/CErc20WrappingDelegate.sol"; +import "./compound/CErc20WrappingDelegate.sol"; import "./midas/SafeOwnableUpgradeable.sol"; import "./utils/PatchedStorage.sol"; import "./oracles/BasePriceOracle.sol"; import { DiamondExtension, DiamondBase } from "./midas/DiamondExtension.sol"; +struct CDelegateUpgradeData { + address implementation; + bool allowResign; + bytes becomeImplementationData; +} + +abstract contract FeeDistributorStorage { + + /** + * @notice The proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). + */ + uint256 public defaultInterestFeeRate; + + /** + * @dev Minimum borrow balance (in ETH) per user per Fuse pool asset (only checked on new borrows, not redemptions). + */ + uint256 public minBorrowEth; + + /** + * @dev Maximum supply balance (in ETH) per user per Fuse pool asset. + * No longer used as of `Rari-Capital/compound-protocol` version `fuse-v1.1.0`. + */ + uint256 public maxSupplyEth; + + /** + * @dev Maximum utilization rate (scaled by 1e18) for Fuse pool assets (only checked on new borrows, not redemptions). + * No longer used as of `Rari-Capital/compound-protocol` version `fuse-v1.1.0`. + */ + uint256 public maxUtilizationRate; + + /** + * @dev Whitelisted Comptroller implementation contract addresses for each existing implementation. + */ + mapping(address => mapping(address => bool)) public comptrollerImplementationWhitelist; + + /** + * @dev Whitelisted CErc20Delegate implementation contract addresses and `allowResign` values for each existing implementation. + */ + mapping(address => mapping(address => mapping(bool => bool))) public cErc20DelegateWhitelist; + + /** + * @dev Whitelisted CEtherDelegate implementation contract addresses and `allowResign` values for each existing implementation. + */ + /// keep this in the storage to not break the layout + mapping(address => mapping(address => mapping(bool => bool))) public cEtherDelegateWhitelist; + + /** + * @dev Latest Comptroller implementation for each existing implementation. + */ + mapping(address => address) internal _latestComptrollerImplementation; + /** + * @dev Latest CErc20Delegate implementation for each existing implementation. + */ + mapping(address => CDelegateUpgradeData) public _latestCErc20Delegate; + + /** + * @dev Latest CEtherDelegate implementation for each existing implementation. + */ + /// keep this in the storage to not break the layout + mapping(address => CDelegateUpgradeData) public _latestCEtherDelegate; + + /** + * @notice Maps Unitroller (Comptroller proxy) addresses to the proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). + * @dev A value of 0 means unset whereas a negative value means 0. + */ + mapping(address => int256) public customInterestFeeRates; + + /** + * @dev used as salt for the creation of new markets + */ + uint256 public marketsCounter; + + /** + * @dev Latest Plugin implementation for each existing implementation. + */ + mapping(address => address) public _latestPluginImplementation; + + /** + * @dev Whitelisted Plugin implementation contract addresses for each existing implementation. + */ + mapping(address => mapping(address => bool)) public pluginImplementationWhitelist; + + mapping(address => DiamondExtension[]) public comptrollerExtensions; + + mapping(address => DiamondExtension[]) public cErc20DelegateExtensions; + + /** + * @dev Whitelisted erc20Wrapping implementation contract addresses for each existing implementation. + */ + mapping(address => mapping(address => bool)) public erc20WrapperUpgradeWhitelist; + + /** + * @dev Latest erc20Wrapping implementation for each existing implementation. + */ + mapping(address => address) public _latestERC20WrapperForUnderlying; + +} + /** * @title FuseFeeDistributor * @author David Lucid (https://github.com/davidlucid) * @notice FuseFeeDistributor controls and receives protocol fees from Fuse pools and relays admin actions to Fuse pools. */ -contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { +contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage, FeeDistributorStorage { using AddressUpgradeable for address; using SafeERC20Upgradeable for IERC20Upgradeable; @@ -37,11 +135,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { maxUtilizationRate = type(uint256).max; } - /** - * @notice The proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). - */ - uint256 public defaultInterestFeeRate; - /** * @dev Sets the default proportion of Fuse pool interest taken as a protocol fee. * @param _defaultInterestFeeRate The default proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). @@ -69,23 +162,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { } } - /** - * @dev Minimum borrow balance (in ETH) per user per Fuse pool asset (only checked on new borrows, not redemptions). - */ - uint256 public minBorrowEth; - - /** - * @dev Maximum supply balance (in ETH) per user per Fuse pool asset. - * No longer used as of `Rari-Capital/compound-protocol` version `fuse-v1.1.0`. - */ - uint256 public maxSupplyEth; - - /** - * @dev Maximum utilization rate (scaled by 1e18) for Fuse pool assets (only checked on new borrows, not redemptions). - * No longer used as of `Rari-Capital/compound-protocol` version `fuse-v1.1.0`. - */ - uint256 public maxUtilizationRate; - /** * @dev Sets the proportion of Fuse pool interest taken as a protocol fee. * @param _minBorrowEth Minimum borrow balance (in ETH) per user per Fuse pool asset (only checked on new borrows, not redemptions). @@ -159,11 +235,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { return proxy; } - /** - * @dev Whitelisted Comptroller implementation contract addresses for each existing implementation. - */ - mapping(address => mapping(address => bool)) public comptrollerImplementationWhitelist; - /** * @dev Adds/removes Comptroller implementations to the whitelist. * @param oldImplementations The old `Comptroller` implementation addresses to upgrade from for each `newImplementations` to upgrade to. @@ -185,11 +256,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { comptrollerImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; } - /** - * @dev Whitelisted CErc20Delegate implementation contract addresses and `allowResign` values for each existing implementation. - */ - mapping(address => mapping(address => mapping(bool => bool))) public cErc20DelegateWhitelist; - /** * @dev Adds/removes CErc20Delegate implementations to the whitelist. * @param oldImplementations The old `CErc20Delegate` implementation addresses to upgrade from for each `newImplementations` to upgrade to. @@ -214,17 +280,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { cErc20DelegateWhitelist[oldImplementations[i]][newImplementations[i]][allowResign[i]] = statuses[i]; } - /** - * @dev Whitelisted CEtherDelegate implementation contract addresses and `allowResign` values for each existing implementation. - */ - /// keep this in the storage to not break the layout - mapping(address => mapping(address => mapping(bool => bool))) public cEtherDelegateWhitelist; - - /** - * @dev Latest Comptroller implementation for each existing implementation. - */ - mapping(address => address) internal _latestComptrollerImplementation; - /** * @dev Latest Comptroller implementation for each existing implementation. */ @@ -247,23 +302,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { _latestComptrollerImplementation[oldImplementation] = newImplementation; } - struct CDelegateUpgradeData { - address implementation; - bool allowResign; - bytes becomeImplementationData; - } - - /** - * @dev Latest CErc20Delegate implementation for each existing implementation. - */ - mapping(address => CDelegateUpgradeData) public _latestCErc20Delegate; - - /** - * @dev Latest CEtherDelegate implementation for each existing implementation. - */ - /// keep this in the storage to not break the layout - mapping(address => CDelegateUpgradeData) public _latestCEtherDelegate; - /** * @dev Latest CErc20Delegate implementation for each existing implementation. */ @@ -304,27 +342,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { ); } - /** - * @notice Maps Unitroller (Comptroller proxy) addresses to the proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). - * @dev A value of 0 means unset whereas a negative value means 0. - */ - mapping(address => int256) public customInterestFeeRates; - - /** - * @dev used as salt for the creation of new markets - */ - uint256 public marketsCounter; - - /** - * @dev Latest Plugin implementation for each existing implementation. - */ - mapping(address => address) public _latestPluginImplementation; - - /** - * @dev Whitelisted Plugin implementation contract addresses for each existing implementation. - */ - mapping(address => mapping(address => bool)) public pluginImplementationWhitelist; - /** * @dev Adds/removes plugin implementations to the whitelist. * @param oldImplementations The old plugin implementation addresses to upgrade from for each `newImplementations` to upgrade to. @@ -347,18 +364,18 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { } function _editERC20WrapperUpgradeWhitelist( - address[] calldata oldImplementations, - address[] calldata newImplementations, + address[] calldata oldWrappers, + address[] calldata newWrappers, bool[] calldata statuses ) external onlyOwner { require( - newImplementations.length > 0 && - newImplementations.length == oldImplementations.length && - newImplementations.length == statuses.length, - "No erc20Wrapping implementations supplied or array lengths not equal." + newWrappers.length > 0 && + newWrappers.length == oldWrappers.length && + newWrappers.length == statuses.length, + "empty array or lengths not equal" ); - for (uint256 i = 0; i < newImplementations.length; i++) - erc20WrapperUpgradeWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i]; + for (uint256 i = 0; i < newWrappers.length; i++) + erc20WrapperUpgradeWhitelist[oldWrappers[i]][newWrappers[i]] = statuses[i]; } /** @@ -380,18 +397,18 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { _latestPluginImplementation[oldImplementation] = newImplementation; } - function latestERC20WrapperForUnderlying(address oldImplementation) external view returns (address) { + function latestERC20WrapperForUnderlying(address oldWrapper) external view returns (address) { return - _latestERC20WrapperForUnderlying[oldImplementation] != address(0) - ? _latestERC20WrapperForUnderlying[oldImplementation] - : oldImplementation; + _latestERC20WrapperForUnderlying[oldWrapper] != address(0) + ? _latestERC20WrapperForUnderlying[oldWrapper] + : oldWrapper; } - function _setLatestERC20WrapperForUnderlying(address oldImplementation, address newImplementation) + function _setLatestERC20WrapperForUnderlying(address oldWrapper, address newWrapper) external onlyOwner { - _latestERC20WrapperForUnderlying[oldImplementation] = newImplementation; + _latestERC20WrapperForUnderlying[oldWrapper] = newWrapper; } /** @@ -409,16 +426,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { return newPluginAddress != oldPluginAddress; } - function _upgradeERC20WrappingToLatestImplementation(address cDelegator) external onlyOwner returns (bool) { - CErc20WrappingDelegate market = CErc20WrappingDelegate(cDelegator); - - address oldWrapper = address(market.underlyingWrapper()); - market._updateWrapper(_latestERC20WrapperForUnderlying[oldWrapper]); - address newWrapper = address(market.underlyingWrapper()); - - return newWrapper != oldWrapper; - } - /** * @notice Returns the proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). */ @@ -445,8 +452,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { customInterestFeeRates[comptroller] = rate; } - mapping(address => DiamondExtension[]) public comptrollerExtensions; - function getComptrollerExtensions(address comptroller) external view returns (DiamondExtension[] memory) { return comptrollerExtensions[comptroller]; } @@ -463,18 +468,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage { DiamondBase(pool)._registerExtension(extensionToAdd, extensionToReplace); } - mapping(address => DiamondExtension[]) public cErc20DelegateExtensions; - - /** - * @dev Whitelisted erc20Wrapping implementation contract addresses for each existing implementation. - */ - mapping(address => mapping(address => bool)) public erc20WrapperUpgradeWhitelist; - - /** - * @dev Latest erc20Wrapping implementation for each existing implementation. - */ - mapping(address => address) public _latestERC20WrapperForUnderlying; - function getCErc20DelegateExtensions(address cErc20Delegate) external view returns (DiamondExtension[] memory) { return cErc20DelegateExtensions[cErc20Delegate]; } diff --git a/contracts/compound/CErc20PluginDelegate.sol b/contracts/compound/CErc20PluginDelegate.sol index 659cf4415..8d1d05097 100644 --- a/contracts/compound/CErc20PluginDelegate.sol +++ b/contracts/compound/CErc20PluginDelegate.sol @@ -59,6 +59,8 @@ contract CErc20PluginDelegate is CErc20Delegate { plugin.redeem(plugin.balanceOf(address(this)), address(this), address(this)); } + emit NewPluginImplementation(address(plugin), _plugin); + plugin = IERC4626(_plugin); EIP20Interface(underlying).approve(_plugin, type(uint256).max); @@ -67,8 +69,6 @@ contract CErc20PluginDelegate is CErc20Delegate { if (amount != 0) { deposit(amount); } - - emit NewPluginImplementation(address(plugin), _plugin); } /*** CToken Overrides ***/ diff --git a/contracts/midas/CErc20WrappingDelegate.sol b/contracts/compound/CErc20WrappingDelegate.sol similarity index 86% rename from contracts/midas/CErc20WrappingDelegate.sol rename to contracts/compound/CErc20WrappingDelegate.sol index 1f0e53ca3..fa2b9e280 100644 --- a/contracts/midas/CErc20WrappingDelegate.sol +++ b/contracts/compound/CErc20WrappingDelegate.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.0; -import { CErc20Delegate } from "../compound/CErc20Delegate.sol"; -import { EIP20Interface } from "../compound/EIP20Interface.sol"; +import { CErc20Delegate } from "./CErc20Delegate.sol"; +import { EIP20Interface } from "./EIP20Interface.sol"; import { IFuseFeeDistributor } from "../compound/IFuseFeeDistributor.sol"; -import { MidasERC20Wrapper, IERC20 } from "./MidasERC20Wrapper.sol"; +import { MidasERC20Wrapper } from "../midas/MidasERC20Wrapper.sol"; contract CErc20WrappingDelegate is CErc20Delegate { event NewErc20WrappingImplementation(address oldImpl, address newImpl); @@ -17,7 +17,13 @@ contract CErc20WrappingDelegate is CErc20Delegate { address _newWrapper = abi.decode(data, (address)); if (address(underlyingWrapper) == address(0)) { - underlyingWrapper = new MidasERC20Wrapper(IERC20(underlying)); + EIP20Interface asErc20 = EIP20Interface(underlying); + underlyingWrapper = new MidasERC20Wrapper( + underlying, + asErc20.name(), + asErc20.symbol(), + asErc20.decimals() + ); } else { if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); @@ -43,6 +49,8 @@ contract CErc20WrappingDelegate is CErc20Delegate { doTransferOut(address(this), underlyingWrapper.balanceOf(address(this))); } + emit NewErc20WrappingImplementation(address(underlyingWrapper), _newWrapper); + underlyingWrapper = MidasERC20Wrapper(_newWrapper); EIP20Interface(underlying).approve(_newWrapper, type(uint256).max); @@ -52,8 +60,6 @@ contract CErc20WrappingDelegate is CErc20Delegate { if (amount != 0) { doTransferIn(address(this), amount); } - - emit NewErc20WrappingImplementation(address(underlyingWrapper), _newWrapper); } function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index 9c3ebedff..ac07d6693 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -4,23 +4,23 @@ pragma solidity >=0.8.0; import { ERC20Wrapper, ERC20 } from "openzeppelin-contracts/token/ERC20/extensions/ERC20Wrapper.sol"; import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; -interface IERC20Wrapper is IERC20 { - function name() external view returns (string memory); - - function symbol() external view returns (string memory); -} - contract MidasERC20Wrapper is ERC20Wrapper { - address private owner; + address private _owner; + uint8 private _decimals; - constructor(IERC20 underlyingToken) - ERC20(IERC20Wrapper(address(underlyingToken)).name(), IERC20Wrapper(address(underlyingToken)).symbol()) - ERC20Wrapper(underlyingToken) + constructor(address underlyingToken_, string memory name_, string memory symbol_, uint8 decimals_) + ERC20(name_, symbol_) + ERC20Wrapper(IERC20(underlyingToken_)) { - owner = msg.sender; + _owner = msg.sender; + _decimals = decimals_; + } + + function decimals() public view override returns (uint8) { + return _decimals; } function recover() public returns (uint256) { - return _recover(owner); + return _recover(_owner); } } diff --git a/contracts/test/ContractsUpgradesTest.t.sol b/contracts/test/ContractsUpgradesTest.t.sol index 455a655ef..084f67c38 100644 --- a/contracts/test/ContractsUpgradesTest.t.sol +++ b/contracts/test/ContractsUpgradesTest.t.sol @@ -43,8 +43,8 @@ contract ContractsUpgradesTest is BaseTest { } // after upgrade - FusePoolDirectory newImpl = FusePoolDirectory(contractToTest); - address ownerAfter = newImpl.owner(); + FusePoolDirectory fpd = FusePoolDirectory(contractToTest); + address ownerAfter = fpd.owner(); emit log_address(ownerAfter); (, FusePoolDirectory.FusePool[] memory poolsAfter) = oldImpl.getActivePools(); @@ -57,6 +57,7 @@ contract ContractsUpgradesTest is BaseTest { function testFuseFeeDistributorUpgrade() public fork(BSC_MAINNET) { address oldCercDelegate = 0x94C50805bC16737ead84e25Cd5Aa956bCE04BBDF; + address oldComptrollerImpl = 0xC634898F59391bfd66eDDC9eB4298A8E9643596c; // before upgrade FuseFeeDistributor ffdProxy = FuseFeeDistributor(payable(ap.getAddress("FuseFeeDistributor"))); @@ -71,6 +72,9 @@ contract ContractsUpgradesTest is BaseTest { // if (whitelistedBefore) emit log("whitelisted before"); // else emit log("should be whitelisted"); + address comptrollerExtensionBefore = address(ffdProxy.comptrollerExtensions(oldComptrollerImpl, 0)); + emit log_named_address("comp ext before", comptrollerExtensionBefore); + // upgrade { FuseFeeDistributor newImpl = new FuseFeeDistributor(); @@ -94,12 +98,15 @@ contract ContractsUpgradesTest is BaseTest { emit log_address(ownerAfter); // if (whitelistedAfter) emit log("whitelisted After"); // else emit log("should be whitelisted"); + address comptrollerExtensionAfter = address(ffdProxy.comptrollerExtensions(oldComptrollerImpl, 0)); + emit log_named_address("comp ext after", comptrollerExtensionAfter); assertEq(latestCErc20DelegateBefore, latestCErc20DelegateAfter, "latest delegates do not match"); assertEq(marketsCounterBefore, marketsCounterAfter, "markets counter does not match"); // assertEq(whitelistedBefore, whitelistedAfter, "whitelisted status does not match"); assertEq(ownerBefore, ownerAfter, "owner mismatch"); + assertEq(comptrollerExtensionBefore, comptrollerExtensionAfter, "comp ext mismatch"); } function testMarketsLatestImplementationsBsc() public fork(BSC_MAINNET) { From fd909fe8330a2df0bde9127a6fb942d83798a0d6 Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Wed, 4 Jan 2023 13:41:43 +0200 Subject: [PATCH 18/22] prettier --- contracts/FuseFeeDistributor.sol | 13 +++---------- contracts/compound/CErc20WrappingDelegate.sol | 7 +------ contracts/midas/MidasERC20Wrapper.sol | 10 ++++++---- 3 files changed, 10 insertions(+), 20 deletions(-) diff --git a/contracts/FuseFeeDistributor.sol b/contracts/FuseFeeDistributor.sol index 9ac1e9be5..668aec22b 100644 --- a/contracts/FuseFeeDistributor.sol +++ b/contracts/FuseFeeDistributor.sol @@ -23,7 +23,6 @@ struct CDelegateUpgradeData { } abstract contract FeeDistributorStorage { - /** * @notice The proportion of Fuse pool interest taken as a protocol fee (scaled by 1e18). */ @@ -67,7 +66,7 @@ abstract contract FeeDistributorStorage { */ mapping(address => address) internal _latestComptrollerImplementation; /** - * @dev Latest CErc20Delegate implementation for each existing implementation. + * @dev Latest CErc20Delegate implementation for each existing implementation. */ mapping(address => CDelegateUpgradeData) public _latestCErc20Delegate; @@ -111,7 +110,6 @@ abstract contract FeeDistributorStorage { * @dev Latest erc20Wrapping implementation for each existing implementation. */ mapping(address => address) public _latestERC20WrapperForUnderlying; - } /** @@ -369,9 +367,7 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage, FeeDistri bool[] calldata statuses ) external onlyOwner { require( - newWrappers.length > 0 && - newWrappers.length == oldWrappers.length && - newWrappers.length == statuses.length, + newWrappers.length > 0 && newWrappers.length == oldWrappers.length && newWrappers.length == statuses.length, "empty array or lengths not equal" ); for (uint256 i = 0; i < newWrappers.length; i++) @@ -404,10 +400,7 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage, FeeDistri : oldWrapper; } - function _setLatestERC20WrapperForUnderlying(address oldWrapper, address newWrapper) - external - onlyOwner - { + function _setLatestERC20WrapperForUnderlying(address oldWrapper, address newWrapper) external onlyOwner { _latestERC20WrapperForUnderlying[oldWrapper] = newWrapper; } diff --git a/contracts/compound/CErc20WrappingDelegate.sol b/contracts/compound/CErc20WrappingDelegate.sol index fa2b9e280..3f6788940 100644 --- a/contracts/compound/CErc20WrappingDelegate.sol +++ b/contracts/compound/CErc20WrappingDelegate.sol @@ -18,12 +18,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { if (address(underlyingWrapper) == address(0)) { EIP20Interface asErc20 = EIP20Interface(underlying); - underlyingWrapper = new MidasERC20Wrapper( - underlying, - asErc20.name(), - asErc20.symbol(), - asErc20.decimals() - ); + underlyingWrapper = new MidasERC20Wrapper(underlying, asErc20.name(), asErc20.symbol(), asErc20.decimals()); } else { if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index ac07d6693..e9b7edf82 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -8,10 +8,12 @@ contract MidasERC20Wrapper is ERC20Wrapper { address private _owner; uint8 private _decimals; - constructor(address underlyingToken_, string memory name_, string memory symbol_, uint8 decimals_) - ERC20(name_, symbol_) - ERC20Wrapper(IERC20(underlyingToken_)) - { + constructor( + address underlyingToken_, + string memory name_, + string memory symbol_, + uint8 decimals_ + ) ERC20(name_, symbol_) ERC20Wrapper(IERC20(underlyingToken_)) { _owner = msg.sender; _decimals = decimals_; } From f7047fe2c3c20dc13978e81c28d77a37e0a7708a Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Tue, 24 Jan 2023 14:44:28 +0200 Subject: [PATCH 19/22] recover accidentally sent tokens --- contracts/compound/CErc20WrappingDelegate.sol | 5 ++--- contracts/midas/MidasERC20Wrapper.sol | 9 +++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/contracts/compound/CErc20WrappingDelegate.sol b/contracts/compound/CErc20WrappingDelegate.sol index 3f6788940..2c6179589 100644 --- a/contracts/compound/CErc20WrappingDelegate.sol +++ b/contracts/compound/CErc20WrappingDelegate.sol @@ -14,13 +14,12 @@ contract CErc20WrappingDelegate is CErc20Delegate { function _becomeImplementation(bytes memory data) public virtual override { require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation"); - address _newWrapper = abi.decode(data, (address)); - if (address(underlyingWrapper) == address(0)) { EIP20Interface asErc20 = EIP20Interface(underlying); underlyingWrapper = new MidasERC20Wrapper(underlying, asErc20.name(), asErc20.symbol(), asErc20.decimals()); } else { - if (_newWrapper == address(0) && address(underlyingWrapper) != address(0)) { + address _newWrapper = abi.decode(data, (address)); + if (_newWrapper == address(0)) { _newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper)); } diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index e9b7edf82..a97afc1ae 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -22,7 +22,12 @@ contract MidasERC20Wrapper is ERC20Wrapper { return _decimals; } - function recover() public returns (uint256) { - return _recover(_owner); + function recover(address token) public returns (uint256) { + if (token == address(this)) { + return _recover(_owner); + } else { + uint256 balance = IERC20(token).balanceOf(address(this)); + return IERC20(token).transfer(_owner, balance) ? balance : 0; + } } } From cc36dec08020e70d84d332943f3ab6e939f1b1de Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:02:55 +0200 Subject: [PATCH 20/22] end to end testing for the wrapping delegate --- contracts/compound/CErc20WrappingDelegate.sol | 2 +- contracts/midas/MidasERC20Wrapper.sol | 24 +++++++++ contracts/test/DeployMarkets.t.sol | 52 +++++++++++++++++-- 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/contracts/compound/CErc20WrappingDelegate.sol b/contracts/compound/CErc20WrappingDelegate.sol index 2c6179589..8734f88c2 100644 --- a/contracts/compound/CErc20WrappingDelegate.sol +++ b/contracts/compound/CErc20WrappingDelegate.sol @@ -58,7 +58,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { function doTransferIn(address from, uint256 amount) internal virtual override returns (uint256) { require(EIP20Interface(underlying).transferFrom(from, address(this), amount), "send"); - + require(EIP20Interface(underlying).approve(address(underlyingWrapper), amount), "approve wrapper"); underlyingWrapper.depositFor(address(this), amount); return amount; } diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index a97afc1ae..930a20c27 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -7,6 +7,8 @@ import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; contract MidasERC20Wrapper is ERC20Wrapper { address private _owner; uint8 private _decimals; + // string private _nameOverride; + // string private _symbolOverride; constructor( address underlyingToken_, @@ -30,4 +32,26 @@ contract MidasERC20Wrapper is ERC20Wrapper { return IERC20(token).transfer(_owner, balance) ? balance : 0; } } + + // function name() public view virtual override returns (string memory) { + // if (bytes(_nameOverride).length == 0) { + // return super.name(); + // } else { + // return _nameOverride; + // } + // } + // + // function symbol() public view virtual override returns (string memory) { + // if (bytes(_symbolOverride).length == 0) { + // return super.symbol(); + // } else { + // return _symbolOverride; + // } + // } + // + // function _overrideNameAndSymbol(string memory name_, string memory symbol_) external { + // require(msg.sender == _owner, "!owner"); + // _name = name_; + // _symbol = symbol_; + // } } diff --git a/contracts/test/DeployMarkets.t.sol b/contracts/test/DeployMarkets.t.sol index c4b0b7aad..652c6b125 100644 --- a/contracts/test/DeployMarkets.t.sol +++ b/contracts/test/DeployMarkets.t.sol @@ -21,6 +21,7 @@ import { ComptrollerFirstExtension } from "../compound/ComptrollerFirstExtension import { CErc20Delegate } from "../compound/CErc20Delegate.sol"; import { CErc20PluginDelegate } from "../compound/CErc20PluginDelegate.sol"; import { CErc20PluginRewardsDelegate } from "../compound/CErc20PluginRewardsDelegate.sol"; +import { CErc20WrappingDelegate } from "../compound/CErc20WrappingDelegate.sol"; import { CErc20Delegator } from "../compound/CErc20Delegator.sol"; import { ComptrollerInterface } from "../compound/ComptrollerInterface.sol"; import { InterestRateModel } from "../compound/InterestRateModel.sol"; @@ -31,6 +32,7 @@ import { MockERC4626 } from "../midas/strategies/MockERC4626.sol"; import { MockERC4626Dynamic } from "../midas/strategies/MockERC4626Dynamic.sol"; import { CTokenFirstExtension, DiamondExtension } from "../compound/CTokenFirstExtension.sol"; import { MidasFlywheelCore } from "../midas/strategies/flywheel/MidasFlywheelCore.sol"; +import { MidasERC20Wrapper } from "../midas/MidasERC20Wrapper.sol"; contract DeployMarketsTest is Test { MockERC20 underlyingToken; @@ -42,6 +44,7 @@ contract DeployMarketsTest is Test { CErc20Delegate cErc20Delegate; CErc20PluginDelegate cErc20PluginDelegate; CErc20PluginRewardsDelegate cErc20PluginRewardsDelegate; + CErc20WrappingDelegate cErc20WrappingDelegate; MockERC4626 mockERC4626; MockERC4626Dynamic mockERC4626Dynamic; @@ -77,18 +80,16 @@ contract DeployMarketsTest is Test { cErc20Delegate = new CErc20Delegate(); cErc20PluginDelegate = new CErc20PluginDelegate(); cErc20PluginRewardsDelegate = new CErc20PluginRewardsDelegate(); + cErc20WrappingDelegate = new CErc20WrappingDelegate(); DiamondExtension[] memory cErc20DelegateExtensions = new DiamondExtension[](1); cErc20DelegateExtensions[0] = new CTokenFirstExtension(); fuseAdmin._setCErc20DelegateExtensions(address(cErc20Delegate), cErc20DelegateExtensions); fuseAdmin._setCErc20DelegateExtensions(address(cErc20PluginDelegate), cErc20DelegateExtensions); fuseAdmin._setCErc20DelegateExtensions(address(cErc20PluginRewardsDelegate), cErc20DelegateExtensions); + fuseAdmin._setCErc20DelegateExtensions(address(cErc20WrappingDelegate), cErc20DelegateExtensions); - for (uint256 i = 0; i < 7; i++) { - t.push(true); - f.push(false); - } - + oldCErC20Implementations.push(address(0)); oldCErC20Implementations.push(address(0)); oldCErC20Implementations.push(address(0)); oldCErC20Implementations.push(address(0)); @@ -100,11 +101,17 @@ contract DeployMarketsTest is Test { newCErc20Implementations.push(address(cErc20Delegate)); newCErc20Implementations.push(address(cErc20PluginDelegate)); newCErc20Implementations.push(address(cErc20PluginRewardsDelegate)); + newCErc20Implementations.push(address(cErc20WrappingDelegate)); newCErc20Implementations.push(address(cErc20PluginDelegate)); newCErc20Implementations.push(address(cErc20PluginRewardsDelegate)); newCErc20Implementations.push(address(cErc20PluginDelegate)); newCErc20Implementations.push(address(cErc20PluginRewardsDelegate)); + for (uint256 i = 0; i < newCErc20Implementations.length; i++) { + t.push(true); + f.push(false); + } + fuseAdmin._editCErc20DelegateWhitelist(oldCErC20Implementations, newCErc20Implementations, f, t); } @@ -269,6 +276,41 @@ contract DeployMarketsTest is Test { assertEq(underlyingToken.balanceOf(address(mockERC4626Dynamic)), 10000000); } + function testDeployCErc20WrappingDelegate() public { + vm.roll(1); + comptroller._deployMarket( + false, + abi.encode( + address(underlyingToken), + comptroller, + payable(address(fuseAdmin)), + InterestRateModel(address(interestModel)), + "cUnderlyingToken", + "CUT", + address(cErc20WrappingDelegate), + abi.encode(address(0)), + uint256(1), + uint256(0) + ), + 0.9e18 + ); + + CTokenInterface[] memory allMarkets = comptroller.asComptrollerFirstExtension().getAllMarkets(); + CErc20WrappingDelegate cToken = CErc20WrappingDelegate(address(allMarkets[allMarkets.length - 1])); + + underlyingToken.approve(address(cToken), 1e36); + address[] memory cTokens = new address[](1); + cTokens[0] = address(cToken); + comptroller.enterMarkets(cTokens); + vm.roll(1); + + cToken.mint(10000000); + assertEq(cToken.totalSupply(), 10000000 * 5); + MidasERC20Wrapper wrapper = cToken.underlyingWrapper(); + assertEq(wrapper.balanceOf(address(cToken)), 10000000); + assertEq(underlyingToken.balanceOf(address(wrapper)), 10000000); + } + function testAutImplementationCErc20Delegate() public { mockERC4626 = new MockERC4626(ERC20(address(underlyingToken))); From 3a1394e82a19f396e29e301d96d4c90c01509591 Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Tue, 24 Jan 2023 19:03:57 +0200 Subject: [PATCH 21/22] prettier --- contracts/midas/MidasERC20Wrapper.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/contracts/midas/MidasERC20Wrapper.sol b/contracts/midas/MidasERC20Wrapper.sol index 930a20c27..4c2ac8240 100644 --- a/contracts/midas/MidasERC20Wrapper.sol +++ b/contracts/midas/MidasERC20Wrapper.sol @@ -7,6 +7,7 @@ import { IERC20 } from "openzeppelin-contracts/token/ERC20/IERC20.sol"; contract MidasERC20Wrapper is ERC20Wrapper { address private _owner; uint8 private _decimals; + // string private _nameOverride; // string private _symbolOverride; From 7381113ef0e835847424bbef5fd5b508616596ad Mon Sep 17 00:00:00 2001 From: Veliko Minkov <2662912+vminkov@users.noreply.github.com> Date: Wed, 25 Jan 2023 11:35:59 +0200 Subject: [PATCH 22/22] wrapper underlying - market not listed when deployed --- contracts/compound/CErc20WrappingDelegate.sol | 1 + contracts/compound/Comptroller.sol | 20 ++++++++++++----- contracts/test/DeployMarkets.t.sol | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/contracts/compound/CErc20WrappingDelegate.sol b/contracts/compound/CErc20WrappingDelegate.sol index 8734f88c2..7001faab5 100644 --- a/contracts/compound/CErc20WrappingDelegate.sol +++ b/contracts/compound/CErc20WrappingDelegate.sol @@ -17,6 +17,7 @@ contract CErc20WrappingDelegate is CErc20Delegate { if (address(underlyingWrapper) == address(0)) { EIP20Interface asErc20 = EIP20Interface(underlying); underlyingWrapper = new MidasERC20Wrapper(underlying, asErc20.name(), asErc20.symbol(), asErc20.decimals()); + underlying = address(underlyingWrapper); } else { address _newWrapper = abi.decode(data, (address)); if (_newWrapper == address(0)) { diff --git a/contracts/compound/Comptroller.sol b/contracts/compound/Comptroller.sol index 20e10834d..7f0e530c7 100644 --- a/contracts/compound/Comptroller.sol +++ b/contracts/compound/Comptroller.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.0; import { CTokenInterface, CErc20Interface } from "./CTokenInterfaces.sol"; +import { CDelegateInterface } from "./CDelegateInterface.sol"; import { ComptrollerErrorReporter } from "./ErrorReporter.sol"; import { Exponential } from "./Exponential.sol"; import { PriceOracle } from "./PriceOracle.sol"; @@ -1272,14 +1273,23 @@ contract Comptroller is ComptrollerV3Storage, ComptrollerInterface, ComptrollerE fuseAdminHasRights = true; // Deploy via Fuse admin - CTokenInterface cToken = CTokenInterface(IFuseFeeDistributor(fuseAdmin).deployCErc20(constructorData)); + address marketAddress = IFuseFeeDistributor(fuseAdmin).deployCErc20(constructorData); // Reset Fuse admin rights to the original value fuseAdminHasRights = oldFuseAdminHasRights; - // Support market here in the Comptroller - uint256 err = _supportMarket(cToken); - // Set collateral factor - return err == uint256(Error.NO_ERROR) ? _setCollateralFactor(cToken, collateralFactorMantissa) : err; + CTokenInterface cToken = CTokenInterface(marketAddress); + if (!compareStrings(CDelegateInterface(marketAddress).contractType(), "CErc20WrappingDelegate")) { + // Support market here in the Comptroller + uint256 err = _supportMarket(cToken); + // Set collateral factor + return err == uint256(Error.NO_ERROR) ? _setCollateralFactor(cToken, collateralFactorMantissa) : err; + } + + return uint256(Error.NO_ERROR); + } + + function compareStrings(string memory a, string memory b) public pure returns (bool) { + return (keccak256(abi.encodePacked((a))) == keccak256(abi.encodePacked((b)))); } /** diff --git a/contracts/test/DeployMarkets.t.sol b/contracts/test/DeployMarkets.t.sol index 652c6b125..63f090e00 100644 --- a/contracts/test/DeployMarkets.t.sol +++ b/contracts/test/DeployMarkets.t.sol @@ -295,6 +295,9 @@ contract DeployMarketsTest is Test { 0.9e18 ); + // TODO configure the oracle + // then call support market and set collateral factor + CTokenInterface[] memory allMarkets = comptroller.asComptrollerFirstExtension().getAllMarkets(); CErc20WrappingDelegate cToken = CErc20WrappingDelegate(address(allMarkets[allMarkets.length - 1])); @@ -309,6 +312,25 @@ contract DeployMarketsTest is Test { MidasERC20Wrapper wrapper = cToken.underlyingWrapper(); assertEq(wrapper.balanceOf(address(cToken)), 10000000); assertEq(underlyingToken.balanceOf(address(wrapper)), 10000000); + + // deploy a second of the same underlying + vm.roll(1); + comptroller._deployMarket( + false, + abi.encode( + address(underlyingToken), + comptroller, + payable(address(fuseAdmin)), + InterestRateModel(address(interestModel)), + "cUnderlyingToken", + "CUT", + address(cErc20WrappingDelegate), + abi.encode(address(0)), + uint256(1), + uint256(0) + ), + 0.9e18 + ); } function testAutImplementationCErc20Delegate() public {