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

208 lines
9.5 KiB
Solidity

// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.21;
import { UUPSUpgradeable } from "@openzeppelin/contracts/proxy/utils/UUPSUpgradeable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { IFluidLiquidity } from "../liquidity/interfaces/iLiquidity.sol";
import { IFluidLendingFactory } from "../protocols/lending/interfaces/iLendingFactory.sol";
import { IFTokenAdmin } from "../protocols/lending/interfaces/iFToken.sol";
import { IFluidVaultT1 } from "../protocols/vault/interfaces/iVaultT1.sol";
import { SafeTransfer } from "../libraries/safeTransfer.sol";
import { Variables } from "./variables.sol";
import { Events } from "./events.sol";
import { ErrorTypes } from "./errorTypes.sol";
import { Error } from "./error.sol";
abstract contract ReserveContractAuth is Variables, Error, Events {
using EnumerableSet for EnumerableSet.AddressSet;
/// @dev validates that an address is not the zero address
modifier validAddress(address value_) {
if (value_ == address(0)) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__AddressZero);
}
_;
}
/// @notice Checks that the sender is an auth
modifier onlyAuth() {
if (!isAuth[msg.sender] && owner() != msg.sender)
revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);
_;
}
/// @notice Updates an auth's status as an auth
/// @param auth_ The address to update
/// @param isAuth_ Whether or not the address should be an auth
function updateAuth(address auth_, bool isAuth_) external onlyOwner validAddress(auth_) {
isAuth[auth_] = isAuth_;
emit LogUpdateAuth(auth_, isAuth_);
}
/// @notice Updates a rebalancer's status as a rebalancer
/// @param rebalancer_ The address to update
/// @param isRebalancer_ Whether or not the address should be a rebalancer
function updateRebalancer(address rebalancer_, bool isRebalancer_) external onlyAuth validAddress(rebalancer_) {
isRebalancer[rebalancer_] = isRebalancer_;
emit LogUpdateRebalancer(rebalancer_, isRebalancer_);
}
/// @notice Approves protocols to spend the reserves tokens
/// @dev The parameters are parallel arrays
/// @param protocols_ The protocols that will be spending reserve tokens
/// @param tokens_ The tokens to approve
/// @param amounts_ The amounts to approve
function approve(
address[] memory protocols_,
address[] memory tokens_,
uint256[] memory amounts_
) external onlyAuth {
if (protocols_.length != tokens_.length || tokens_.length != amounts_.length) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
}
for (uint256 i = 0; i < protocols_.length; i++) {
address protocol_ = protocols_[i];
address token_ = tokens_[i];
uint256 amount_ = amounts_[i];
uint256 existingAllowance_ = IERC20(token_).allowance(address(this), protocol_);
// making approval 0 first and then re-approving with a new amount.
SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);
SafeERC20.safeApprove(IERC20(address(token_)), protocol_, amount_);
_protocolTokens[protocol_].add(token_);
emit LogAllow(protocol_, token_, amount_, existingAllowance_);
}
}
/// @notice Revokes protocols' ability to spend the reserves tokens
/// @dev The parameters are parallel arrays
/// @param protocols_ The protocols that will no longer be spending reserve tokens
/// @param tokens_ The tokens to revoke
function revoke(address[] memory protocols_, address[] memory tokens_) external onlyAuth {
if (protocols_.length != tokens_.length) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__InvalidInputLenghts);
}
for (uint256 i = 0; i < protocols_.length; i++) {
address protocol_ = protocols_[i];
address token_ = tokens_[i];
SafeERC20.safeApprove(IERC20(address(token_)), protocol_, 0);
_protocolTokens[protocol_].remove(token_);
emit LogRevoke(protocol_, token_);
}
}
}
/// @title Reserve Contract
/// @notice This contract manages the approval of tokens for use by protocols and
/// the execution of rebalances on protocols
contract FluidReserveContract is Error, ReserveContractAuth, UUPSUpgradeable {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
/// @notice Checks that the sender is a rebalancer
modifier onlyRebalancer() {
if (!isRebalancer[msg.sender]) revert FluidReserveContractError(ErrorTypes.ReserveContract__Unauthorized);
_;
}
constructor(IFluidLiquidity liquidity_) validAddress(address(liquidity_)) Variables(liquidity_) {
// ensure logic contract initializer is not abused by disabling initializing
// see https://forum.openzeppelin.com/t/security-advisory-initialize-uups-implementation-contracts/15301
// and https://docs.openzeppelin.com/upgrades-plugins/1.x/writing-upgradeable#initializing_the_implementation_contract
_disableInitializers();
}
/// @notice initializes the contract
/// @param _auths The addresses that have the auth to approve and revoke protocol token allowances
/// @param _rebalancers The addresses that can execute a rebalance on a protocol
/// @param owner_ owner address is able to upgrade contract and update auth users
function initialize(
address[] memory _auths,
address[] memory _rebalancers,
address owner_
) public initializer validAddress(owner_) {
for (uint256 i = 0; i < _auths.length; i++) {
isAuth[_auths[i]] = true;
emit LogUpdateAuth(_auths[i], true);
}
for (uint256 i = 0; i < _rebalancers.length; i++) {
isRebalancer[_rebalancers[i]] = true;
emit LogUpdateRebalancer(_rebalancers[i], true);
}
_transferOwnership(owner_);
}
function _authorizeUpgrade(address) internal override onlyOwner {}
/// @notice override renounce ownership as it could leave the contract in an unwanted state if called by mistake.
function renounceOwnership() public view override onlyOwner {
revert FluidReserveContractError(ErrorTypes.ReserveContract__RenounceOwnershipUnsupported);
}
/// @notice Executes a rebalance on a protocol by calling that protocol's `rebalance` function
/// @param protocol_ The protocol to rebalance
/// @param value_ any msg.value to send along (as fetched from resolver!)
function rebalanceFToken(address protocol_, uint256 value_) external payable onlyRebalancer {
uint256 amount_ = IFTokenAdmin(protocol_).rebalance{ value: value_ }();
emit LogRebalanceFToken(protocol_, amount_);
}
/// @notice Executes a rebalance on a protocol by calling that protocol's `rebalance` function
/// @param protocol_ The protocol to rebalance
/// @param value_ any msg.value to send along (as fetched from resolver!)
function rebalanceVault(address protocol_, uint256 value_) external payable onlyRebalancer {
(int256 colAmount_, int256 debtAmount_) = IFluidVaultT1(protocol_).rebalance{ value: value_ }();
IFluidVaultT1.ConstantViews memory constants_ = IFluidVaultT1(protocol_).constantsView();
if (constants_.supplyToken == NATIVE_TOKEN_ADDRESS) {
if (value_ > 0 && colAmount_ < 0) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
}
}
if (constants_.borrowToken == NATIVE_TOKEN_ADDRESS) {
if (value_ > 0 && debtAmount_ > 0) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
}
}
if (value_ > 0 && !(constants_.supplyToken == NATIVE_TOKEN_ADDRESS || constants_.borrowToken == NATIVE_TOKEN_ADDRESS)) {
revert FluidReserveContractError(ErrorTypes.ReserveContract__WrongValueSent);
}
emit LogRebalanceVault(protocol_, colAmount_, debtAmount_);
}
function transferFunds(address[] calldata tokens_) external virtual onlyAuth {
for (uint256 i = 0; i < tokens_.length; i++) {
SafeTransfer.safeTransfer(
address(tokens_[i]),
address(LIQUIDITY),
IERC20(tokens_[i]).balanceOf(address(this))
);
emit LogTransferFunds(tokens_[i]);
}
}
/// @notice Gets the tokens that are approved for use by a protocol
/// @param protocol_ The protocol to get the tokens for
/// @return result_ The tokens that are approved for use by the protocol
function getProtocolTokens(address protocol_) external view returns (address[] memory result_) {
EnumerableSet.AddressSet storage tokens_ = _protocolTokens[protocol_];
result_ = new address[](tokens_.length());
for (uint256 i = 0; i < tokens_.length(); i++) {
result_[i] = tokens_.at(i);
}
}
/// @notice allow receive native token
receive() external payable {}
}