Skip to content

Feat add erc20 wrapping delegate #509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 28 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
53f90b4
Feat add erc20 wrapping delegate
coolnft Dec 13, 2022
ed65271
add infinite approval
coolnft Dec 13, 2022
53969e5
prettier issue fixed.
coolnft Dec 14, 2022
1cbab65
update CErc20Wrapping implementation
coolnft Dec 15, 2022
8904e21
prettier issue fixed.
coolnft Dec 15, 2022
5d4a332
variable order updated.
coolnft Dec 15, 2022
50e4bd6
prettier issue fixed.
coolnft Dec 15, 2022
7fac66b
fuse fee distributor variable naming changes
coolnft Dec 16, 2022
961fdb6
prettier issues fixed.
coolnft Dec 16, 2022
e41001a
naming changes
coolnft Dec 16, 2022
4a43e3b
naming changes
coolnft Dec 16, 2022
7e301e8
prettier issue fixed.
coolnft Dec 16, 2022
603f3e4
Merge branch 'development' of github.com:Midas-Protocol/contracts int…
coolnft Dec 19, 2022
d5d054c
MidasERC20wrapper added.
coolnft Dec 19, 2022
37023e9
update minor issue
coolnft Dec 20, 2022
a6c70b1
prettier issue fixed.
coolnft Dec 20, 2022
b8b2beb
erc20 wrapping delegate updated.
coolnft Dec 20, 2022
0f2ad93
Merge branch 'development' into feat/add-erc20wrappingdelegator
vminkov Jan 3, 2023
86ae4ac
refactoring the wrapping delegate
vminkov Jan 4, 2023
fd909fe
prettier
vminkov Jan 4, 2023
e944509
Merge branch 'development' into feat/add-erc20wrappingdelegator
vminkov Jan 12, 2023
00be68a
Merge branch 'development' into feat/add-erc20wrappingdelegator
vminkov Jan 18, 2023
aef7fec
Merge branch 'development' into feat/add-erc20wrappingdelegator
vminkov Jan 24, 2023
f7047fe
recover accidentally sent tokens
vminkov Jan 24, 2023
cc36dec
end to end testing for the wrapping delegate
vminkov Jan 24, 2023
3a1394e
prettier
vminkov Jan 24, 2023
7381113
wrapper underlying - market not listed when deployed
vminkov Jan 25, 2023
f9c7c71
Merge branch 'development' into feat/add-erc20wrappingdelegator
vminkov Jan 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
208 changes: 122 additions & 86 deletions contracts/FuseFeeDistributor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,115 @@ import "./compound/ErrorReporter.sol";
import "./external/compound/IComptroller.sol";
import "./compound/CErc20Delegator.sol";
import "./compound/CErc20PluginDelegate.sol";
import "./compound/CErc20WrappingDelegate.sol";
import "./midas/SafeOwnableUpgradeable.sol";
import "./utils/PatchedStorage.sol";
import "./oracles/BasePriceOracle.sol";
import { CTokenExtensionInterface } from "./compound/CTokenInterfaces.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 <[email protected]> (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;

Expand All @@ -37,11 +134,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).
Expand Down Expand Up @@ -69,23 +161,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).
Expand Down Expand Up @@ -159,11 +234,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.
Expand All @@ -185,11 +255,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.
Expand All @@ -214,17 +279,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.
*/
Expand All @@ -247,23 +301,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.
*/
Expand Down Expand Up @@ -304,27 +341,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.
Expand All @@ -346,6 +362,19 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage {
pluginImplementationWhitelist[oldImplementations[i]][newImplementations[i]] = statuses[i];
}

function _editERC20WrapperUpgradeWhitelist(
address[] calldata oldWrappers,
address[] calldata newWrappers,
bool[] calldata statuses
) external onlyOwner {
require(
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++)
erc20WrapperUpgradeWhitelist[oldWrappers[i]][newWrappers[i]] = statuses[i];
}

/**
* @dev Latest Plugin implementation for each existing implementation.
*/
Expand All @@ -365,6 +394,17 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage {
_latestPluginImplementation[oldImplementation] = newImplementation;
}

function latestERC20WrapperForUnderlying(address oldWrapper) external view returns (address) {
return
_latestERC20WrapperForUnderlying[oldWrapper] != address(0)
? _latestERC20WrapperForUnderlying[oldWrapper]
: oldWrapper;
}

function _setLatestERC20WrapperForUnderlying(address oldWrapper, address newWrapper) external onlyOwner {
_latestERC20WrapperForUnderlying[oldWrapper] = newWrapper;
}

/**
* @dev Upgrades a plugin of a CErc20PluginDelegate market to the latest implementation
* @param cDelegator the proxy address
Expand Down Expand Up @@ -406,8 +446,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];
}
Expand All @@ -424,8 +462,6 @@ contract FuseFeeDistributor is SafeOwnableUpgradeable, PatchedStorage {
DiamondBase(pool)._registerExtension(extensionToAdd, extensionToReplace);
}

mapping(address => DiamondExtension[]) public cErc20DelegateExtensions;

function getCErc20DelegateExtensions(address cErc20Delegate) external view returns (DiamondExtension[] memory) {
return cErc20DelegateExtensions[cErc20Delegate];
}
Expand Down
4 changes: 2 additions & 2 deletions contracts/compound/CErc20PluginDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -67,8 +69,6 @@ contract CErc20PluginDelegate is CErc20Delegate {
if (amount != 0) {
deposit(amount);
}

emit NewPluginImplementation(address(plugin), _plugin);
}

/*** CToken Overrides ***/
Expand Down
74 changes: 74 additions & 0 deletions contracts/compound/CErc20WrappingDelegate.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity >=0.8.0;

import { CErc20Delegate } from "./CErc20Delegate.sol";
import { EIP20Interface } from "./EIP20Interface.sol";
import { IFuseFeeDistributor } from "../compound/IFuseFeeDistributor.sol";
import { MidasERC20Wrapper } from "../midas/MidasERC20Wrapper.sol";

contract CErc20WrappingDelegate is CErc20Delegate {
event NewErc20WrappingImplementation(address oldImpl, address newImpl);

MidasERC20Wrapper public underlyingWrapper;

function _becomeImplementation(bytes memory data) public virtual override {
require(msg.sender == address(this) || hasAdminRights(), "only self and admins can call _becomeImplementation");

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)) {
_newWrapper = IFuseFeeDistributor(fuseAdmin).latestERC20WrapperForUnderlying(address(underlyingWrapper));
}

if (_newWrapper != address(0) && _newWrapper != address(underlyingWrapper)) {
_updateWrapper(_newWrapper);
}
}
}

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;

require(
IFuseFeeDistributor(fuseAdmin).erc20WrapperUpgradeWhitelist(oldImplementation, _newWrapper),
"erc20Wrapping implementation not whitelisted"
);

if (address(underlyingWrapper) != address(0) && underlyingWrapper.balanceOf(address(this)) != 0) {
doTransferOut(address(this), underlyingWrapper.balanceOf(address(this)));
}

emit NewErc20WrappingImplementation(address(underlyingWrapper), _newWrapper);

underlyingWrapper = MidasERC20Wrapper(_newWrapper);

EIP20Interface(underlying).approve(_newWrapper, type(uint256).max);

uint256 amount = EIP20Interface(underlying).balanceOf(address(this));

if (amount != 0) {
doTransferIn(address(this), amount);
}
}

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;
}

function doTransferOut(address to, uint256 amount) internal virtual override {
underlyingWrapper.withdrawTo(to, amount);
}

function contractType() external pure virtual override returns (string memory) {
return "CErc20WrappingDelegate";
}
}
Loading