From 71019c751b33d700ac79c9543631970560721c2d Mon Sep 17 00:00:00 2001 From: Doc Akan Date: Tue, 26 Dec 2023 17:18:45 +0000 Subject: [PATCH] feat: adding rari controller --- cspell.json | 4 + .../dollar/facets/DollarAmoMinterFacet.sol | 10 + .../dollar/interfaces/ICErc20Delegator.sol | 121 +++++++++++ .../dollar/interfaces/IDollarAmoMinter.sol | 6 + .../dollar/interfaces/IRariComptroller.sol | 195 ++++++++++++++++++ .../dollar/libraries/LibDollarAmoMinter.sol | 15 ++ .../libraries/LibFuseRariAmoStrategy.sol | 152 ++++++++++++++ .../diamond/facets/UbiquityPoolFacet.t.sol | 8 + 8 files changed, 511 insertions(+) create mode 100644 packages/contracts/src/dollar/interfaces/ICErc20Delegator.sol create mode 100644 packages/contracts/src/dollar/interfaces/IRariComptroller.sol create mode 100644 packages/contracts/src/dollar/libraries/LibFuseRariAmoStrategy.sol diff --git a/cspell.json b/cspell.json index 379dd503d..592352ccd 100644 --- a/cspell.json +++ b/cspell.json @@ -79,6 +79,8 @@ "extcodesize", "FRAX", "Funcs", + "fuserariamo", + "FUSERARIAMO", "Gelato", "habemus", "IAMO", @@ -114,6 +116,7 @@ "proto", "psender", "Qmanager", + "Rari", "Rarible", "Reentrancy", "retdata", @@ -148,6 +151,7 @@ "UbiquiStick", "Unassigns", "uncollateralized", + "Unitrollers", "usedapp", "Vitalik", "wagmi", diff --git a/packages/contracts/src/dollar/facets/DollarAmoMinterFacet.sol b/packages/contracts/src/dollar/facets/DollarAmoMinterFacet.sol index 13cf4c10b..6cbf99665 100644 --- a/packages/contracts/src/dollar/facets/DollarAmoMinterFacet.sol +++ b/packages/contracts/src/dollar/facets/DollarAmoMinterFacet.sol @@ -167,6 +167,11 @@ contract DollarAmoMinterFacet is Modifiers, IDollarAmoMinter { LibDollarAmoMinter.setTimelock(_newTimelock); } + /// @notice Returns the timelock address. + function timelockAddress() external view returns (address _timelockAddr) { + return LibDollarAmoMinter.timelockAddress(); + } + /// @notice Sets the custodian address. /// @param _custodianAddress The address of the custodian. function setCustodian( @@ -175,6 +180,11 @@ contract DollarAmoMinterFacet is Modifiers, IDollarAmoMinter { LibDollarAmoMinter.setCustodian(_custodianAddress); } + /// @notice Returns the custodian address. + function custodianAddress() external view returns (address _custodianAddr) { + return LibDollarAmoMinter.custodianAddress(); + } + /// @notice Sets the dollar minting cap. /// @param _dollarMintCap The new dollar minting cap. function setDollarMintCap( diff --git a/packages/contracts/src/dollar/interfaces/ICErc20Delegator.sol b/packages/contracts/src/dollar/interfaces/ICErc20Delegator.sol new file mode 100644 index 000000000..7c742bac6 --- /dev/null +++ b/packages/contracts/src/dollar/interfaces/ICErc20Delegator.sol @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface ICErc20Delegator { + function _acceptAdmin() external returns (uint256); + function _addReserves(uint256 addAmount) external returns (uint256); + function _reduceReserves(uint256 reduceAmount) external returns (uint256); + function _renounceAdminRights() external returns (uint256); + function _renounceFuseAdminRights() external returns (uint256); + function _resignImplementation() external; + function _setAdminFee( + uint256 newAdminFeeMantissa + ) external returns (uint256); + function _setComptroller(address newComptroller) external returns (uint256); + function _setFuseFee() external returns (uint256); + function _setInterestRateModel( + address newInterestRateModel + ) external returns (uint256); + function _setPendingAdmin( + address newPendingAdmin + ) external returns (uint256); + function _setReserveFactor( + uint256 newReserveFactorMantissa + ) external returns (uint256); + function _withdrawAdminFees( + uint256 withdrawAmount + ) external returns (uint256); + function _withdrawFuseFees( + uint256 withdrawAmount + ) external returns (uint256); + function accrualBlockNumber() external view returns (uint256); + function accrueInterest() external returns (uint256); + function admin() external view returns (address); + function adminFeeMantissa() external view returns (uint256); + function adminHasRights() external view returns (bool); + function allowance( + address owner, + address spender + ) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + function balanceOf(address owner) external view returns (uint256); + function balanceOfUnderlying(address owner) external returns (uint256); + function borrow(uint256 borrowAmount) external returns (uint256); + function borrowBalanceCurrent(address account) external returns (uint256); + function borrowBalanceStored( + address account + ) external view returns (uint256); + function borrowIndex() external view returns (uint256); + function borrowRatePerBlock() external view returns (uint256); + function comptroller() external view returns (address); + function decimals() external view returns (uint8); + function exchangeRateCurrent() external returns (uint256); + function exchangeRateStored() external view returns (uint256); + function fuseAdminHasRights() external view returns (bool); + function fuseFeeMantissa() external view returns (uint256); + function getAccountSnapshot( + address account + ) external view returns (uint256, uint256, uint256, uint256); + function getCash() external view returns (uint256); + function implementation() external view returns (address); + function initialize( + address comptroller_, + address interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + uint256 reserveFactorMantissa_, + uint256 adminFeeMantissa_ + ) external; + function initialize( + address underlying_, + address comptroller_, + address interestRateModel_, + uint256 initialExchangeRateMantissa_, + string memory name_, + string memory symbol_, + uint8 decimals_, + uint256 reserveFactorMantissa_, + uint256 adminFeeMantissa_ + ) external; + function interestRateModel() external view returns (address); + function isCEther() external view returns (bool); + function isCToken() external view returns (bool); + function liquidateBorrow( + address borrower, + uint256 repayAmount, + address cTokenCollateral + ) external returns (uint256); + function mint(uint256 mintAmount) external returns (uint256); + function name() external view returns (string memory); + function pendingAdmin() external view returns (address); + function redeem(uint256 redeemTokens) external returns (uint256); + function redeemUnderlying(uint256 redeemAmount) external returns (uint256); + function repayBorrow(uint256 repayAmount) external returns (uint256); + function repayBorrowBehalf( + address borrower, + uint256 repayAmount + ) external returns (uint256); + function reserveFactorMantissa() external view returns (uint256); + function seize( + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256); + function supplyRatePerBlock() external view returns (uint256); + function symbol() external view returns (string memory); + function totalAdminFees() external view returns (uint256); + function totalBorrows() external view returns (uint256); + function totalBorrowsCurrent() external returns (uint256); + function totalFuseFees() external view returns (uint256); + function totalReserves() external view returns (uint256); + function totalSupply() external view returns (uint256); + function transfer(address dst, uint256 amount) external returns (bool); + function transferFrom( + address src, + address dst, + uint256 amount + ) external returns (bool); + function underlying() external view returns (address); +} diff --git a/packages/contracts/src/dollar/interfaces/IDollarAmoMinter.sol b/packages/contracts/src/dollar/interfaces/IDollarAmoMinter.sol index c7f6431ea..d18000907 100644 --- a/packages/contracts/src/dollar/interfaces/IDollarAmoMinter.sol +++ b/packages/contracts/src/dollar/interfaces/IDollarAmoMinter.sol @@ -111,10 +111,16 @@ interface IDollarAmoMinter { /// @param _newTimelock The address of the new timelock. function setTimelock(address _newTimelock) external; + /// @notice Returns the timelock address. + function timelockAddress() external view returns (address _timelockAddr); + /// @notice Sets the custodian address. /// @param _custodianAddress The address of the custodian. function setCustodian(address _custodianAddress) external; + /// @notice Returns the custodian address. + function custodianAddress() external view returns (address _custodianAddr); + /// @notice Sets the dollar mint cap. /// @param _dollarMintCap The new dollar mint cap to be set. function setDollarMintCap(uint256 _dollarMintCap) external; diff --git a/packages/contracts/src/dollar/interfaces/IRariComptroller.sol b/packages/contracts/src/dollar/interfaces/IRariComptroller.sol new file mode 100644 index 000000000..36767ecdf --- /dev/null +++ b/packages/contracts/src/dollar/interfaces/IRariComptroller.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.8.19; + +interface IRariComptroller { + function _become(address unitroller) external; + function _borrowGuardianPaused() external view returns (bool); + function _mintGuardianPaused() external view returns (bool); + function _setBorrowPaused( + address cToken, + bool state + ) external returns (bool); + function _setCloseFactor( + uint256 newCloseFactorMantissa + ) external returns (uint256); + function _setCollateralFactor( + address cToken, + uint256 newCollateralFactorMantissa + ) external returns (uint256); + function _setLiquidationIncentive( + uint256 newLiquidationIncentiveMantissa + ) external returns (uint256); + function _setMaxAssets(uint256 newMaxAssets) external returns (uint256); + function _setMintPaused(address cToken, bool state) external returns (bool); + function _setPauseGuardian( + address newPauseGuardian + ) external returns (uint256); + function _setPriceOracle(address newOracle) external returns (uint256); + function _setSeizePaused(bool state) external returns (bool); + function _setTransferPaused(bool state) external returns (bool); + function _supportMarket(address cToken) external returns (uint256); + function _supportMarketAndSetCollateralFactor( + address cToken, + uint256 newCollateralFactorMantissa + ) external returns (uint256); + function _unsupportMarket(address cToken) external returns (uint256); + function accountAssets(address, uint256) external view returns (address); + function admin() external view returns (address); + function adminHasRights() external view returns (bool); + function allBorrowers(uint256) external view returns (address); + function allMarkets(uint256) external view returns (address); + function borrowAllowed( + address cToken, + address borrower, + uint256 borrowAmount + ) external returns (uint256); + function borrowGuardianPaused(address) external view returns (bool); + function borrowVerify( + address cToken, + address borrower, + uint256 borrowAmount + ) external; + function borrowWithinLimits( + address cToken, + uint256 accountBorrowsNew + ) external returns (uint256); + function cTokensByUnderlying(address) external view returns (address); + function checkMembership( + address account, + address cToken + ) external view returns (bool); + function closeFactorMantissa() external view returns (uint256); + function comptrollerImplementation() external view returns (address); + function enforceWhitelist() external view returns (bool); + function enterMarkets( + address[] calldata cTokens + ) external returns (uint256[] memory); + function exitMarket(address cTokenAddress) external returns (uint256); + function fuseAdminHasRights() external view returns (bool); + function getAccountLiquidity( + address account + ) external view returns (uint256, uint256, uint256); + function getAllBorrowers() external view returns (address[] memory); + function getAllMarkets() external view returns (address[] memory); + function getAssetsIn( + address account + ) external view returns (address[] memory); + function getHypotheticalAccountLiquidity( + address account, + address cTokenModify, + uint256 redeemTokens, + uint256 borrowAmount + ) external view returns (uint256, uint256, uint256); + function getMaxBorrow( + address account, + address cTokenModify + ) external returns (uint256, uint256); + function getMaxRedeem( + address account, + address cTokenModify + ) external returns (uint256, uint256); + function isComptroller() external view returns (bool); + function liquidateBorrowAllowed( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint256 repayAmount + ) external returns (uint256); + function liquidateBorrowVerify( + address cTokenBorrowed, + address cTokenCollateral, + address liquidator, + address borrower, + uint256 actualRepayAmount, + uint256 seizeTokens + ) external; + function liquidateCalculateSeizeTokens( + address cTokenBorrowed, + address cTokenCollateral, + uint256 actualRepayAmount + ) external view returns (uint256, uint256); + function liquidationIncentiveMantissa() external view returns (uint256); + function markets( + address + ) external view returns (bool isListed, uint256 collateralFactorMantissa); + function maxAssets() external view returns (uint256); + function mintAllowed( + address cToken, + address minter, + uint256 mintAmount + ) external returns (uint256); + function mintGuardianPaused(address) external view returns (bool); + function mintVerify( + address cToken, + address minter, + uint256 actualMintAmount, + uint256 mintTokens + ) external; + function mintWithinLimits( + address cToken, + uint256 exchangeRateMantissa, + uint256 accountTokens, + uint256 mintAmount + ) external returns (uint256); + function oracle() external view returns (address); + function pauseGuardian() external view returns (address); + function pendingAdmin() external view returns (address); + function pendingComptrollerImplementation() external view returns (address); + function redeemAllowed( + address cToken, + address redeemer, + uint256 redeemTokens + ) external returns (uint256); + function redeemVerify( + address cToken, + address redeemer, + uint256 redeemAmount, + uint256 redeemTokens + ) external; + function repayBorrowAllowed( + address cToken, + address payer, + address borrower, + uint256 repayAmount + ) external returns (uint256); + function repayBorrowVerify( + address cToken, + address payer, + address borrower, + uint256 actualRepayAmount, + uint256 borrowerIndex + ) external; + function seizeAllowed( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint256 seizeTokens + ) external returns (uint256); + function seizeGuardianPaused() external view returns (bool); + function seizeVerify( + address cTokenCollateral, + address cTokenBorrowed, + address liquidator, + address borrower, + uint256 seizeTokens + ) external; + function suppliers(address) external view returns (bool); + function transferAllowed( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external returns (uint256); + function transferGuardianPaused() external view returns (bool); + function transferVerify( + address cToken, + address src, + address dst, + uint256 transferTokens + ) external; + function whitelist(address) external view returns (bool); + function whitelistArray(uint256) external view returns (address); +} diff --git a/packages/contracts/src/dollar/libraries/LibDollarAmoMinter.sol b/packages/contracts/src/dollar/libraries/LibDollarAmoMinter.sol index 86349259b..092e3d25d 100644 --- a/packages/contracts/src/dollar/libraries/LibDollarAmoMinter.sol +++ b/packages/contracts/src/dollar/libraries/LibDollarAmoMinter.sol @@ -489,6 +489,12 @@ library LibDollarAmoMinter { minterStorage.timelockAddress = _newTimelock; } + function timelockAddress() internal view returns (address _timelockAddr) { + DollarAmoMinterData storage minterStorage = dollarAmoMinterStorage(); + + return minterStorage.timelockAddress; + } + /// @notice Sets the custodian address. /// @param _custodianAddress The new address to be set as the custodian. /// @dev This function updates the custodian address and ensures it is not a zero address. @@ -502,6 +508,15 @@ library LibDollarAmoMinter { minterStorage.custodianAddress = _custodianAddress; } + /// @notice This function retrieves the custodian address from the DollarAmoMinterData storage. + /// @dev Accesses the DollarAmoMinterData storage to get the custodian address. + /// @return _custodianAddr The address of the custodian stored in the DollarAmoMinterData. + function custodianAddress() internal view returns (address _custodianAddr) { + DollarAmoMinterData storage minterStorage = dollarAmoMinterStorage(); + + return minterStorage.custodianAddress; + } + /// @notice Sets the dollar mint cap. /// @param _dollarMintCap The new dollar mint cap to be set. /// @dev This function updates the dollar mint cap. diff --git a/packages/contracts/src/dollar/libraries/LibFuseRariAmoStrategy.sol b/packages/contracts/src/dollar/libraries/LibFuseRariAmoStrategy.sol new file mode 100644 index 000000000..1228b5994 --- /dev/null +++ b/packages/contracts/src/dollar/libraries/LibFuseRariAmoStrategy.sol @@ -0,0 +1,152 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "@openzeppelin/contracts/utils/math/Math.sol"; +import "../interfaces/IUbiquityDollarToken.sol"; +import "../interfaces/IRariComptroller.sol"; +import "../interfaces/IDollarAmoMinter.sol"; +import "../interfaces/ICErc20Delegator.sol"; +import "./Constants.sol"; + +library LibFuseRariAmoStrategy { + using Math for uint256; + + bytes32 constant FUSERARIAMO_CONTROL_STORAGE_SLOT = + bytes32( + uint256(keccak256("ubiquity.contracts.fuserariamo.storage")) - 1 + ); + + struct FuseRariAmoStrategyData { + IUbiquityDollarToken DOLLAR; + IDollarAmoMinter amoMinter; + uint256 globalDollarCollateralRatio; + address timelockAddress; + address custodianAddress; + address[] fusePoolsArray; + mapping(address => bool) fusePools; + } + + function fuseRariAmoStrategyStorage() + internal + pure + returns (FuseRariAmoStrategyData storage l) + { + bytes32 slot = FUSERARIAMO_CONTROL_STORAGE_SLOT; + assembly { + l.slot := slot + } + } + + function init( + address _dollar, + address[] memory _initialUnitrollers, + address[] memory _initialFusePools, + address _amoMinterAddress, + uint256 _globalDollarCollateralRatio + ) internal { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + strategyStorage.fusePoolsArray = _initialFusePools; + for (uint256 i = 0; i < strategyStorage.fusePoolsArray.length; i++) { + // Set the pools as valid + strategyStorage.fusePools[_initialFusePools[i]] = true; + + // Enter markets + address[] memory cTokens = new address[](1); + cTokens[0] = strategyStorage.fusePoolsArray[i]; + IRariComptroller(_initialUnitrollers[i]).enterMarkets(cTokens); + + strategyStorage.DOLLAR = IUbiquityDollarToken(_dollar); + strategyStorage.amoMinter = IDollarAmoMinter(_amoMinterAddress); + strategyStorage.custodianAddress = strategyStorage + .amoMinter + .custodianAddress(); + strategyStorage.timelockAddress = strategyStorage + .amoMinter + .timelockAddress(); + + strategyStorage + .globalDollarCollateralRatio = _globalDollarCollateralRatio; + } + } + + function showAllocations() + internal + view + returns (uint256[3] memory allocations) + { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + allocations[0] = strategyStorage.DOLLAR.balanceOf(address(this)); + + uint256 sumFusePoolTally = 0; + for (uint i = 0; i < strategyStorage.fusePoolsArray.length; i++) { + // Make sure the pool is enabled first + address poolAddress = strategyStorage.fusePoolsArray[i]; + if (strategyStorage.fusePools[poolAddress]) { + sumFusePoolTally = sumFusePoolTally + dollarInPoolByPoolId(i); + } + } + allocations[1] = sumFusePoolTally; + + allocations[2] = allocations[0] + allocations[1]; // Total FRAX value + } + + function dollarBalances() + internal + view + returns (uint256 dollarVal, uint256 collatVal) + { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + dollarVal = showAllocations()[2]; + collatVal = + (dollarVal) * + (strategyStorage.globalDollarCollateralRatio / + UBIQUITY_POOL_PRICE_PRECISION); + } + + function allPoolAddresses() internal view returns (address[] memory) { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + return strategyStorage.fusePoolsArray; + } + + function allPoolsLength() internal view returns (uint256) { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + return strategyStorage.fusePoolsArray.length; + } + + function poolAddrToId( + address _poolAddress + ) internal view returns (uint256) { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + for (uint i = 0; i < strategyStorage.fusePoolsArray.length; i++) { + if (strategyStorage.fusePoolsArray[i] == _poolAddress) { + return i; + } + } + revert("Pool not found"); + } + + function dollarInPoolByPoolId( + uint256 _poolId + ) internal view returns (uint256) { + FuseRariAmoStrategyData + storage strategyStorage = fuseRariAmoStrategyStorage(); + + ICErc20Delegator delegator = ICErc20Delegator( + strategyStorage.fusePoolsArray[_poolId] + ); + uint256 cTokenBalance = delegator.balanceOf(address(this)); + return cTokenBalance * (delegator.exchangeRateStored() / (1e18)); + } +} diff --git a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol index 6f3ad010c..bd506afe7 100644 --- a/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol +++ b/packages/contracts/test/diamond/facets/UbiquityPoolFacet.t.sol @@ -72,8 +72,16 @@ contract MockDollarAmoMinter is IDollarAmoMinter { function setTimelock(address new_timelock) external {} + function timelockAddress() external view returns (address _timelockAddr) {} + function setCustodian(address _custodian_address) external {} + function custodianAddress() + external + view + returns (address _custodianAddr) + {} + function setDollarMintCap(uint256 _dollar_mint_cap) external {} function setCreditsMintCap(uint256 _credits_mint_cap) external {}