fluid-contracts-public/contracts/periphery/resolvers/lending/main.sol
2024-07-11 13:05:09 +00:00

228 lines
9.0 KiB
Solidity

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/draft-IERC20Permit.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { LiquidityCalcs } from "../../../libraries/liquidityCalcs.sol";
import { IFluidLendingFactory } from "../../../protocols/lending/interfaces/iLendingFactory.sol";
import { IFluidLendingRewardsRateModel } from "../../../protocols/lending/interfaces/iLendingRewardsRateModel.sol";
import { IFluidLiquidity } from "../../../liquidity/interfaces/iLiquidity.sol";
import { IAllowanceTransfer } from "../../../protocols/lending/interfaces/permit2/iAllowanceTransfer.sol";
import { IFToken, IFTokenNativeUnderlying } from "../../../protocols/lending/interfaces/iFToken.sol";
import { IFluidLiquidityResolver } from "../../../periphery/resolvers/liquidity/iLiquidityResolver.sol";
import { Structs as LiquidityStructs } from "../../../periphery/resolvers/liquidity/structs.sol";
import { IFluidLendingResolver } from "./iLendingResolver.sol";
import { Structs } from "./structs.sol";
/// @notice Fluid Lending protocol (fTokens) resolver
/// Implements various view-only methods to give easy access to Lending protocol data.
contract FluidLendingResolver is IFluidLendingResolver, Structs {
/// @dev address that is mapped to the chain native token
address internal constant _NATIVE_TOKEN_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @inheritdoc IFluidLendingResolver
IFluidLendingFactory public immutable LENDING_FACTORY;
/// @inheritdoc IFluidLendingResolver
IFluidLiquidityResolver public immutable LIQUIDITY_RESOLVER;
/// @notice thrown if an input param address is zero
error FluidLendingResolver__AddressZero();
/// @notice constructor sets the immutable `LENDING_FACTORY` and `LIQUIDITY_RESOLVER` address
constructor(IFluidLendingFactory lendingFactory_, IFluidLiquidityResolver liquidityResolver_) {
if (address(lendingFactory_) == address(0) || address(liquidityResolver_) == address(0)) {
revert FluidLendingResolver__AddressZero();
}
LENDING_FACTORY = lendingFactory_;
LIQUIDITY_RESOLVER = liquidityResolver_;
}
/// @inheritdoc IFluidLendingResolver
function isLendingFactoryAuth(address auth_) external view returns (bool) {
return LENDING_FACTORY.isAuth(auth_);
}
/// @inheritdoc IFluidLendingResolver
function isLendingFactoryDeployer(address deployer_) external view returns (bool) {
return LENDING_FACTORY.isDeployer(deployer_);
}
/// @inheritdoc IFluidLendingResolver
function getAllFTokenTypes() public view returns (string[] memory) {
return LENDING_FACTORY.fTokenTypes();
}
/// @inheritdoc IFluidLendingResolver
function getAllFTokens() public view returns (address[] memory) {
return LENDING_FACTORY.allTokens();
}
/// @inheritdoc IFluidLendingResolver
function computeFToken(address asset_, string calldata fTokenType_) external view returns (address) {
return LENDING_FACTORY.computeToken(asset_, fTokenType_);
}
/// @inheritdoc IFluidLendingResolver
function getFTokenDetails(IFToken fToken_) public view returns (FTokenDetails memory fTokenDetails_) {
address underlying_ = fToken_.asset();
bool isNativeUnderlying_ = false;
try IFTokenNativeUnderlying(address(fToken_)).NATIVE_TOKEN_ADDRESS() {
// if NATIVE_TOKEN_ADDRESS is defined, fTokenType must be NativeUnderlying.
isNativeUnderlying_ = true;
} catch {}
bool supportsEIP2612Deposits_ = false;
try IERC20Permit(underlying_).DOMAIN_SEPARATOR() {
// if DOMAIN_SEPARATOR is defined, we assume underlying supports EIP2612. Not a 100% guarantee
supportsEIP2612Deposits_ = true;
} catch {}
(, uint256 rewardsRate_) = getFTokenRewards(fToken_);
(
LiquidityStructs.UserSupplyData memory userSupplyData_,
LiquidityStructs.OverallTokenData memory overallTokenData_
) = LIQUIDITY_RESOLVER.getUserSupplyData(
address(fToken_),
isNativeUnderlying_ ? _NATIVE_TOKEN_ADDRESS : underlying_
);
uint256 totalAssets_ = fToken_.totalAssets();
fTokenDetails_ = FTokenDetails(
address(fToken_),
supportsEIP2612Deposits_,
isNativeUnderlying_,
fToken_.name(),
fToken_.symbol(),
fToken_.decimals(),
underlying_,
totalAssets_,
fToken_.totalSupply(),
fToken_.convertToShares(10 ** fToken_.decimals()), // example convertToShares for 10 ** decimals
fToken_.convertToAssets(10 ** fToken_.decimals()), // example convertToAssets for 10 ** decimals
rewardsRate_,
overallTokenData_.supplyRate,
int256(userSupplyData_.supply) - int256(totalAssets_), // rebalanceDifference
userSupplyData_
);
return fTokenDetails_;
}
/// @inheritdoc IFluidLendingResolver
function getFTokenInternalData(
IFToken fToken_
)
public
view
returns (
IFluidLiquidity liquidity_,
IFluidLendingFactory lendingFactory_,
IFluidLendingRewardsRateModel lendingRewardsRateModel_,
IAllowanceTransfer permit2_,
address rebalancer_,
bool rewardsActive_,
uint256 liquidityBalance_,
uint256 liquidityExchangePrice_,
uint256 tokenExchangePrice_
)
{
return fToken_.getData();
}
/// @inheritdoc IFluidLendingResolver
function getFTokensEntireData() public view returns (FTokenDetails[] memory) {
address[] memory allTokens = getAllFTokens();
FTokenDetails[] memory fTokenDetailsArr_ = new FTokenDetails[](allTokens.length);
for (uint256 i = 0; i < allTokens.length; ) {
fTokenDetailsArr_[i] = getFTokenDetails(IFToken(allTokens[i]));
unchecked {
i++;
}
}
return fTokenDetailsArr_;
}
/// @inheritdoc IFluidLendingResolver
function getUserPositions(address user_) external view returns (FTokenDetailsUserPosition[] memory) {
FTokenDetails[] memory fTokensEntireData_ = getFTokensEntireData();
FTokenDetailsUserPosition[] memory userPositionArr_ = new FTokenDetailsUserPosition[](
fTokensEntireData_.length
);
for (uint256 i = 0; i < fTokensEntireData_.length; ) {
userPositionArr_[i].fTokenDetails = fTokensEntireData_[i];
userPositionArr_[i].userPosition = getUserPosition(IFToken(fTokensEntireData_[i].tokenAddress), user_);
unchecked {
i++;
}
}
return userPositionArr_;
}
/// @inheritdoc IFluidLendingResolver
function getFTokenRewards(
IFToken fToken_
) public view returns (IFluidLendingRewardsRateModel rewardsRateModel_, uint256 rewardsRate_) {
bool rewardsActive_;
(, , rewardsRateModel_, , , rewardsActive_, , , ) = fToken_.getData();
if (rewardsActive_ && address(rewardsRateModel_) != address(0)) {
(rewardsRate_, , ) = rewardsRateModel_.getRate(fToken_.totalAssets());
}
}
/// @inheritdoc IFluidLendingResolver
function getFTokenRewardsRateModelConfig(
IFToken fToken_
)
public
view
returns (
uint256 duration_,
uint256 startTime_,
uint256 endTime_,
uint256 startTvl_,
uint256 maxRate_,
uint256 rewardAmount_,
address initiator_
)
{
IFluidLendingRewardsRateModel rewardsRateModel_;
(, , rewardsRateModel_, , , , , , ) = fToken_.getData();
if (address(rewardsRateModel_) != address(0)) {
(duration_, startTime_, endTime_, startTvl_, maxRate_, rewardAmount_, initiator_) = rewardsRateModel_
.getConfig();
}
}
/// @inheritdoc IFluidLendingResolver
function getUserPosition(IFToken fToken_, address user_) public view returns (UserPosition memory userPosition) {
IERC20 underlying_ = IERC20(fToken_.asset());
userPosition.fTokenShares = fToken_.balanceOf(user_);
userPosition.underlyingAssets = fToken_.convertToAssets(userPosition.fTokenShares);
userPosition.underlyingBalance = underlying_.balanceOf(user_);
userPosition.allowance = underlying_.allowance(user_, address(fToken_));
}
/// @inheritdoc IFluidLendingResolver
function getPreviews(
IFToken fToken_,
uint256 assets_,
uint256 shares_
)
public
view
returns (uint256 previewDeposit_, uint256 previewMint_, uint256 previewWithdraw_, uint256 previewRedeem_)
{
previewDeposit_ = fToken_.previewDeposit(assets_);
previewMint_ = fToken_.previewMint(shares_);
previewWithdraw_ = fToken_.previewWithdraw(assets_);
previewRedeem_ = fToken_.previewRedeem(shares_);
}
}