From 573f5fe7852d192b775a9dc2cca1db8ca1af4c58 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 10 Oct 2024 10:07:51 -0400 Subject: [PATCH 1/7] feat: Mellow LRT Vault price feed contract --- contracts/mellow-lrt/interfaces/IVault.sol | 403 ++++++++++++++++++ .../interfaces/IVaultConfigurator.sol | 395 +++++++++++++++++ .../external/chainlink/IAggregatorV3.sol | 17 + .../external/lido/IDepositContract.sol | 6 + .../external/lido/IDepositSecurityModule.sol | 33 ++ .../interfaces/external/lido/ILidoLocator.sol | 6 + .../external/lido/IStakingRouter.sol | 26 ++ .../interfaces/external/lido/ISteth.sol | 10 + .../interfaces/external/lido/IWSteth.sol | 12 + .../interfaces/external/lido/IWeth.sol | 7 + .../external/lido/IWithdrawalQueue.sol | 6 + .../interfaces/external/symbiotic/IBond.sol | 107 +++++ .../external/symbiotic/IDefaultBond.sol | 49 +++ .../external/uniswap/ISwapRouter.sol | 72 ++++ .../interfaces/modules/IDefaultModule.sol | 13 + .../interfaces/modules/ITvlModule.sol | 24 ++ .../modules/erc20/IERC20SwapModule.sol | 55 +++ .../modules/erc20/IERC20TvlModule.sol | 25 ++ .../modules/erc20/IManagedTvlModule.sol | 42 ++ .../modules/obol/IStakingModule.sol | 103 +++++ .../modules/symbiotic/IDefaultBondModule.sol | 44 ++ .../symbiotic/IDefaultBondTvlModule.sol | 41 ++ .../interfaces/oracles/IChainlinkOracle.sol | 113 +++++ .../oracles/IManagedRatiosOracle.sol | 71 +++ .../interfaces/oracles/IPriceOracle.sol | 21 + .../interfaces/oracles/IRatiosOracle.sol | 21 + .../interfaces/security/IAdminProxy.sol | 230 ++++++++++ .../strategies/IDefaultBondStrategy.sol | 103 +++++ .../strategies/ISimpleDVTStakingStrategy.sol | 97 +++++ .../utils/IDefaultAccessControl.sol | 40 ++ .../interfaces/utils/IDepositCallback.sol | 19 + .../interfaces/utils/IDepositWrapper.sol | 82 ++++ .../interfaces/utils/IWithdrawalCallback.sol | 15 + .../validators/IAllowAllValidator.sol | 10 + .../validators/IDefaultBondValidator.sol | 56 +++ .../validators/IERC20SwapValidator.sol | 83 ++++ .../validators/IManagedValidator.sol | 293 +++++++++++++ .../interfaces/validators/IValidator.sol | 22 + contracts/mellowpricefeed/CloneFactory.sol | 0 contracts/mellowpricefeed/MellowPriceFeed.sol | 133 ++++++ 40 files changed, 2905 insertions(+) create mode 100644 contracts/mellow-lrt/interfaces/IVault.sol create mode 100644 contracts/mellow-lrt/interfaces/IVaultConfigurator.sol create mode 100644 contracts/mellow-lrt/interfaces/external/chainlink/IAggregatorV3.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IDepositContract.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IDepositSecurityModule.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/ILidoLocator.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IStakingRouter.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/ISteth.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IWSteth.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IWeth.sol create mode 100644 contracts/mellow-lrt/interfaces/external/lido/IWithdrawalQueue.sol create mode 100644 contracts/mellow-lrt/interfaces/external/symbiotic/IBond.sol create mode 100644 contracts/mellow-lrt/interfaces/external/symbiotic/IDefaultBond.sol create mode 100644 contracts/mellow-lrt/interfaces/external/uniswap/ISwapRouter.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/IDefaultModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/ITvlModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/erc20/IERC20SwapModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/erc20/IERC20TvlModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/erc20/IManagedTvlModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/obol/IStakingModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondModule.sol create mode 100644 contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondTvlModule.sol create mode 100644 contracts/mellow-lrt/interfaces/oracles/IChainlinkOracle.sol create mode 100644 contracts/mellow-lrt/interfaces/oracles/IManagedRatiosOracle.sol create mode 100644 contracts/mellow-lrt/interfaces/oracles/IPriceOracle.sol create mode 100644 contracts/mellow-lrt/interfaces/oracles/IRatiosOracle.sol create mode 100644 contracts/mellow-lrt/interfaces/security/IAdminProxy.sol create mode 100644 contracts/mellow-lrt/interfaces/strategies/IDefaultBondStrategy.sol create mode 100644 contracts/mellow-lrt/interfaces/strategies/ISimpleDVTStakingStrategy.sol create mode 100644 contracts/mellow-lrt/interfaces/utils/IDefaultAccessControl.sol create mode 100644 contracts/mellow-lrt/interfaces/utils/IDepositCallback.sol create mode 100644 contracts/mellow-lrt/interfaces/utils/IDepositWrapper.sol create mode 100644 contracts/mellow-lrt/interfaces/utils/IWithdrawalCallback.sol create mode 100644 contracts/mellow-lrt/interfaces/validators/IAllowAllValidator.sol create mode 100644 contracts/mellow-lrt/interfaces/validators/IDefaultBondValidator.sol create mode 100644 contracts/mellow-lrt/interfaces/validators/IERC20SwapValidator.sol create mode 100644 contracts/mellow-lrt/interfaces/validators/IManagedValidator.sol create mode 100644 contracts/mellow-lrt/interfaces/validators/IValidator.sol create mode 100644 contracts/mellowpricefeed/CloneFactory.sol create mode 100644 contracts/mellowpricefeed/MellowPriceFeed.sol diff --git a/contracts/mellow-lrt/interfaces/IVault.sol b/contracts/mellow-lrt/interfaces/IVault.sol new file mode 100644 index 0000000..d3a790e --- /dev/null +++ b/contracts/mellow-lrt/interfaces/IVault.sol @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/utils/Arrays.sol"; + +import "./modules/ITvlModule.sol"; +import "./validators/IValidator.sol"; + +import "./oracles/IPriceOracle.sol"; +import "./oracles/IRatiosOracle.sol"; + +import "./utils/IDepositCallback.sol"; +import "./utils/IWithdrawalCallback.sol"; + +import "./IVaultConfigurator.sol"; + +/** + * @title IVault + * @notice Interface defining core methods, constants, and errors for vault contracts. + * Includes events, data structures, functions, and permissions required for managing the vault. + * @dev Main contract of the system managing interactions between users, administrators, and operators. + * System parameters are set within the corresponding contract - VaultConfigurator. + * Upon deposit, LP tokens are issued to users based on asset valuation by oracles. + * Deposits are made through the deposit function, where a deposit can only be made in underlyingTokens and + * only at the specified ratiosOracle ratio. Deposits can be paused by setting the isDepositLocked flag. + * + * Withdrawals can occur through two scenarios: + * - Regular withdrawal via the registerWithdrawal function and emergency withdrawal via the emergencyWithdraw function. + * In a regular withdrawal, the user registers a withdrawal request, after which the operator must perform a series of operations + * to ensure there are enough underlyingTokens on the vault's balance to fulfill the user's request. Subsequently, the operator must call + * the processWithdrawals function. If a user's request is not processed within the emergencyWithdrawalDelay period, the user can perform an emergency withdrawal. + * Note! In this case, the user may receive less funds than entitled by the system, as this function only handles ERC20 tokens in the system. + * Therefore, if the system has a base asset that is not represented as an ERC20 token, the corresponding portion of the funds will be lost by the user. + * + * It is assumed that the main system management will occur through calls to delegateModules via delegateCalls on behalf of the operator. + * For this to be possible, certain conditions must be met: + * - From the validator's perspective, two conditions must be met: + * 1. The caller must have the right to call the delegateCall function with the corresponding data parameter. + * 2. The contract itself must be able to call the function on the delegateModule with the specified data. + * - From the configurator's perspective, the called module must have the appropriate approval - isDelegateModuleApproved. + * + * If external calls need to be made, the externalCall function is used, for the execution of which a similar set of properties exists: + * - From the validator's perspective, two conditions must be met: + * 1. The caller must have the right to call the externalCall function with the corresponding data parameter. + * 2. The contract itself must be able to call the function on the external contract with the specified data. + * - From the configurator's perspective, the called contract must NOT have isDelegateModuleApproved permission. + * + * Vault also has the functionality of adding and removing underlyingTokens, as well as tvlModules. + * For this purpose, the following functions are available, which can only be called by the vault's admin: + * - addToken + * - removeToken + * - addTvlModule + * - removeTvlModule + * Upon calling removeToken, it is checked that the underlyingTvl function for the specified token returns a zero value. Otherwise, the function reverts with a NonZeroValue error. + * It is important to note that there is no such check when calling removeTvlModule, so when updating parameters, sequential execution of a transaction to remove the old and add the new tvlModule is implied. + */ +interface IVault is IERC20 { + /// @dev Errors + error Deadline(); + error InvalidState(); + error InvalidLength(); + error InvalidToken(); + error NonZeroValue(); + error ValueZero(); + error InsufficientLpAmount(); + error InsufficientAmount(); + error LimitOverflow(); + error AlreadyAdded(); + + /// @notice Struct representing a user's withdrawal request. + struct WithdrawalRequest { + address to; + uint256 lpAmount; + bytes32 tokensHash; // keccak256 hash of the tokens array at the moment of request + uint256[] minAmounts; + uint256 deadline; + uint256 timestamp; + } + + /// @notice Struct representing the current state used for processing withdrawals. + struct ProcessWithdrawalsStack { + address[] tokens; + uint128[] ratiosX96; + uint256[] erc20Balances; + uint256 totalSupply; + uint256 totalValue; + uint256 ratiosX96Value; + uint256 timestamp; + uint256 feeD9; + bytes32 tokensHash; // keccak256 hash of the tokens array at the moment of the call + } + + /// @notice 2^96, used for fixed-point arithmetic + function Q96() external view returns (uint256); + + /// @notice Multiplier of 1e9 + function D9() external view returns (uint256); + + /// @notice Returns the vault's configurator, which handles permissions and configuration settings. + /// @return IVaultConfigurator The address of the configurator contract. + function configurator() external view returns (IVaultConfigurator); + + /// @notice Returns the withdrawal request of a given user. + /// @param user The address of the user. + /// @return request The withdrawal request associated with the user. + function withdrawalRequest( + address user + ) external view returns (WithdrawalRequest memory request); + + /// @return count The number of users with pending withdrawal requests. + function pendingWithdrawersCount() external view returns (uint256 count); + + /// @notice Returns an array of addresses with pending withdrawal requests. + /// @return users An array of addresses with pending withdrawal requests. + function pendingWithdrawers() + external + view + returns (address[] memory users); + + /// @notice Returns an array of addresses with pending withdrawal requests. + /// @param limit The maximum number of users to return. + /// @param offset The number of users to skip before returning. + /// @return users An array of addresses with pending withdrawal requests. + function pendingWithdrawers( + uint256 limit, + uint256 offset + ) external view returns (address[] memory users); + + /// @notice Returns an array of underlying tokens of the vault. + /// @return underlyinigTokens_ An array of underlying token addresses. + function underlyingTokens() + external + view + returns (address[] memory underlyinigTokens_); + + /// @notice Checks if a token is an underlying token of the vault. + /// @return isUnderlyingToken_ true if the token is an underlying token of the vault. + function isUnderlyingToken( + address token + ) external view returns (bool isUnderlyingToken_); + + /// @notice Returns an array of addresses of all TVL modules. + /// @return tvlModules_ An array of TVL module addresses. + function tvlModules() external view returns (address[] memory tvlModules_); + + /// @notice Calculates and returns the total value locked (TVL) of the underlying tokens. + /// @return tokens An array of underlying token addresses. + /// @return amounts An array of the amounts of each underlying token in the TVL. + function underlyingTvl() + external + view + returns (address[] memory tokens, uint256[] memory amounts); + + /// @notice Calculates and returns the base TVL (Total Value Locked) across all tokens in the vault. + /// @return tokens An array of token addresses. + /// @return amounts An array of the amounts of each token in the base TVL. + function baseTvl() + external + view + returns (address[] memory tokens, uint256[] memory amounts); + + /// @notice Adds a new token to the list of underlying tokens in the vault. + /// @dev Only accessible by an admin. + /// @param token The address of the token to add. + function addToken(address token) external; + + /// @notice Removes a token from the list of underlying tokens in the vault. + /// @dev Only accessible by an admin. + /// @param token The address of the token to remove. + function removeToken(address token) external; + + /// @notice Adds a new TVL module to the vault. + /// @dev Only accessible by an admin. + /// @param module The address of the TVL module to add. + function addTvlModule(address module) external; + + /// @notice Removes an existing TVL module from the vault. + /// @dev Only accessible by an admin. + /// @param module The address of the TVL module to remove. + function removeTvlModule(address module) external; + + /// @notice Performs an external call to a given address with specified data. + /// @dev Only operators or admins should call this function. Checks access permissions. + /// @param to The address to which the call will be made. + /// @param data The calldata to use for the external call. + /// @return success Indicates if the call was successful. + /// @return response The response data from the external call. + /// @dev Checks permissions using the validator from the configurator. + function externalCall( + address to, + bytes calldata data + ) external returns (bool success, bytes memory response); + + /// @notice Executes a delegate call to a specified address with given data. + /// @dev Only operators or admins should call this function. Checks access permissions. + /// @param to The address to which the delegate call will be made. + /// @param data The calldata to use for the delegate call. + /// @return success Indicates if the delegate call was successful. + /// @return response The response data from the delegate call. + /// @dev Checks permissions using the validator from the configurator. + function delegateCall( + address to, + bytes calldata data + ) external returns (bool success, bytes memory response); + + /// @notice Deposits specified amounts of tokens into the vault in exchange for LP tokens. + /// @dev Only accessible when deposits are unlocked. + /// @param to The address to receive LP tokens. + /// @param amounts An array specifying the amounts for each underlying token. + /// @param minLpAmount The minimum amount of LP tokens to mint. + /// @param deadline The time before which the operation must be completed. + /// @param referralCode The referral code to use for the deposit. + /// @return actualAmounts The actual amounts deposited for each underlying token. + /// @return lpAmount The amount of LP tokens minted. + function deposit( + address to, + uint256[] memory amounts, + uint256 minLpAmount, + uint256 deadline, + uint256 referralCode + ) external returns (uint256[] memory actualAmounts, uint256 lpAmount); + + /// @notice Handles emergency withdrawals, proportionally withdrawing all tokens in the system (not just the underlying). + /// @dev Transfers tokens based on the user's share of lpAmount / totalSupply. + /// @param minAmounts An array of minimum amounts expected for each underlying token. + /// @param deadline The time before which the operation must be completed. + /// @return actualAmounts The actual amounts withdrawn for each token. + function emergencyWithdraw( + uint256[] memory minAmounts, + uint256 deadline + ) external returns (uint256[] memory actualAmounts); + + /// @notice Cancels a pending withdrawal request. + function cancelWithdrawalRequest() external; + + /// @notice Registers a new withdrawal request, optionally closing previous requests. + /// @param to The address to receive the withdrawn tokens. + /// @param lpAmount The amount of LP tokens to withdraw. + /// @param minAmounts An array specifying minimum amounts for each token. + /// @param deadline The time before which the operation must be completed. + /// @param requestDeadline The deadline before which the request should be fulfilled. + /// @param closePrevious Whether to close a previous request if it exists. + function registerWithdrawal( + address to, + uint256 lpAmount, + uint256[] memory minAmounts, + uint256 deadline, + uint256 requestDeadline, + bool closePrevious + ) external; + + /// @notice Analyzes a withdrawal request based on the current vault state. + /// @param s The current state stack to use for analysis. + /// @param request The withdrawal request to analyze. + /// @return processingPossible Whether processing is possible based on current vault state. + /// @return withdrawalPossible Whether the withdrawal can be fulfilled. + /// @return expectedAmounts The expected amounts to be withdrawn for each token. + function analyzeRequest( + ProcessWithdrawalsStack memory s, + WithdrawalRequest memory request + ) + external + pure + returns ( + bool processingPossible, + bool withdrawalPossible, + uint256[] memory expectedAmounts + ); + + /// @notice Calculates and returns the state stack required for processing withdrawal requests. + /// @return s The state stack with current vault balances and data. + function calculateStack() + external + view + returns (ProcessWithdrawalsStack memory s); + + /// @notice Processes multiple withdrawal requests by fulfilling eligible withdrawals. + /// @param users An array of user addresses whose withdrawal requests should be processed. + /// @return statuses An array indicating the status of each user's withdrawal request. + function processWithdrawals( + address[] memory users + ) external returns (bool[] memory statuses); + + /** + * @notice Emitted when a token is added to the vault. + * @param token The address of the token added. + */ + event TokenAdded(address token); + + /** + * @notice Emitted when a token is removed from the vault. + * @param token The address of the token removed. + */ + event TokenRemoved(address token); + + /** + * @notice Emitted when a TVL module is added to the vault. + * @param module The address of the TVL module added. + */ + event TvlModuleAdded(address module); + + /** + * @notice Emitted when a TVL module is removed from the vault. + * @param module The address of the TVL module removed. + */ + event TvlModuleRemoved(address module); + + /** + * @notice Emitted when an external call is made. + * @param to The address of the contract called. + * @param data The calldata of the call. + * @param success The success status of the call. + * @param response The response data of the call. + */ + event ExternalCall( + address indexed to, + bytes data, + bool success, + bytes response + ); + + /** + * @notice Emitted when a delegate call is made. + * @param to The address of the contract called. + * @param data The calldata of the call. + * @param success The success status of the call. + * @param response The response data of the call. + */ + event DelegateCall( + address indexed to, + bytes data, + bool success, + bytes response + ); + + /** + * @notice Emitted when a deposit occurs. + * @param to The address where LP tokens are deposited. + * @param amounts The amounts of tokens deposited. + * @param lpAmount The amount of LP tokens minted. + * @param referralCode The referral code used for the deposit. + */ + event Deposit( + address indexed to, + uint256[] amounts, + uint256 lpAmount, + uint256 referralCode + ); + + /** + * @notice Emitted when a deposit callback occurs. + * @param callback The address of the deposit callback contract. + * @param amounts The amounts of tokens deposited. + * @param lpAmount The amount of LP tokens minted. + */ + event DepositCallback( + address indexed callback, + uint256[] amounts, + uint256 lpAmount + ); + + /** + * @notice Emitted when a withdrawal request is made. + * @param from The address of the user making the request. + * @param request The details of the withdrawal request. + */ + event WithdrawalRequested(address indexed from, WithdrawalRequest request); + + /** + * @notice Emitted when a withdrawal request is canceled. + * @param user The address of the user canceling the request. + * @param origin The origin of the cancellation. + */ + event WithdrawalRequestCanceled(address indexed user, address origin); + + /** + * @notice Emitted when an emergency withdrawal occurs. + * @param from The address of the user initiating the emergency withdrawal. + * @param request The details of the withdrawal request. + * @param amounts The actual amounts withdrawn. + */ + event EmergencyWithdrawal( + address indexed from, + WithdrawalRequest request, + uint256[] amounts + ); + + /** + * @notice Emitted when withdrawals are processed. + * @param users The addresses of the users whose withdrawals are processed. + * @param statuses The statuses of the withdrawal processing. + */ + event WithdrawalsProcessed(address[] users, bool[] statuses); + + /** + * @notice Emitted when a withdrawal callback occurs. + * @param callback The address of the withdrawal callback contract. + */ + event WithdrawCallback(address indexed callback); +} diff --git a/contracts/mellow-lrt/interfaces/IVaultConfigurator.sol b/contracts/mellow-lrt/interfaces/IVaultConfigurator.sol new file mode 100644 index 0000000..adc52db --- /dev/null +++ b/contracts/mellow-lrt/interfaces/IVaultConfigurator.sol @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; +import "./IVault.sol"; + +/** + * @title IVaultConfigurator + * @notice Contract defining the configuration and access control for a vault system. + * This interface specifies the parameters for the primary Vault contract, + * facilitating secure configuration updates through a two-stage process: staging and committing, with each parameter update subject to a specified delay. + * The stage function sets the new value and timestamp for the parameter, while the commit function finalizes the update + * + * The delay parameter is expressed in seconds and is defined for each parameter of this contract as follows: + * - baseDelay: the base delay for stage/commit operations + * - depositCallbackDelay: delay for changing the deposit callback contract address + * - withdrawalCallbackDelay: delay for changing the withdrawal callback contract address + * - withdrawalFeeD9Delay: delay for changing the withdrawal fee + * - isDepositLockedDelay: delay for locking deposits + * - areTransfersLockedDelay: delay for locking LP token transfers + * - delegateModuleApprovalDelay: delay for approving delegated modules + * - maximalTotalSupplyDelay: delay for changing the maximum total supply + * - ratiosOracleDelay: delay for changing the ratios oracle address + * - priceOracleDelay: delay for changing the price oracle address + * - validatorDelay: delay for changing the validator address + * - emergencyWithdrawalDelay: delay for withdrawing funds after calling registerWithdrawal + * + * Each of the above parameters has a pair of functions, stage/commit, through which their updates occur. The delay for all these parameters is set to baseDelay. + * + * With the exception of functions for isDepositLocked parameter, all mutable functions of the contract can only be called by the vault's admin. + * Function for isDepositLocked parameter can be called by either the operator or the vault's admin + * to enable faster deposit locking if deemed necessary from the operator/strategy standpoint. + */ +interface IVaultConfigurator { + /// @dev Errors + error AddressZero(); + error InvalidDelay(); + error InvalidTimestamp(); + error InvalidWithdrawalFee(); + error InvalidTotalSupply(); + + /// @notice Struct to represent a staged data change with a delay period. + struct Data { + uint256 value; // Current value + uint256 stagedValue; // Staged value waiting to be committed + uint256 stageTimestamp; // Timestamp of staging + } + + /// @notice Returns the maximum allowed delay for any staged data. + /// @return uint256 The constant `MAX_DELAY` indicating the maximum delay period (365 days). + function MAX_DELAY() external pure returns (uint256); + + /// @notice Returns the maximum withdrawal fee allowed. + /// @return uint256 The constant `MAX_WITHDRAWAL_FEE` indicating the maximum withdrawal fee (5%). + function MAX_WITHDRAWAL_FEE() external pure returns (uint256); + + /// @notice Returns the address of the vault associated with this configurator. + /// @return address of the vault contract. + function vault() external view returns (address); + + ///@notice Stages an approval for the specified delegate module. + /// @param module The address of the module to approve. + function stageDelegateModuleApproval(address module) external; + + /// @notice Commits the previously staged delegate module approval after the delay period. + /// @param module The address of the module to approve. + function commitDelegateModuleApproval(address module) external; + + /// @notice Rolls back any staged delegate module approval. + /// @param module The address of the module to roll back. + function rollbackStagedDelegateModuleApproval(address module) external; + + /// @notice @notice Revokes the approval of the specified delegate module. + /// @param module The address of the module to revoke approval from. + function revokeDelegateModuleApproval(address module) external; + + /// @notice Returns the base delay value for all staging operations. + /// @return uint256 The base delay value in seconds. + function baseDelay() external view returns (uint256); + + /// @notice Checks if the specified delegate module is approved for use. + /// @param module The address of the module to check. + /// @return bool `true` if the module is approved, otherwise `false`. + function isDelegateModuleApproved( + address module + ) external view returns (bool); + + /// @notice Returns whether deposits are currently locked. + /// @notice operator owned parameter. + /// @return bool `true` if deposits are locked, otherwise `false`. + function isDepositLocked() external view returns (bool); + + /// @notice Returns whether LP token transfers are currently locked. + /// @notice admin owned parameter. + /// @return bool `true` if transfers are locked, otherwise `false`. + function areTransfersLocked() external view returns (bool); + + /// @notice Returns the maximum total supply of LP tokens allowed. + /// @return uint256 The maximum total supply of LP tokens. + function maximalTotalSupply() external view returns (uint256); + + /// @notice Returns the address of the deposit callback contract. + /// @return address The address of the deposit callback contract. + function depositCallback() external view returns (address); + + /// @notice Returns the address of the withdrawal callback contract. + /// @return address The address of the withdrawal callback contract. + function withdrawalCallback() external view returns (address); + + /// @notice Returns the current withdrawal fee in D9 format. + /// @return uint256 The withdrawal fee, represented as an integer with 9 decimal places. + function withdrawalFeeD9() external view returns (uint256); + + /// @notice Returns the delay for committing deposit callback changes. + /// @return uint256 The delay in seconds. + function depositCallbackDelay() external view returns (uint256); + + /// @notice Returns the delay for committing withdrawal callback changes. + /// @return uint256 The delay in seconds. + function withdrawalCallbackDelay() external view returns (uint256); + + /// @notice Returns the delay for committing withdrawal fee changes. + /// @return uint256 The delay in seconds. + function withdrawalFeeD9Delay() external view returns (uint256); + + /// @notice Returns the delay for committing deposit locks. + /// @return uint256 The delay in seconds. + function isDepositLockedDelay() external view returns (uint256); + + /// @notice Returns the delay for committing transfers locks. + /// @return uint256 The delay in seconds. + function areTransfersLockedDelay() external view returns (uint256); + + /// @notice Returns the delay for committing delegate module approvals. + /// @return uint256 The delay in seconds. + function delegateModuleApprovalDelay() external view returns (uint256); + + /// @notice Returns the delay for committing maximum total supply changes. + /// @return uint256 The delay in seconds. + function maximalTotalSupplyDelay() external view returns (uint256); + + /// @notice Returns the address of the ratios oracle. + /// @return address The address of the ratios oracle. + function ratiosOracle() external view returns (address); + + /// @notice Returns the address of the price oracle. + /// @return address The address of the price oracle. + function priceOracle() external view returns (address); + + /// @notice Returns the address of the validator. + /// @return address The address of the validator. + function validator() external view returns (address); + + /// @notice Returns the delay for committing validator changes. + /// @return uint256 The delay in seconds. + function validatorDelay() external view returns (uint256); + + /// @notice Returns the delay for committing price oracle changes. + /// @return uint256 The delay in seconds. + function priceOracleDelay() external view returns (uint256); + + /// @notice Returns the delay for committing ratios oracle changes. + /// @return uint256 The delay in seconds. + function ratiosOracleDelay() external view returns (uint256); + + /// @notice Returns the delay required between calling `registerWithdrawal` and being able to perform an emergency withdrawal for that request. + /// @return uint256 The minimum delay time, in seconds, that a user must wait after calling `registerWithdrawal` before executing an emergency withdrawal. + function emergencyWithdrawalDelay() external view returns (uint256); + + /// @notice Stages the deposits lock by setting a staged value and timestamp. + function stageDepositsLock() external; + + /// @notice Commits the previously staged deposits lock after the delay period. + function commitDepositsLock() external; + + /// @notice Rolls back any staged deposits lock. + function rollbackStagedDepositsLock() external; + + /// @notice Revokes the current deposits lock, unlocking deposits. + function revokeDepositsLock() external; + + /// @notice Stages the transfers lock by setting a staged value and timestamp. + /// @param flag The new value to stage. + function stageTransfersLock(bool flag) external; + + /// @notice Commits the previously staged transfers lock after the delay period. + function commitTransfersLock() external; + + /// @notice Rolls back any staged transfers lock. + function rollbackStagedTransfersLock() external; + + /// @notice Stages the maximum total supply with a staged value and timestamp. + /// @param maximalTotalSupply_ The maximum total supply to stage. + function stageMaximalTotalSupply(uint256 maximalTotalSupply_) external; + + /// @notice Commits the previously staged maximum total supply after the delay period. + function commitMaximalTotalSupply() external; + + /// @notice Rolls back any staged maximum total supply changes. + function rollbackStagedMaximalTotalSupply() external; + + /// @notice Stages a new deposit callback address. + /// @param callback The address of the new deposit callback contract. + function stageDepositCallback(address callback) external; + + /// @notice Commits the previously staged deposit callback address after the delay period. + function commitDepositCallback() external; + + /// @notice Rolls back any staged deposit callback changes. + function rollbackStagedDepositCallback() external; + + /// @notice Stages a new withdrawal callback address. + /// @param callback The address of the new withdrawal callback contract. + function stageWithdrawalCallback(address callback) external; + + /// @notice Commits the previously staged withdrawal callback address after the delay period. + function commitWithdrawalCallback() external; + + /// @notice Rolls back any staged withdrawal callback changes. + function rollbackStagedWithdrawalCallback() external; + + /// @notice Stages a new withdrawal fee in D9 format. + /// @param feeD9 The new withdrawal fee in D9 format. + function stageWithdrawalFeeD9(uint256 feeD9) external; + + /// @notice Commits the previously staged withdrawal fee after the delay period. + function commitWithdrawalFeeD9() external; + + /// @notice Rolls back any staged withdrawal fee changes. + function rollbackStagedWithdrawalFeeD9() external; + + /// @notice Stages a base delay value. + /// @param delay_ The base delay value to stage. + function stageBaseDelay(uint256 delay_) external; + + /// @notice Commits the previously staged base delay after the delay period. + function commitBaseDelay() external; + + /// @notice Rolls back any staged base delay changes. + function rollbackStagedBaseDelay() external; + + /// @notice Stages a delay value for the deposit callback. + /// @param delay_ The delay value to stage. + function stageDepositCallbackDelay(uint256 delay_) external; + + /// @notice Commits the previously staged deposit callback delay after the delay period. + function commitDepositCallbackDelay() external; + + /// @notice Rolls back any staged deposit callback delay changes. + function rollbackStagedDepositCallbackDelay() external; + + /// @notice Stages a delay value for the withdrawal callback. + /// @param delay_ The delay value to stage. + function stageWithdrawalCallbackDelay(uint256 delay_) external; + + /// @notice Commits the previously staged withdrawal callback delay after the delay period. + function commitWithdrawalCallbackDelay() external; + + /// @notice Rolls back any staged withdrawal callback delay changes. + function rollbackStagedWithdrawalCallbackDelay() external; + + /// @notice Stages a delay value for the withdrawal fee in D9 format. + /// @param delay_ The delay value to stage. + function stageWithdrawalFeeD9Delay(uint256 delay_) external; + + /// @notice Commits the previously staged withdrawal fee delay after the delay period. + function commitWithdrawalFeeD9Delay() external; + + /// @notice Rolls back any staged withdrawal fee delay changes. + function rollbackStagedWithdrawalFeeD9Delay() external; + + /// @notice Stages a delay value for locking deposits. + /// @param delay_ The delay value to stage. + function stageDepositsLockedDelay(uint256 delay_) external; + + /// @notice Commits the previously staged deposits lock delay after the delay period. + function commitDepositsLockedDelay() external; + + /// @notice Rolls back any staged deposits lock delay changes. + function rollbackStagedDepositsLockedDelay() external; + + /// @notice Stages a delay value for locking transfers. + /// @param delay_ The delay value to stage. + function stageTransfersLockedDelay(uint256 delay_) external; + + /// @notice Commits the previously staged transfers lock delay after the delay period. + function commitTransfersLockedDelay() external; + + /// @notice Rolls back any staged transfers lock delay changes. + function rollbackStagedTransfersLockedDelay() external; + + /// @notice Stages a delay value for the delegate module approval. + /// @param delay_ The delay value to stage. + function stageDelegateModuleApprovalDelay(uint256 delay_) external; + + /// @notice Commits the previously staged delegate module approval delay after the delay period. + function commitDelegateModuleApprovalDelay() external; + + /// @notice Rolls back any staged delegate module approval delay changes. + function rollbackStagedDelegateModuleApprovalDelay() external; + + /// @notice Stages a delay value for the maximum total supply. + /// @param delay_ The delay value to stage. + function stageMaximalTotalSupplyDelay(uint256 delay_) external; + + /// @notice Commits the previously staged maximum total supply delay after the delay period. + function commitMaximalTotalSupplyDelay() external; + + /// @notice Rolls back any staged maximum total supply delay changes. + function rollbackStagedMaximalTotalSupplyDelay() external; + + /// @notice Stages a ratios oracle address. + /// @param oracle The address of the new ratios oracle. + function stageRatiosOracle(address oracle) external; + + /// @notice Commits the previously staged ratios oracle after the delay period. + function commitRatiosOracle() external; + + /// @notice Rolls back any staged ratios oracle changes. + function rollbackStagedRatiosOracle() external; + + /// @notice Stages a price oracle address. + /// @param oracle The address of the new price oracle. + function stagePriceOracle(address oracle) external; + + /// @notice Commits the previously staged price oracle after the delay period. + function commitPriceOracle() external; + + /// @notice Rolls back any staged price oracle changes. + function rollbackStagedPriceOracle() external; + + /// @notice Stages a validator address. + /// @param validator_ The address of the new validator. + function stageValidator(address validator_) external; + + /// @notice Commits the previously staged validator after the delay period. + function commitValidator() external; + + /// @notice Rolls back any staged validator changes. + function rollbackStagedValidator() external; + + /// @notice Stages a delay value for the validator. + /// @param delay_ The delay value to stage. + function stageValidatorDelay(uint256 delay_) external; + + /// @notice Commits the previously staged validator delay after the delay period. + function commitValidatorDelay() external; + + /// @notice Rolls back any staged validator delay changes. + function rollbackStagedValidatorDelay() external; + + /// @notice Stages a delay value for the price oracle. + /// @param delay_ The delay value to stage. + function stagePriceOracleDelay(uint256 delay_) external; + + /// @notice Commits the previously staged price oracle delay after the delay period. + function commitPriceOracleDelay() external; + + /// @notice Rolls back any staged price oracle delay changes. + function rollbackStagedPriceOracleDelay() external; + + /// @notice Stages a delay value for the ratios oracle. + /// @param delay_ The delay value to stage. + function stageRatiosOracleDelay(uint256 delay_) external; + + /// @notice Commits the previously staged ratios oracle delay after the delay period. + function commitRatiosOracleDelay() external; + + /// @notice Rolls back any staged ratios oracle delay changes. + function rollbackStagedRatiosOracleDelay() external; + + /// @notice Stages a delay value for emergency withdrawals. + /// @param delay_ The delay value to stage. + function stageEmergencyWithdrawalDelay(uint256 delay_) external; + + /// @notice Commits the previously staged emergency withdrawal delay. + function commitEmergencyWithdrawalDelay() external; + + /// @notice Rolls back any staged emergency withdrawal delay changes. + function rollbackStagedEmergencyWithdrawalDelay() external; + + /// @dev Emitted when a value is staged for future commitment for given slot. + event Stage( + bytes32 indexed slot, + Data indexed data, + uint256 value, + uint256 timestamp + ); + + /// @dev Emitted when a staged value is committed and updated for given slot. + event Commit(bytes32 indexed slot, Data indexed data, uint256 timestamp); + + /// @dev Emitted when a staged value is rolled back without commitment for given slot. + event Rollback(bytes32 indexed slot, Data indexed data, uint256 timestamp); +} diff --git a/contracts/mellow-lrt/interfaces/external/chainlink/IAggregatorV3.sol b/contracts/mellow-lrt/interfaces/external/chainlink/IAggregatorV3.sol new file mode 100644 index 0000000..2825e02 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/chainlink/IAggregatorV3.sol @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IAggregatorV3 { + function decimals() external view returns (uint8); + + function latestRoundData() + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IDepositContract.sol b/contracts/mellow-lrt/interfaces/external/lido/IDepositContract.sol new file mode 100644 index 0000000..cc93704 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IDepositContract.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IDepositContract { + function get_deposit_root() external view returns (bytes32 rootHash); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IDepositSecurityModule.sol b/contracts/mellow-lrt/interfaces/external/lido/IDepositSecurityModule.sol new file mode 100644 index 0000000..37e599d --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IDepositSecurityModule.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IDepositSecurityModule { + struct Signature { + bytes32 r; + bytes32 vs; + } + + function depositBufferedEther( + uint256 blockNumber, + bytes32 blockHash, + bytes32 depositRoot, + uint256 stakingModuleId, + uint256 nonce, + bytes calldata depositCalldata, + Signature[] calldata sortedGuardianSignatures + ) external; + + function addGuardian(address addr, uint256 newQuorum) external; + + function getOwner() external view returns (address); + + function setMinDepositBlockDistance(uint256 newValue) external; + + function getGuardianIndex(address) external view returns (int256); + + function STAKING_ROUTER() external view returns (address); + + function DEPOSIT_CONTRACT() external view returns (address); + + function getMaxDeposits() external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/ILidoLocator.sol b/contracts/mellow-lrt/interfaces/external/lido/ILidoLocator.sol new file mode 100644 index 0000000..9cf073b --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/ILidoLocator.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface ILidoLocator { + function depositSecurityModule() external view returns (address); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IStakingRouter.sol b/contracts/mellow-lrt/interfaces/external/lido/IStakingRouter.sol new file mode 100644 index 0000000..710e793 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IStakingRouter.sol @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IStakingRouter { + function pauseStakingModule(uint256 _stakingModuleId) external; + function resumeStakingModule(uint256 _stakingModuleId) external; + function getStakingModuleIsDepositPaused( + uint256 _stakingModuleId + ) external view returns (bool); + function getStakingModuleIsActive( + uint256 _stakingModuleId + ) external view returns (bool); + function getStakingModuleNonce( + uint256 _stakingModuleId + ) external view returns (uint256); + function getStakingModuleLastDepositBlock( + uint256 _stakingModuleId + ) external view returns (uint256); + function hasStakingModule( + uint256 _stakingModuleId + ) external view returns (bool); + function getStakingModuleMaxDepositsCount( + uint256 _stakingModuleId, + uint256 depositableEther + ) external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/ISteth.sol b/contracts/mellow-lrt/interfaces/external/lido/ISteth.sol new file mode 100644 index 0000000..9df6c40 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/ISteth.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface ISteth { + function submit(address _referral) external payable returns (uint256); + + function getBufferedEther() external view returns (uint256); + + function DEPOSIT_SIZE() external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IWSteth.sol b/contracts/mellow-lrt/interfaces/external/lido/IWSteth.sol new file mode 100644 index 0000000..feee95d --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IWSteth.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IWSteth { + function wrap(uint256 stethAmount) external payable returns (uint256); + + function unwrap(uint256 wstethAmount) external returns (uint256); + + function getStETHByWstETH( + uint256 wstethAmount + ) external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IWeth.sol b/contracts/mellow-lrt/interfaces/external/lido/IWeth.sol new file mode 100644 index 0000000..e699f56 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IWeth.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +interface IWeth { + function deposit() external payable; + function withdraw(uint256 amount) external; +} diff --git a/contracts/mellow-lrt/interfaces/external/lido/IWithdrawalQueue.sol b/contracts/mellow-lrt/interfaces/external/lido/IWithdrawalQueue.sol new file mode 100644 index 0000000..146b29a --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/lido/IWithdrawalQueue.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +interface IWithdrawalQueue { + function unfinalizedStETH() external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/symbiotic/IBond.sol b/contracts/mellow-lrt/interfaces/external/symbiotic/IBond.sol new file mode 100644 index 0000000..0f6b161 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/symbiotic/IBond.sol @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +interface IBond is IERC20 { + /** + * @notice Emitted when debt is issued. + * @param issuer address of the debt's issuer + * @param recipient address that should receive the underlying asset + * @param debtIssued amount of the debt issued + */ + event IssueDebt( + address indexed issuer, + address indexed recipient, + uint256 debtIssued + ); + + /** + * @notice Emitted when debt is repaid. + * @param issuer address of the debt's issuer + * @param recipient address that received the underlying asset + * @param debtRepaid amount of the debt repaid + */ + event RepayDebt( + address indexed issuer, + address indexed recipient, + uint256 debtRepaid + ); + + /** + * @notice Get the bond's underlying asset. + * @return asset address of the underlying asset + */ + function asset() external view returns (address); + + /** + * @notice Get a total amount of repaid debt. + * @return total repaid debt + */ + function totalRepaidDebt() external view returns (uint256); + + /** + * @notice Get an amount of repaid debt created by a particular issuer. + * @param issuer address of the debt's issuer + * @return particular issuer's repaid debt + */ + function issuerRepaidDebt(address issuer) external view returns (uint256); + + /** + * @notice Get an amount of repaid debt to a particular recipient. + * @param recipient address that received the underlying asset + * @return particular recipient's repaid debt + */ + function recipientRepaidDebt( + address recipient + ) external view returns (uint256); + + /** + * @notice Get an amount of repaid debt for a particular issuer-recipient pair. + * @param issuer address of the debt's issuer + * @param recipient address that received the underlying asset + * @return particular pair's repaid debt + */ + function repaidDebt( + address issuer, + address recipient + ) external view returns (uint256); + + /** + * @notice Get a total amount of debt. + * @return total debt + */ + function totalDebt() external view returns (uint256); + + /** + * @notice Get a current debt created by a particular issuer. + * @param issuer address of the debt's issuer + * @return particular issuer's debt + */ + function issuerDebt(address issuer) external view returns (uint256); + + /** + * @notice Get a current debt to a particular recipient. + * @param recipient address that should receive the underlying asset + * @return particular recipient's debt + */ + function recipientDebt(address recipient) external view returns (uint256); + + /** + * @notice Get a current debt for a particular issuer-recipient pair. + * @param issuer address of the debt's issuer + * @param recipient address that should receive the underlying asset + * @return particular pair's debt + */ + function debt( + address issuer, + address recipient + ) external view returns (uint256); + + /** + * @notice Burn a given amount of the bond, and increase a debt of the underlying asset for the caller. + * @param recipient address that should receive the underlying asset + * @param amount amount of the bond + */ + function issueDebt(address recipient, uint256 amount) external; +} diff --git a/contracts/mellow-lrt/interfaces/external/symbiotic/IDefaultBond.sol b/contracts/mellow-lrt/interfaces/external/symbiotic/IDefaultBond.sol new file mode 100644 index 0000000..738ec49 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/symbiotic/IDefaultBond.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IBond} from "./IBond.sol"; + +interface IDefaultBond is IBond { + error InsufficientDeposit(); + error InsufficientWithdraw(); + error InsufficientIssueDebt(); + + /** + * @notice Deposit a given amount of the underlying asset, and mint the bond to a particular recipient. + * @param recipient address of the bond's recipient + * @param amount amount of the underlying asset + * @return amount of the bond minted + */ + function deposit( + address recipient, + uint256 amount + ) external returns (uint256); + + /** + * @notice Deposit a given amount of the underlying asset using a permit functionality, and mint the bond to a particular recipient. + * @param recipient address of the bond's recipient + * @param amount amount of the underlying asset + * @param deadline timestamp of the signature's deadline + * @param v v component of the signature + * @param r r component of the signature + * @param s s component of the signature + * @return amount of the bond minted + */ + function deposit( + address recipient, + uint256 amount, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256); + + /** + * @notice Withdraw a given amount of the underlying asset, and transfer it to a particular recipient. + * @param recipient address of the underlying asset's recipient + * @param amount amount of the underlying asset + */ + function withdraw(address recipient, uint256 amount) external; + + function limit() external view returns (uint256); +} diff --git a/contracts/mellow-lrt/interfaces/external/uniswap/ISwapRouter.sol b/contracts/mellow-lrt/interfaces/external/uniswap/ISwapRouter.sol new file mode 100644 index 0000000..d7bb7b9 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/external/uniswap/ISwapRouter.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity ^0.8.20; + +/// @title Router token swapping functionality +/// @notice Functions for swapping tokens via Uniswap V3 +interface ISwapRouter { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another token + /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + /// @return amountOut The amount of the received token + function exactInputSingle( + ExactInputSingleParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + /// @return amountOut The amount of the received token + function exactInput( + ExactInputParams calldata params + ) external payable returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another token + /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + /// @return amountIn The amount of the input token + function exactOutputSingle( + ExactOutputSingleParams calldata params + ) external payable returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + /// @return amountIn The amount of the input token + function exactOutput( + ExactOutputParams calldata params + ) external payable returns (uint256 amountIn); +} diff --git a/contracts/mellow-lrt/interfaces/modules/IDefaultModule.sol b/contracts/mellow-lrt/interfaces/modules/IDefaultModule.sol new file mode 100644 index 0000000..f7a5fab --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/IDefaultModule.sol @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: BSL-1.1 + +// Solidity version pragma +pragma solidity ^0.8.20; + +/** + * @title IDefaultModule + * @notice Interface defining methods and errors for the DefaultModule contract. + */ +interface IDefaultModule { + /// @dev Error indicating that an operation is not allowed + error Forbidden(); +} diff --git a/contracts/mellow-lrt/interfaces/modules/ITvlModule.sol b/contracts/mellow-lrt/interfaces/modules/ITvlModule.sol new file mode 100644 index 0000000..ec85a76 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/ITvlModule.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +/** + * @title ITvlModule + * @notice Interface for a Total Value Locked (TVL) module, providing information about token balances. + */ +interface ITvlModule { + // Structure representing TVL data for a token + struct Data { + address token; // Address of the token + address underlyingToken; // Address of the underlying token + uint256 amount; // Current amount of the token + uint256 underlyingAmount; // Current amount of the underlying token + bool isDebt; // Flag indicating if the token represents debt + } + + /** + * @notice Returns Total Value Locked (TVL) data for a specific user. + * @param user The address of the user. + * @return data An array of TVL data for each token held by the user. + */ + function tvl(address user) external view returns (Data[] memory data); +} diff --git a/contracts/mellow-lrt/interfaces/modules/erc20/IERC20SwapModule.sol b/contracts/mellow-lrt/interfaces/modules/erc20/IERC20SwapModule.sol new file mode 100644 index 0000000..d1a319f --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/erc20/IERC20SwapModule.sol @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +/** + * @title IERC20SwapModule + * @notice Interface defining methods for an ERC20 Swap Module. + */ +interface IERC20SwapModule { + /// @dev Errors + error Deadline(); + error SwapFailed(); + error InvalidSwapAmounts(); + + /** + * @notice Struct defining parameters for a token swap. + */ + struct SwapParams { + address tokenIn; // Address of the input token + address tokenOut; // Address of the output token + uint256 amountIn; // Amount of input token to swap + uint256 minAmountOut; // Minimum amount of output token expected + uint256 deadline; // Swap deadline timestamp + } + + /** + * @notice Executes a token swap. + * @param params Parameters defining the swap. + * @param to Address of the target contract to execute the swap on. + * @param data Data to be passed to the target contract for the swap. + * @return Result of the swap execution. + */ + function swap( + SwapParams calldata params, + address to, + bytes calldata data + ) external returns (bytes memory); + + /** + * @notice Emitted when a token swap occurs. + * @param params Swap parameters including tokenIn, tokenOut, amountIn, minAmountOut, and deadline. + * @param to The address receiving the swapped tokens. + * @param data Additional data related to the swap. + * @param timestamp Timestamp of the swap. + * @param response Response data from the swap operation. + */ + event ERC20SwapModuleSwap( + SwapParams params, + address to, + bytes data, + uint256 timestamp, + bytes response + ); +} diff --git a/contracts/mellow-lrt/interfaces/modules/erc20/IERC20TvlModule.sol b/contracts/mellow-lrt/interfaces/modules/erc20/IERC20TvlModule.sol new file mode 100644 index 0000000..3f288cb --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/erc20/IERC20TvlModule.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import "../ITvlModule.sol"; +import "../../IVault.sol"; + +/** + * @title IManagedTvlModule + * @notice Interface defining methods for a managed Total Value Locked (TVL) Module, + * allowing setting and retrieving parameters for specific vaults. + */ +interface IERC20TvlModule is ITvlModule { + /** + * @notice Calculates the Total Value Locked (TVL) of a vault holding ERC20 tokens. + * @param vault The address of the vault for which to calculate the TVL. + * @return data An array of TVL data for each underlying token held by the vault. + * @dev The TVL data includes information such as token address, underlying token address, + * token balance, and underlying token balance. + * This function should be implemented to accurately calculate the TVL of the vault. + * It should not be callable via delegate calls. + */ + function tvl(address vault) external view returns (Data[] memory data); +} diff --git a/contracts/mellow-lrt/interfaces/modules/erc20/IManagedTvlModule.sol b/contracts/mellow-lrt/interfaces/modules/erc20/IManagedTvlModule.sol new file mode 100644 index 0000000..fc56855 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/erc20/IManagedTvlModule.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "../ITvlModule.sol"; +import "../../utils/IDefaultAccessControl.sol"; + +import "../../IVault.sol"; + +// Interface declaration for a Managed Total Value Locked (TVL) Module +interface IManagedTvlModule is ITvlModule { + error InvalidToken(); + + /** + * @notice Retrieves the parameters set for a specific vault. + * @param vault The address of the vault for which to retrieve parameters. + * @return Parameters set for the specified vault. + * @dev This function returns the parameters set for the specified vault. + * It should not modify state variables and should be view-only. + */ + function vaultParams(address vault) external view returns (bytes memory); + + /** + * @notice Sets parameters for a specific vault. + * @param vault The address of the vault for which to set parameters. + * @param data An array of TVL data to be set as parameters for the vault. + * @dev This function sets the parameters for the specified vault. + * It should only be callable by authorized contracts. + */ + function setParams(address vault, Data[] memory data) external; + + /** + * @notice Emitted when parameters are set for a specific vault. + * @param vault The address of the vault for which parameters are set. + * @param data An array of TVL data representing the parameters set for the vault. + * @param timestamp Timestamp of the parameter setting. + */ + event ManagedTvlModuleSetParams( + address indexed vault, + Data[] data, + uint256 timestamp + ); +} diff --git a/contracts/mellow-lrt/interfaces/modules/obol/IStakingModule.sol b/contracts/mellow-lrt/interfaces/modules/obol/IStakingModule.sol new file mode 100644 index 0000000..c3c362f --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/obol/IStakingModule.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/utils/math/Math.sol"; + +import "../../external/lido/IWeth.sol"; +import "../../external/lido/ISteth.sol"; +import "../../external/lido/IWSteth.sol"; +import "../../external/lido/IWithdrawalQueue.sol"; +import "../../external/lido/IDepositSecurityModule.sol"; +import "../../external/lido/ILidoLocator.sol"; +import "../../external/lido/IStakingRouter.sol"; +import "../../external/lido/IDepositContract.sol"; + +/** + * @title IStakingModule + * @notice Staking module handles the secure conversion and deposit of assets into staking mechanisms. + * + * @dev Implements functions to convert and safely deposit assets into the Lido staking modules while adhering + * to security checks and balances provided by a committee of guardians within the Deposit Security Module. + */ +interface IStakingModule { + /// @dev Custom errors: + error NotEnoughWeth(); + error InvalidWithdrawalQueueState(); + error InvalidAmount(); + error InvalidDepositRoot(); + + /** + * @return Address of the WETH token. + */ + function weth() external view returns (address); + + /** + * @return Address of the stETH token. + */ + function steth() external view returns (address); + + /** + * @return Address of the wstETH token. + */ + function wsteth() external view returns (address); + + /** + * @dev LidoLocator is the universal address book for the Lido protocol. + * @return Address of the Lido Locator module.` + */ + function lidoLocator() external view returns (ILidoLocator); + + /** + * @notice Interface to the Withdrawal Queue module. + * @return The withdrawal queue address. + */ + function withdrawalQueue() external view returns (IWithdrawalQueue); + + /** + * @notice The unique identifier for this staking module. + * @return An immutable identifier used to differentiate this module within a system of multiple modules. + */ + function stakingModuleId() external view returns (uint256); + + /** + * @notice Converts a specified amount of wETH directly to wstETH, ensuring all security protocols are adhered to. + * @param amount The amount of wETH to convert. + */ + function convert(uint256 amount) external; + + /** + * @notice Converts wETH to wstETH and securely deposits it into the staking contract according to the specified security protocols. + * @param blockNumber The block number at the time of the deposit operation, used for security verification. + * @param blockHash The hash of the block at the time of the deposit, used for security verification. + * @param depositRoot The merkle root of the deposit records, used for security verification. + * @param nonce A nonce to ensure uniqueness of the deposit operation. + * @param depositCalldata Additional calldata required for the deposit operation. + * @param sortedGuardianSignatures Signatures from guardians to authenticate and secure the deposit. + * Signatures must be sorted in ascending order by address of the guardian. Each signature must + * be produced for the keccak256 hash of the following message (each component taking 32 bytes): + * + * | ATTEST_MESSAGE_PREFIX | blockNumber | blockHash | depositRoot | stakingModuleId | nonce | + */ + function convertAndDeposit( + uint256 blockNumber, + bytes32 blockHash, + bytes32 depositRoot, + uint256 nonce, + bytes calldata depositCalldata, + IDepositSecurityModule.Signature[] calldata sortedGuardianSignatures + ) external; + + /** + * @notice Emitted when wETH is converted to wstETH. + * @param amount The amount of wETH that was converted. + */ + event Converted(uint256 amount); + + /** + * @notice Emitted after the assets are securely deposited. + * @param amount The amount of assets that were deposited. + * @param blockNumber The block number at which the deposit was recorded. + */ + event DepositCompleted(uint256 amount, uint256 blockNumber); +} diff --git a/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondModule.sol b/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondModule.sol new file mode 100644 index 0000000..d867401 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondModule.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/utils/math/Math.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "../../external/symbiotic/IDefaultBond.sol"; + +// Interface declaration for Default Bond Module +interface IDefaultBondModule { + /** + * @notice Deposits a specified amount of tokens into a bond contract. + * @param bond Address of the bond contract. + * @param amount Amount of tokens to deposit. + * @return The amount of tokens deposited. + */ + function deposit(address bond, uint256 amount) external returns (uint256); + + /** + * @notice Withdraws a specified amount of tokens from a bond contract. + * @param bond Address of the bond contract. + * @param amount Amount of tokens to withdraw. + * @return The amount of tokens withdrawn. + */ + function withdraw(address bond, uint256 amount) external returns (uint256); + + /** + * @notice Emitted when tokens are deposited into a bond. + * @param bond The address of the bond contract. + * @param amount The amount of tokens deposited. + * @param timestamp Timestamp of the deposit. + */ + event DefaultBondModuleDeposit( + address indexed bond, + uint256 amount, + uint256 timestamp + ); + + /** + * @notice Emitted when tokens are withdrawn from a bond. + * @param bond The address of the bond contract. + * @param amount The amount of tokens withdrawn. + */ + event DefaultBondModuleWithdraw(address indexed bond, uint256 amount); +} diff --git a/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondTvlModule.sol b/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondTvlModule.sol new file mode 100644 index 0000000..82c3a12 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/modules/symbiotic/IDefaultBondTvlModule.sol @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; + +import "../../external/symbiotic/IBond.sol"; +import "../../utils/IDefaultAccessControl.sol"; + +import "../ITvlModule.sol"; + +import "../../IVault.sol"; + +// Interface declaration for Default Bond TVL Module +interface IDefaultBondTvlModule is ITvlModule { + error InvalidToken(); + + /** + * @notice Retrieves the bond parameters set for a specific vault. + * @param vault The address of the vault for which to retrieve bond parameters. + * @return Bond parameters set for the specified vault. + * @dev This function returns the bond parameters set for the specified vault. + * It should not modify state variables and should be view-only. + */ + function vaultParams(address vault) external view returns (bytes memory); + + /** + * @notice Sets bond parameters for a specific vault. + * @param vault The address of the vault for which to set bond parameters. + * @param bonds An array of bond addresses to be set as parameters for the vault. + * @dev This function sets the bond parameters for the specified vault. + * It should only be callable by authorized contracts. + */ + function setParams(address vault, address[] memory bonds) external; + + /** + * @notice Emitted when parameters are set for a specific vault in the Default Bond TVL Module. + * @param vault The address of the vault for which parameters are set. + * @param bonds An array of bond addresses representing the parameters set for the vault. + */ + event DefaultBondTvlModuleSetParams(address indexed vault, address[] bonds); +} diff --git a/contracts/mellow-lrt/interfaces/oracles/IChainlinkOracle.sol b/contracts/mellow-lrt/interfaces/oracles/IChainlinkOracle.sol new file mode 100644 index 0000000..e951626 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/oracles/IChainlinkOracle.sol @@ -0,0 +1,113 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; + +import "../utils/IDefaultAccessControl.sol"; +import "../external/chainlink/IAggregatorV3.sol"; + +import "./IPriceOracle.sol"; + +/** + * @title IChainlinkOracle + * @notice Interface defining a price oracle that uses Chainlink data. + */ +interface IChainlinkOracle is IPriceOracle { + /// @dev Errors + error AddressZero(); + error InvalidLength(); + error Forbidden(); + error StaleOracle(); + error InvalidOracleData(); + + /// @notice Struct containing Chainlink oracle data. + /// @param aggregatorV3 The address of the Chainlink aggregator. + /// @param maxAge The maximum allowable age for an oracle result before it's considered stale. + struct AggregatorData { + address aggregatorV3; + uint256 maxAge; + } + + /** + * @notice Returns the constant Q96 used for ratio calculations with 96-bit precision. + * @return uint256 The value of Q96 (2^96) for ratio calculations. + */ + function Q96() external view returns (uint256); + + /** + * @notice Returns the Chainlink price aggregator address for a specific vault and token. + * @param vault The address of the vault. + * @param token The address of the token. + * @return data The Chainlink oracle data for the token. + */ + function aggregatorsData( + address vault, + address token + ) external view returns (AggregatorData memory data); + + /** + * @notice Returns the base token associated with a specific vault. + * @param vault The address of the vault. + * @return address of the base token. + */ + function baseTokens(address vault) external view returns (address); + + /** + * @notice Sets the base token for a specific vault. + * @param vault The address of the vault to set the base token for. + * @param baseToken The address of the base token to associate with the vault. + */ + function setBaseToken(address vault, address baseToken) external; + + /** + * @notice Sets Chainlink price oracles for a given vault and an array of tokens. + * @param vault The address of the vault to associate the tokens and oracles with. + * @param tokens An array of token addresses that require price data. + * @param aggregatorsData An array of Chainlink oracle addresses set with max allowed ages for the tokens. + * @dev Both arrays should have the same length. + */ + function setChainlinkOracles( + address vault, + address[] memory tokens, + AggregatorData[] memory aggregatorsData + ) external; + + /** + * @notice Retrieves the latest price for a specific token from a given vault's associated Chainlink oracle. + * @param vault The address of the vault requesting the price. + * @param token The address of the token to get the price for. + * @return answer The latest price of the token. + * @return decimals The number of decimals used by the Chainlink oracle for this price. + * @dev Reverts with `StaleOracle` if the price data is too old. + */ + function getPrice( + address vault, + address token + ) external view returns (uint256 answer, uint8 decimals); + + /** + * @notice Emitted when the base token is set for a specific vault in the Chainlink Oracle. + * @param vault The address of the vault for which the base token is set. + * @param baseToken The address of the base token set for the vault. + * @param timestamp The timestamp when the base token is set. + */ + event ChainlinkOracleSetBaseToken( + address indexed vault, + address baseToken, + uint256 timestamp + ); + + /** + * @notice Emitted when Chainlink oracles are set for a specific vault in the Chainlink Oracle. + * @param vault The address of the vault for which Chainlink oracles are set. + * @param tokens An array of token addresses for which Chainlink oracles are set. + * @param aggregatorsData An array of Chainlink oracle addresses set with max allowed ages for the tokens. + * @param timestamp The timestamp when Chainlink oracles are set. + */ + event ChainlinkOracleSetChainlinkOracles( + address indexed vault, + address[] tokens, + AggregatorData[] aggregatorsData, + uint256 timestamp + ); +} diff --git a/contracts/mellow-lrt/interfaces/oracles/IManagedRatiosOracle.sol b/contracts/mellow-lrt/interfaces/oracles/IManagedRatiosOracle.sol new file mode 100644 index 0000000..d5b4ee4 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/oracles/IManagedRatiosOracle.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "./IRatiosOracle.sol"; +import "../IVault.sol"; + +import "../utils/IDefaultAccessControl.sol"; + +/** + * @title IManagedRatiosOracle + * @notice Interface defining a managed ratios oracle, enabling ratio updates and data retrieval. + */ +interface IManagedRatiosOracle is IRatiosOracle { + /// @dev Errors + error Forbidden(); + error InvalidCumulativeRatio(); + error InvalidLength(); + error InvalidToken(); + + /** + * @notice Structure representing the data for target ratios associated with a vault's tokens. + * @param tokensHash The hash of the vault's tokens, used to validate the token data. + * @param ratiosX96 An array representing the target ratios of each token using 96-bit precision. + */ + struct Data { + bytes32 tokensHash; + uint128[] ratiosX96; + } + + /** + * @notice Returns the constant Q96 used for ratio calculations with 96-bit precision. + * @return uint256 The value of Q96 (2^96) for ratio calculations. + */ + function Q96() external view returns (uint256); + + /** + * @notice Updates the target ratios for a specific vault. + * @param vault The address of the vault to update the ratios for. + * @param isDeposit A boolean indicating whether the ratios are for a deposit or a withdrawal. + * @param ratiosX96 An array of target ratios for the vault's underlying tokens. + * @dev The cumulative ratio must be exactly `Q96`. + */ + function updateRatios( + address vault, + bool isDeposit, + uint128[] memory ratiosX96 + ) external; + + /** + * @notice Returns the encoded ratio data associated with a specific vault address. + * @param vault The address of the vault to retrieve the data for. + * @param isDeposit A boolean indicating whether the ratios are for a deposit or a withdrawal. + * @return bytes The encoded ratio data. + */ + function vaultToData( + address vault, + bool isDeposit + ) external view returns (bytes memory); + + /** + * @notice Emitted when ratios are updated for a specific vault in the Managed Ratios Oracle. + * @param vault The address of the vault for which ratios are updated. + * @param isDeposit A boolean indicating whether the ratios are for a deposit or a withdrawal. + * @param ratiosX96 An array of updated ratios expressed in 96-bit precision. + */ + event ManagedRatiosOracleUpdateRatios( + address indexed vault, + bool isDeposit, + uint128[] ratiosX96 + ); +} diff --git a/contracts/mellow-lrt/interfaces/oracles/IPriceOracle.sol b/contracts/mellow-lrt/interfaces/oracles/IPriceOracle.sol new file mode 100644 index 0000000..7791846 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/oracles/IPriceOracle.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +/** + * @title IPriceOracle + * @notice Interface defining a standard price oracle that provides token prices in 96-bit precision. + */ +interface IPriceOracle { + /** + * @notice Returns the price of a specific token relative to the base token of the given vault, expressed in 96-bit precision. + * @param vault The address of the vault requesting the price. + * @param token The address of the token to calculate the price for. + * @return priceX96_ The price of the token relative to the base token, using 96-bit precision. + * @dev Implementations should ensure prices are accurate and may involve external oracle data. + * Reverts with an appropriate error if the price cannot be provided. + */ + function priceX96( + address vault, + address token + ) external view returns (uint256 priceX96_); +} diff --git a/contracts/mellow-lrt/interfaces/oracles/IRatiosOracle.sol b/contracts/mellow-lrt/interfaces/oracles/IRatiosOracle.sol new file mode 100644 index 0000000..e54aae4 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/oracles/IRatiosOracle.sol @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +/** + * @title IRatiosOracle + * @notice Interface for a ratios oracle, providing the target allocation ratios for a vault. + */ +interface IRatiosOracle { + /** + * @notice Retrieves the target allocation ratios (using 96-bit precision) for a specific vault's tokens. + * @param vault The address of the vault requesting the ratios. + * @param isDeposit A boolean indicating whether the ratios are for a deposit or a withdrawal. + * @return ratiosX96 An array representing the target ratios for each token, expressed in 96-bit precision. + * @dev The array of ratios should align with the underlying tokens associated with the vault. + * Reverts if the ratios cannot be provided due to missing or mismatched data. + */ + function getTargetRatiosX96( + address vault, + bool isDeposit + ) external view returns (uint128[] memory ratiosX96); +} diff --git a/contracts/mellow-lrt/interfaces/security/IAdminProxy.sol b/contracts/mellow-lrt/interfaces/security/IAdminProxy.sol new file mode 100644 index 0000000..e5e88cd --- /dev/null +++ b/contracts/mellow-lrt/interfaces/security/IAdminProxy.sol @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +/** + * @title IAdminProxy Interface + * @dev This interface defines the structure and functions of the AdminProxy contract. + * It includes error definitions, struct definitions, function signatures, and event declarations. + */ +interface IAdminProxy { + /** + * @dev Custom error to indicate forbidden actions. + */ + error Forbidden(); + + /** + * @dev Structure to represent a proposal. + * @param implementation The address of the implementation contract. + * @param callData The calldata to be sent to the implementation contract when the proposal is accepted. + */ + struct Proposal { + address implementation; + bytes callData; + } + + /** + * @dev Returns the address of the proxy contract. + * @return The proxy contract address. + */ + function proxy() external view returns (ITransparentUpgradeableProxy); + + /** + * @dev Returns the address of the proxy admin contract. + * @return The proxy admin contract address. + */ + function proxyAdmin() external view returns (ProxyAdmin); + + /** + * @dev Returns the current base implementation. + * @return The current base implementation proposal. + */ + function baseImplementation() external view returns (Proposal memory); + + /** + * @dev Returns the currently proposed base implementation. + * @return The proposed base implementation proposal. + */ + function proposedBaseImplementation() + external + view + returns (Proposal memory); + + /** + * @dev Returns the address of the current proposer. + * @return The proposer address. + */ + function proposer() external view returns (address); + + /** + * @dev Returns the address of the current acceptor. + * @return The acceptor address. + */ + function acceptor() external view returns (address); + + /** + * @dev Returns the address of the emergency operator. + * @return The emergency operator address. + */ + function emergencyOperator() external view returns (address); + + /** + * @dev Returns the proposal at the specified index. + * @param index The index of the proposal to retrieve. + * @return The proposal at the given index. + */ + function proposalAt(uint256 index) external view returns (Proposal memory); + + /** + * @dev Returns the total number of proposals. + * @return The number of proposals. + */ + function proposalsCount() external view returns (uint256); + + /** + * @dev Returns the latest accepted nonce. + * @return The latest accepted nonce. + */ + function latestAcceptedNonce() external view returns (uint256); + + /** + * @dev Function to upgrade the proposer to a new address. + * This can only be called by the acceptor. + * @param newProposer The address of the new proposer. + */ + function upgradeProposer(address newProposer) external; + + /** + * @dev Function to upgrade the acceptor to a new address. + * This can only be called by the current acceptor. + * @param newAcceptor The address of the new acceptor. + */ + function upgradeAcceptor(address newAcceptor) external; + + /** + * @dev Function to upgrade the emergency operator to a new address. + * This can only be called by the acceptor. + * @param newEmergencyOperator The address of the new emergency operator. + */ + function upgradeEmergencyOperator(address newEmergencyOperator) external; + + /** + * @dev Function to propose a new implementation. + * This can be called by the proposer or the acceptor. + * @param implementation The address of the proposed implementation contract. + * @param callData The calldata to be sent to the proposed implementation contract. + */ + function propose(address implementation, bytes calldata callData) external; + + /** + * @dev Function to propose a new base implementation. + * This can be called by the proposer or the acceptor. + * @param implementation The address of the proposed base implementation contract. + * @param callData The calldata to be sent to the proposed base implementation contract. + */ + function proposeBaseImplementation( + address implementation, + bytes calldata callData + ) external; + + /** + * @dev Function to accept the proposed base implementation. + * This can only be called by the acceptor. + * The proposed base implementation is set as the new base implementation. + */ + function acceptBaseImplementation() external; + + /** + * @dev Function to accept a proposal at the specified index. + * This can only be called by the acceptor. + * The proxy is upgraded to the implementation specified in the accepted proposal. + * @param index The index of the proposal to accept. + */ + function acceptProposal(uint256 index) external; + + /** + * @dev Function to reject all proposals. + * This can only be called by the acceptor. + * All existing proposals are rejected by setting the latest accepted nonce to the length of the proposals array. + */ + function rejectAllProposals() external; + + /** + * @dev Function to reset to the base implementation. + * This can only be called by the emergency operator. + * The proxy is reset to the base implementation, and the emergency operator address is cleared. + */ + function resetToBaseImplementation() external; + + /** + * @dev Emitted when the emergency operator is upgraded. + * @param newEmergencyOperator The address of the new emergency operator. + * @param origin The address that initiated the upgrade. + */ + event EmergencyOperatorUpgraded( + address newEmergencyOperator, + address origin + ); + + /** + * @dev Emitted when the proposer is upgraded. + * @param newProposer The address of the new proposer. + * @param origin The address that initiated the upgrade. + */ + event ProposerUpgraded(address newProposer, address origin); + + /** + * @dev Emitted when the acceptor is upgraded. + * @param newAcceptor The address of the new acceptor. + * @param origin The address that initiated the upgrade. + */ + event AcceptorUpgraded(address newAcceptor, address origin); + + /** + * @dev Emitted when a proposal is accepted. + * @param index The index of the accepted proposal. + * @param origin The address that accepted the proposal. + */ + event ProposalAccepted(uint256 index, address origin); + + /** + * @dev Emitted when all proposals are rejected. + * @param latestAcceptedNonce The nonce of the latest accepted proposal. + * @param origin The address that rejected all proposals. + */ + event AllProposalsRejected(uint256 latestAcceptedNonce, address origin); + + /** + * @dev Emitted when the proxy is reset to the base implementation. + * @param implementation The base implementation to which the proxy was reset. + * @param origin The address that initiated the reset. + */ + event ResetToBaseImplementation(Proposal implementation, address origin); + + /** + * @dev Emitted when a new implementation is proposed. + * @param implementation The address of the proposed implementation. + * @param callData The calldata for the proposed implementation. + * @param origin The address that proposed the implementation. + */ + event ImplementationProposed( + address implementation, + bytes callData, + address origin + ); + + /** + * @dev Emitted when a new base implementation is proposed. + * @param implementation The proposed base implementation. + * @param origin The address that proposed the base implementation. + */ + event BaseImplementationProposed(Proposal implementation, address origin); + + /** + * @dev Emitted when the proposed base implementation is accepted. + * @param implementation The accepted base implementation. + * @param origin The address that accepted the base implementation. + */ + event BaseImplementationAccepted(Proposal implementation, address origin); +} diff --git a/contracts/mellow-lrt/interfaces/strategies/IDefaultBondStrategy.sol b/contracts/mellow-lrt/interfaces/strategies/IDefaultBondStrategy.sol new file mode 100644 index 0000000..88a4a08 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/strategies/IDefaultBondStrategy.sol @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "../utils/IDepositCallback.sol"; + +import "../modules/erc20/IERC20TvlModule.sol"; +import "../modules/symbiotic/IDefaultBondModule.sol"; + +/** + * @title IDefaultBondStrategy + * @notice Interface defining the functions for managing bond strategies with deposits and withdrawals. + * The contract of the basic defaultBond strategy, the only operations of which are deposits into various bonds in a specified proportion + * and processing of user withdrawals. Note that this strategy allows users to make instant withdrawals when calling processWithdrawals([msg.sender]). + */ +interface IDefaultBondStrategy is IDepositCallback { + /// @dev Errors + error InvalidCumulativeRatio(); + error InvalidBond(); + + /** + * @notice Structure representing data for a specific bond allocation. + * @param bond The address of the bond to allocate funds to. + * @param ratioX96 The proportion of funds to allocate to this bond, using a 96-bit precision ratio. + */ + struct Data { + address bond; + uint256 ratioX96; + } + + /** + * @notice Returns the constant Q96, which is used as a multiplier for ratio calculations. + * @return The value of Q96 (2^96) for ratio calculations. + */ + function Q96() external view returns (uint256); + + /** + * @notice Returns the vault associated with this strategy. + * @return The address of the vault. + */ + function vault() external view returns (IVault); + + /** + * @notice Returns the ERC20 TVL module used for calculating token values. + * @return IERC20TvlModule The address of the ERC20 TVL module. + */ + function erc20TvlModule() external view returns (IERC20TvlModule); + + /** + * @notice Returns the bond module used for managing bond transactions. + * @return IDefaultBondModule The address of the bond module. + */ + function bondModule() external view returns (IDefaultBondModule); + + /** + * @notice Returns the bond data associated with a specific token address. + * @param token The address of the token. + * @return bytes The bond data encoded as bytes. + */ + function tokenToData(address token) external view returns (bytes memory); + + /** + * @notice Sets the bond allocation data for a specific token. + * @param token The address of the token to associate with bond data. + * @param data An array of `Data` structures representing the bond allocation details. + * @dev The cumulative ratio of all bond allocations should sum up to `Q96`. + */ + function setData(address token, Data[] memory data) external; + + /** + * @notice Processes all pending withdrawals for all users. + * Withdraws from bonds and processes through the vault. + */ + function processAll() external; + + /** + * @notice Processes withdrawals for a specific list of users. + * Withdraws from bonds and processes through the vault. + * @param users An array of user addresses to process withdrawals for. + */ + function processWithdrawals(address[] memory users) external; + + /** + * @notice Emitted when bond allocation data is set for a specific token in the Default Bond Strategy. + * @param token The address of the token for which bond allocation data is set. + * @param data An array of `Data` structures representing the bond allocation details. + * @param timestamp The timestamp when the bond allocation data is set. + */ + event DefaultBondStrategySetData( + address indexed token, + IDefaultBondStrategy.Data[] data, + uint256 timestamp + ); + + /** + * @notice Emitted when withdrawals are processed for a list of users in the Default Bond Strategy. + * @param users An array of user addresses for whom withdrawals are processed. + * @param timestamp The timestamp when the withdrawals are processed. + */ + event DefaultBondStrategyProcessWithdrawals( + address[] users, + uint256 timestamp + ); +} diff --git a/contracts/mellow-lrt/interfaces/strategies/ISimpleDVTStakingStrategy.sol b/contracts/mellow-lrt/interfaces/strategies/ISimpleDVTStakingStrategy.sol new file mode 100644 index 0000000..b896882 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/strategies/ISimpleDVTStakingStrategy.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "../modules/obol/IStakingModule.sol"; +import "../IVault.sol"; + +/** + * @title ISimpleDVTStakingStrategy + * @dev The strategy uses the staking module for converting tokens and the vault for managing the deposits and withdrawals. + */ +interface ISimpleDVTStakingStrategy { + /// @dev Custom errors: + error NotEnoughWeth(); // Thrown if the contract has insufficient WETH for operations. + error InvalidWithdrawalQueueState(); // Thrown if the withdrawal queue state is inconsistent or invalid. + error LimitOverflow(); // Thrown if the maximum allowed remainder is exceeded. + error DepositFailed(); // Thrown if the deposit operation failed. + + /** + * @return The vault address. + */ + function vault() external view returns (IVault); + + /** + * @return The staking module address. + */ + function stakingModule() external view returns (IStakingModule); + + /** + * @notice Returns the maximum allowed remainder of staked tokens in the vault after the convert operation. + * @return The maximum allowed remainder. + */ + function maxAllowedRemainder() external view returns (uint256); + + /** + * @notice Sets the maximum allowed remainder of staked tokens in the vault. + * @param newMaxAllowedRemainder The new maximum remainder allowed. + */ + function setMaxAllowedRemainder(uint256 newMaxAllowedRemainder) external; + + /** + * @notice Converts and deposits into specific staking module. + * @param blockNumber The block number at the time of the operation for verification. + * @param blockHash The block hash at the time of the operation for additional verification. + * @param depositRoot The root hash of the deposit records for verification. + * @param nonce A nonce to ensure the uniqueness of the operation. + * @param depositCalldata The calldata required for the deposit operation. + * @param sortedGuardianSignatures The signatures from guardians verifying the operation. + * @notice The function can be called by anyone. + */ + function convertAndDeposit( + uint256 blockNumber, + bytes32 blockHash, + bytes32 depositRoot, + uint256 nonce, + bytes calldata depositCalldata, + IDepositSecurityModule.Signature[] calldata sortedGuardianSignatures + ) external; + + /** + * @notice Processes withdrawals from the vault, possibly staking some of the withdrawn assets. + * @param users An array of user addresses to process withdrawals for. + * @param amountForStake The amount from the withdrawals to be staked. + * @return statuses An array of booleans indicating the success of each individual withdrawal. + */ + function processWithdrawals( + address[] memory users, + uint256 amountForStake + ) external returns (bool[] memory statuses); + + /** + * @notice Emitted when the max allowed remainder is updated. + * @param newMaxAllowedRemainder The new maximum allowed remainder. + * @param sender The address of the admin who made the change. + */ + event MaxAllowedRemainderChanged( + uint256 newMaxAllowedRemainder, + address sender + ); + + /** + * @notice Emitted after attempting to convert and deposit tokens via the staking module. + * @param sender The address that initiated the operation. + */ + event ConvertAndDeposit(address sender); + + /** + * @notice Emitted when processing withdrawals. + * @param users Array of user addresses involved in the withdrawal process. + * @param amountForStake The amount of assets intended forstaking. + * @param sender The address that initiated the withdrawal process. + */ + event ProcessWithdrawals( + address[] users, + uint256 amountForStake, + address sender + ); +} diff --git a/contracts/mellow-lrt/interfaces/utils/IDefaultAccessControl.sol b/contracts/mellow-lrt/interfaces/utils/IDefaultAccessControl.sol new file mode 100644 index 0000000..7650a15 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/utils/IDefaultAccessControl.sol @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/access/extensions/IAccessControlEnumerable.sol"; + +/// @notice This is a default access control with 3 roles: +/// +/// - ADMIN: allowed to do anything +/// - ADMIN_DELEGATE: allowed to do anything except assigning ADMIN and ADMIN_DELEGATE roles +/// - OPERATOR: low-privileged role, generally keeper or some other bot +interface IDefaultAccessControl is IAccessControlEnumerable { + error Forbidden(); + error AddressZero(); + + function OPERATOR() external view returns (bytes32); + + function ADMIN_ROLE() external view returns (bytes32); + + function ADMIN_DELEGATE_ROLE() external view returns (bytes32); + + /// @notice Checks that the address is contract admin. + /// @param who Address to check + /// @return `true` if who is admin, `false` otherwise + function isAdmin(address who) external view returns (bool); + + /// @notice Checks that the address is contract admin. + /// @param who Address to check + /// @return `true` if who is operator, `false` otherwise + function isOperator(address who) external view returns (bool); + + /// @notice Checks that the address is contract admin. + /// @param who Address to check + /// @dev throws Forbbiden() if the sender does not have the admin or admin_delegate role + function requireAdmin(address who) external view; + + /// @notice Checks that the address is contract admin. + /// @param who Address to check + /// @dev throws Forbbiden() if the sender has no roles + function requireAtLeastOperator(address who) external view; +} diff --git a/contracts/mellow-lrt/interfaces/utils/IDepositCallback.sol b/contracts/mellow-lrt/interfaces/utils/IDepositCallback.sol new file mode 100644 index 0000000..80f800a --- /dev/null +++ b/contracts/mellow-lrt/interfaces/utils/IDepositCallback.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title IDepositCallback + * @notice Interface defining a callback function to handle deposit results. + */ +interface IDepositCallback { + /** + * @notice Handles the callback after a deposit operation has been executed. + * @param actualAmounts An array representing the actual amounts of each token that were deposited. + * @param lpAmount The total amount of LP tokens that were issued as a result of the deposit. + * @dev This function is intended to be implemented by contracts that need to take further action following a deposit. + */ + function depositCallback( + uint256[] memory actualAmounts, + uint256 lpAmount + ) external; +} diff --git a/contracts/mellow-lrt/interfaces/utils/IDepositWrapper.sol b/contracts/mellow-lrt/interfaces/utils/IDepositWrapper.sol new file mode 100644 index 0000000..5de0660 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/utils/IDepositWrapper.sol @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; + +import "../external/lido/IWeth.sol"; +import "../external/lido/ISteth.sol"; +import "../external/lido/IWSteth.sol"; + +import "../IVault.sol"; + +/** + * @title IDepositWrapper + * @notice Interface defining the functions for wrapping tokens before deposit into a vault. + */ +interface IDepositWrapper { + /// @dev Errors + error AddressZero(); + error InvalidToken(); + error InvalidAmount(); + error InvalidTokenList(); + error InvalidSender(); + + /** + * @notice Returns the address of the WETH token. + * @return The address of the WETH token. + */ + function weth() external view returns (address); + + /** + * @notice Returns the address of the stETH token. + * @return The address of the stETH token. + */ + function steth() external view returns (address); + + /** + * @notice Returns the address of the wstETH token. + * @return The address of the wstETH token. + */ + function wsteth() external view returns (address); + + /** + * @notice Returns the address of the vault to which deposits are made. + * @return The address of the vault. + */ + function vault() external view returns (IVault); + + /** + * @notice Deposits specified tokens into the vault, converting them to the required format if necessary. + * @param to The address that will receive the resulting LP tokens. + * @param token The address of the token to deposit (can be WETH, stETH, wstETH, or ETH). + * @param amount The amount of tokens to deposit. + * @param minLpAmount The minimum number of LP tokens expected from the deposit. + * @param deadline The deadline timestamp for the deposit transaction. + * @param referralCode The referral code for the deposit. + * @return lpAmount The amount of LP tokens obtained from the deposit. + */ + function deposit( + address to, + address token, + uint256 amount, + uint256 minLpAmount, + uint256 deadline, + uint256 referralCode + ) external payable returns (uint256 lpAmount); + + /** + * @notice Emitted when a deposit is executed in the Deposit Wrapper contract. + * @param sender The address of the account initiating the deposit. + * @param token The address of the token being deposited. + * @param amount The amount of the token being deposited. + * @param lpAmount The amount of LP tokens received after the deposit. + * @param deadline The deadline by which the deposit must be executed. + */ + event DepositWrapperDeposit( + address indexed sender, + address token, + uint256 amount, + uint256 lpAmount, + uint256 deadline + ); +} diff --git a/contracts/mellow-lrt/interfaces/utils/IWithdrawalCallback.sol b/contracts/mellow-lrt/interfaces/utils/IWithdrawalCallback.sol new file mode 100644 index 0000000..e575eef --- /dev/null +++ b/contracts/mellow-lrt/interfaces/utils/IWithdrawalCallback.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +/** + * @title IWithdrawalCallback + * @notice Interface defining a callback function to handle post-withdrawal actions in processWithdrawals function. + */ +interface IWithdrawalCallback { + /** + * @notice Handles the callback after a withdrawal operation has been executed. + * @dev This function should be implemented to carry out any additional actions required after the withdrawal. + * It does not take any parameters and will be invoked once the withdrawal process is complete. + */ + function withdrawalCallback() external; +} diff --git a/contracts/mellow-lrt/interfaces/validators/IAllowAllValidator.sol b/contracts/mellow-lrt/interfaces/validators/IAllowAllValidator.sol new file mode 100644 index 0000000..3b02b07 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/validators/IAllowAllValidator.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "./IValidator.sol"; + +/** + * @title IAllowAllValidator + * @notice Interface for a validator that allows all transactions without additional checks. + */ +interface IAllowAllValidator is IValidator {} diff --git a/contracts/mellow-lrt/interfaces/validators/IDefaultBondValidator.sol b/contracts/mellow-lrt/interfaces/validators/IDefaultBondValidator.sol new file mode 100644 index 0000000..089ff10 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/validators/IDefaultBondValidator.sol @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "./IValidator.sol"; + +import "../modules/symbiotic/IDefaultBondModule.sol"; + +/** + * @title IDefaultBondValidator + * @notice Interface defining a validator for supported bond deposits and withdrawals. + */ +interface IDefaultBondValidator is IValidator { + /// @dev Errors + error InvalidLength(); // Thrown when data does not match the expected length. + error ZeroAmount(); // Thrown when a transaction specifies a zero amount. + error InvalidSelector(); // Thrown when a function selector is not recognized. + error UnsupportedBond(); // Thrown when an unsupported bond is referenced. + + /** + * @notice Checks if a specific bond is supported. + * @param bond The address of the bond to verify. + * @return `true` if the bond is supported, otherwise `false`. + */ + function isSupportedBond(address bond) external view returns (bool); + + /** + * @notice Sets the supported status of a specific bond. + * @param bond The address of the bond to update. + * @param flag `true` to mark the bond as supported, otherwise `false`. + */ + function setSupportedBond(address bond, bool flag) external; + + /** + * @notice Validates bond deposit or withdrawal operations, ensuring that only supported bonds are accessed. + * @param data The calldata containing deposit or withdrawal details to validate. + * @dev Reverts with appropriate errors if the data is incorrectly formatted or if the bond is unsupported. + * Checks for `deposit` or `withdraw` function signatures from the `IDefaultBondModule`. + */ + function validate( + address from, + address to, + bytes calldata data + ) external view override; + + /** + * @notice Emitted when a supported bond is set or updated in the Default Bond Validator contract. + * @param bond The address of the bond. + * @param flag A boolean indicating whether the bond is supported or not. + * @param timestamp The timestamp when the action was executed. + */ + event DefaultBondValidatorSetSupportedBond( + address indexed bond, + bool flag, + uint256 timestamp + ); +} diff --git a/contracts/mellow-lrt/interfaces/validators/IERC20SwapValidator.sol b/contracts/mellow-lrt/interfaces/validators/IERC20SwapValidator.sol new file mode 100644 index 0000000..07f25e1 --- /dev/null +++ b/contracts/mellow-lrt/interfaces/validators/IERC20SwapValidator.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/utils/Strings.sol"; + +import "./IValidator.sol"; + +import "../modules/erc20/IERC20SwapModule.sol"; + +/** + * @title IERC20SwapValidator + * @notice Validator for ERC20 token swaps, ensuring that only authorized routers and tokens are used. + */ +interface IERC20SwapValidator is IValidator { + /// @dev Errors + error InvalidLength(); + error InvalidSelector(); + error UnsupportedToken(); + error UnsupportedRouter(); + + /** + * @dev Maps each router's address to a boolean indicating whether it is supported or not. + * If `true`, the router is supported for swaps. + */ + function isSupportedRouter(address) external view returns (bool); + /** + * @dev Maps each ERC20 token's address to a boolean indicating whether it is supported or not. + * If `true`, the token is supported for swaps. + */ + function isSupportedToken(address) external view returns (bool); + + /** + * @notice Sets the supported status of a specific router. + * @param router The address of the router to update. + * @param flag `true` to mark the router as supported, otherwise `false`. + */ + function setSupportedRouter(address router, bool flag) external; + + /** + * @notice Sets the supported status of a specific ERC20 token. + * @param token The address of the token to update. + * @param flag `true` to mark the token as supported, otherwise `false`. + */ + function setSupportedToken(address token, bool flag) external; + + /** + * @notice Validates swap operations involving specific routers and tokens to ensure compliance. + * @param from The address initiating the transaction. + * @param to The address receiving the transaction (swap module). + * @param data The calldata containing details of the swap operation. + * @dev The `validate` function checks function signatures, swap parameters, and the validity of involved routers and tokens. + * Reverts with appropriate errors if validation fails. + */ + function validate( + address from, + address to, + bytes calldata data + ) external view; + + /** + * @notice Emitted when a supported router is set or updated in the ERC20 Swap Validator contract. + * @param router The address of the router. + * @param flag A boolean indicating whether the router is supported or not. + * @param timestamp The timestamp when the action was executed. + */ + event ERC20SwapValidatorSetSupportedRouter( + address indexed router, + bool flag, + uint256 timestamp + ); + + /** + * @notice Emitted when a supported token is set or updated in the ERC20 Swap Validator contract. + * @param token The address of the token. + * @param flag A boolean indicating whether the token is supported or not. + * @param timestamp The timestamp when the action was executed. + */ + event ERC20SwapValidatorSetSupportedToken( + address indexed token, + bool flag, + uint256 timestamp + ); +} diff --git a/contracts/mellow-lrt/interfaces/validators/IManagedValidator.sol b/contracts/mellow-lrt/interfaces/validators/IManagedValidator.sol new file mode 100644 index 0000000..c10f19f --- /dev/null +++ b/contracts/mellow-lrt/interfaces/validators/IManagedValidator.sol @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +import "./IValidator.sol"; + +/** + * @title ManagedValidator + * @notice A role-based validator that provides control over access permissions. + * Allows role-based management of contract permissions and supports custom validation logic. + * + * The primary validator contract of the system, used to check the access permissions of users to call contracts with specified selectors. + * The main function of the contract is hasPermissions(from, to, selector), which checks whether the specified caller "from" has the right to make the call to.call(abi.encodeWithSelector(selector, someData)). + * + * Bitwise masks are used to store roles, thus the maximum number of roles in the system is limited to 256 (0-255). + * The system consists of 4 types of roles: + * 1. publicRoles - bitmask of public roles available to all users + * 2. userRoles - bitmask of roles for the calling user + * 3. allowAllSignaturesRoles - bitmask of roles for the called contract + * 4. allowSignatureRoles - bitmask of roles for the called contract and specific selector + * + * Additionally, the system has a dedicated role - 255 - ADMIN_ROLE, which grants full access to all contract functions without additional checks. + * + * Therefore, the hasPermissions algorithm looks like this: + * 1. Determine the set of roles possessed by the specified user (userRoles[from] | publicRoles) + * 2. If the user has the ADMIN_ROLE role, access is granted ((userRoles[from] | publicRoles) & ADMIN_ROLE_MASK != 0) + * 3. If the called contract has at least one role in its corresponding set that matches a role in the user's role set, access is granted (allowAllSignaturesRoles[to] & (publicRoles | userRoles[from]) != 0) + * 4. If the called contract with specified function selector have at least one role in their corresponding role sets that matches a role in the user's role set, access is granted (allowSignatureRoles[to][selector] & (publicRoles | userRoles[from]) != 0) + * 5. Otherwise, access is denied and the function returns false + * + * For greater flexibility, it is possible to set a custom validator for the called contract, which will be used after the standard check of permissions. + * Thus, the validate function checks the presence of permissions as follows: + * 1. If the data does not contain at least 4 bytes (required for the selector), the function reverts with an InvalidData error. + * 2. If the hasPermissions function returns false, the function reverts with a Forbidden error. + * 3. If a custom validator is set for the contract, the validate function of the custom validator is called. + */ +interface IManagedValidator is IValidator { + /// @dev Errors + error Forbidden(); + error InvalidData(); + + /** + * @notice Storage structure used for maintaining role-based access control data. + */ + struct Storage { + /// @dev Maps each user's address to their assigned roles using a bitmask. + mapping(address => uint256) userRoles; + /// @dev A bitmask representing public roles that are accessible by all users. + uint256 publicRoles; + /// @dev Maps each contract's address to a bitmask of roles that allow access to all functions on the contract. + mapping(address => uint256) allowAllSignaturesRoles; + /// @dev Maps each contract's address and function signature to a bitmask of roles that allow access to specific functions. + mapping(address => mapping(bytes4 => uint256)) allowSignatureRoles; + /// @dev Maps each contract's address to the address of a custom validator, if one is set. + mapping(address => address) customValidator; + } + + /// @dev A constant representing the admin role bitmask. + function ADMIN_ROLE_MASK() external view returns (uint256); + + /// @dev A constant representing the storage position for the role-based data. + function STORAGE_POSITION() external view returns (bytes32); + + /** + * @notice Checks whether a user has permission for a specific function on a given contract. + * @param user The address of the user to check. + * @param contractAddress The address of the contract being accessed. + * @param signature The function signature being checked. + * @return `true` if the user has permission, otherwise `false`. + */ + function hasPermission( + address user, + address contractAddress, + bytes4 signature + ) external view returns (bool); + + /** + * @notice Ensures that a user has the necessary permissions for the specified function. + * @param user The address of the user being verified. + * @param contractAddress The address of the contract being accessed. + * @param signature The function signature being checked. + * @dev Reverts with `Forbidden` if the user lacks the required permissions. + */ + function requirePermission( + address user, + address contractAddress, + bytes4 signature + ) external view; + + /** + * @notice Grants a public role. + * @param role The bitmask index of the role to grant. + */ + function grantPublicRole(uint8 role) external; + + /** + * @notice Revokes a public role, preventing all users from accessing the specified functions. + * @param role The bitmask index of the role to revoke. + */ + function revokePublicRole(uint8 role) external; + + /** + * @notice Assigns a specific role to a given user. + * @param user The address of the user to assign the role to. + * @param role The bitmask index of the role to assign. + */ + function grantRole(address user, uint8 role) external; + + /** + * @notice Revokes a specific role from a given user. + * @param user The address of the user to revoke the role from. + * @param role The bitmask index of the role to revoke. + */ + function revokeRole(address user, uint8 role) external; + + /** + * @notice Sets a custom validator for a specified contract. + * @param contractAddress The address of the contract that will use the custom validator. + * @param validator The address of the custom validator. + * @dev Reverts with `Forbidden` if the validator is set to this contract. + */ + function setCustomValidator( + address contractAddress, + address validator + ) external; + + /** + * @notice Grants a role for a specified contract. + * @param contractAddress The address of the contract. + * @param role The bitmask index of the role to grant. + */ + function grantContractRole(address contractAddress, uint8 role) external; + + /** + * @notice Revokes a role from a specified contract. + * @param contractAddress The address of the contract. + * @param role The bitmask index of the role to revoke. + */ + function revokeContractRole(address contractAddress, uint8 role) external; + + /** + * @notice Grants a role for a specified pair contract-selector. + * @param contractAddress The address of the contract. + * @param signature The function signature. + * @param role The bitmask index of the role to grant. + */ + function grantContractSignatureRole( + address contractAddress, + bytes4 signature, + uint8 role + ) external; + + /** + * @notice Revokes a role from a specified pair contract-selector. + * @param contractAddress The address of the contract. + * @param signature The function signature. + * @param role The bitmask index of the role to revoke. + */ + function revokeContractSignatureRole( + address contractAddress, + bytes4 signature, + uint8 role + ) external; + + /** + * @notice Returns the custom validator assigned to a specified contract. + * @param contractAddress The address of the contract. + * @return address of the custom validator. + */ + function customValidator( + address contractAddress + ) external view returns (address); + + /** + * @notice Returns the bitmask representing the roles assigned to a given user. + * @param user The address of the user. + * @return uint256 The bitmask of roles assigned to the user. + */ + function userRoles(address user) external view returns (uint256); + + /** + * @notice Returns the bitmask representing the public roles accessible to all users. + * @return uint256 The bitmask of public roles. + */ + function publicRoles() external view returns (uint256); + + /** + * @notice Returns the bitmask representing roles that allow access to all functions on a specific contract. + * @param contractAddress The address of the contract. + * @return uint256 The bitmask of roles allowing access to all functions on the contract. + */ + function allowAllSignaturesRoles( + address contractAddress + ) external view returns (uint256); + + /** + * @notice Returns the bitmask representing roles that allow access to specific pair of contract-selector. + * @param contractAddress The address of the contract. + * @param selector The function signature. + * @return The bitmask of roles allowing access to the specified function on the contract. + */ + function allowSignatureRoles( + address contractAddress, + bytes4 selector + ) external view returns (uint256); + + /** + * @notice Validates access permissions for a user to execute a function on a target contract. + * @param from The address of the user attempting the action. + * @param to The address of the target contract. + * @param data The call data containing the function signature and arguments. + * @dev Reverts with `InvalidData` if the call data is too short. + * Uses a custom validator if one is configured for the target contract. + */ + function validate( + address from, + address to, + bytes calldata data + ) external view; + + /** + * @notice Emitted when a public role is granted to a user in the Managed Validator contract. + * @param role The index of the public role. + */ + event PublicRoleGranted(uint8 role); + + /** + * @notice Emitted when a public role is revoked from a user in the Managed Validator contract. + * @param role The index of the public role. + */ + event PublicRoleRevoked(uint8 role); + + /** + * @notice Emitted when a role is granted to a user in the Managed Validator contract. + * @param user The address of the user. + * @param role The index of the role. + */ + event RoleGranted(address indexed user, uint8 role); + + /** + * @notice Emitted when a role is revoked from a user in the Managed Validator contract. + * @param user The address of the user. + * @param role The index of the role. + */ + event RoleRevoked(address indexed user, uint8 role); + + /** + * @notice Emitted when a custom validator is set for a contract in the Managed Validator contract. + * @param contractAddress The address of the contract. + * @param validator The address of the custom validator. + */ + event CustomValidatorSet( + address indexed contractAddress, + address validator + ); + + /** + * @notice Emitted when a role is granted to a contract in the Managed Validator contract. + * @param contractAddress The address of the contract. + * @param role The index of the role. + */ + event ContractRoleGranted(address indexed contractAddress, uint8 role); + + /** + * @notice Emitted when a role is revoked from a contract in the Managed Validator contract. + * @param contractAddress The address of the contract. + * @param role The index of the role. + */ + event ContractRoleRevoked(address indexed contractAddress, uint8 role); + + /** + * @notice Emitted when a role is granted to a pair contract-selector in the Managed Validator contract. + * @param contractAddress The address of the contract. + * @param signature The function signature. + * @param role The index of the role. + */ + event ContractSignatureRoleGranted( + address indexed contractAddress, + bytes4 signature, + uint8 role + ); + + /** + * @notice Emitted when a role is revoked from a pair contract-selector in the Managed Validator contract. + * @param contractAddress The address of the contract. + * @param signature The function signature. + * @param role The index of the role. + */ + event ContractSignatureRoleRevoked( + address indexed contractAddress, + bytes4 signature, + uint8 role + ); +} diff --git a/contracts/mellow-lrt/interfaces/validators/IValidator.sol b/contracts/mellow-lrt/interfaces/validators/IValidator.sol new file mode 100644 index 0000000..69522fb --- /dev/null +++ b/contracts/mellow-lrt/interfaces/validators/IValidator.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BSL-1.1 +pragma solidity ^0.8.20; + +/** + * @title IValidator + * @notice Interface defining a generic validator for transaction data. + */ +interface IValidator { + /** + * @notice Validates a transaction involving two addresses based on the provided calldata. + * @param from The address initiating the transaction. + * @param to The target address of the transaction. + * @param data The transaction data containing the function selector and any necessary parameters. + * @dev Implementers should validate that the transaction is authorized, properly formatted, and adheres to the required business logic. + * Reverts if the transaction is invalid. + */ + function validate( + address from, + address to, + bytes calldata data + ) external view; +} diff --git a/contracts/mellowpricefeed/CloneFactory.sol b/contracts/mellowpricefeed/CloneFactory.sol new file mode 100644 index 0000000..e69de29 diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol new file mode 100644 index 0000000..ed9ec65 --- /dev/null +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; +import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import "../mellow-lrt/interfaces/IVault.sol"; + +/// @title Contract for retreiving a Mellow LRT Vault's exchange rate value with chainlink's AggregatorV3Interface +/// implemented. +/// @author Ojo Network (https://docs.ojo.network/) +contract PriceFeedQuoted is Initializable, AggregatorV3Interface { + uint8 private priceFeedDecimals; + + string private priceFeedBase; + + string private priceFeedQuote; + + IVault public immutable vault; + + uint80 constant DEFAULT_ROUND = 1; + + uint256 constant DEFAULT_VERSION = 1; + + uint256 internal constant INT256_MAX = uint256(type(int256).max); + + error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); + + error UnsafeUintToIntConversion(uint256 value); + + constructor(address vault_) { + vault = IVault(vault_); + } + + /// @notice Initialize clone of this contract. + /// @dev This function is used in place of a constructor in proxy contracts. + /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. + /// @param _priceFeedBase Base asset of PriceFeed. + /// @param _priceFeedQuote Quote asset of PriceFeed. + function initialize(uint8 _priceFeedDecimals, string calldata _priceFeedBase, string calldata _priceFeedQuote) + external + initializer { + priceFeedDecimals = _priceFeedDecimals; + priceFeedBase = _priceFeedBase; + priceFeedQuote = _priceFeedQuote; + } + + /// @notice Amount of decimals price is denominated in. + function decimals() external view returns (uint8) { + return priceFeedDecimals; + } + + /// @notice Asset pair that this proxy is tracking. + function description() external view returns (string memory) { + return string(abi.encodePacked(priceFeedBase, "/", priceFeedQuote)); + } + + /// @notice Version always returns 1. + function version() external view returns (uint256) { + return DEFAULT_VERSION; + } + + /// @dev Latest round always returns 1 since this contract does not support rounds. + function latestRound() public pure returns (uint80) { + return DEFAULT_ROUND; + } + + /// @notice Calculates exchange rate from the Mellow LRT Vault from a specified round. + /// @dev Even though rounds are not utilized in this contract getRoundData is implemented for contracts + /// that still rely on it. Function will revert if specified round is not the latest round. + /// @return roundId Round ID of price data, this is always set to 1. + /// @return answer Price in USD of asset this contract is tracking. + /// @return startedAt Timestamp relating to price update. + /// @return updatedAt Timestamp relating to price update. + /// @return answeredInRound Equal to round ID. + function getRoundData(uint80 _roundId) + external + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) { + if (_roundId != latestRound()) { + revert GetRoundDataCanBeOnlyCalledWithLatestRound(_roundId); + } + return latestRoundData(); + } + + /// @notice Calculates exchange rate from the Mellow LRT Vault. + /// @return roundId Round ID of price data, this is always set to 1. + /// @return answer Price of priceFeedBase quoted by priceFeedQuote. + /// @return startedAt Timestamp relating to price update. + /// @return updatedAt Timestamp relating to price update. + /// @return answeredInRound Equal to round ID. + function latestRoundData() + public + view + returns ( + uint80 roundId, + int256 answer, + uint256 startedAt, + uint256 updatedAt, + uint80 answeredInRound + ) { + roundId = latestRound(); + bytes32 baseAssetName = bytes32(bytes(priceFeedBase)); + bytes32 quoteAssetName = bytes32(bytes(priceFeedQuote)); + + IVault.ProcessWithdrawalsStack memory processWithdrawalsStack = vault.calculateStack(); + + answer = 0; + if (processWithdrawalsStack.totalSupply != 0) { + answer = int256(processWithdrawalsStack.totalSupply) * 1e18 / int256(processWithdrawalsStack.totalSupply); + } + + // These values are equal after chainlink’s OCR update + startedAt = processWithdrawalsStack.timestamp; + updatedAt = startedAt; + + // roundId is always equal to answeredInRound + answeredInRound = roundId; + + return ( + roundId, + answer, + startedAt, + updatedAt, + answeredInRound + ); + } +} From 86e89592cb91bcbd3243c35cb06e0068aaec20d2 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 10 Oct 2024 10:16:23 -0400 Subject: [PATCH 2/7] factory contract --- contracts/mellowpricefeed/CloneFactory.sol | 38 +++++++++++++++++++ contracts/mellowpricefeed/MellowPriceFeed.sol | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/contracts/mellowpricefeed/CloneFactory.sol b/contracts/mellowpricefeed/CloneFactory.sol index e69de29..76da69e 100644 --- a/contracts/mellowpricefeed/CloneFactory.sol +++ b/contracts/mellowpricefeed/CloneFactory.sol @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "./MellowPriceFeed.sol"; +import "@openzeppelin/contracts/proxy/Clones.sol"; + +/// @title Factory for creating MellowPriceFeed contract clones. +/// @notice This contract will create a MellowPriceFeed clone and map its address to the clone creator. +/// @dev Cloning is done with OpenZeppelin's Clones contract. +contract CloneFactory { + event MellowPriceFeedCloneCreated( + address _mellowPriceFeedCloneAddress + ); + + mapping (address => address) public MellowPriceFeedCloneAddresses; + address public implementationAddress; + + /// @param _implementationAddress Address of implementation contract to be cloned. + constructor(address _implementationAddress) { + implementationAddress = _implementationAddress; + } + + /// @notice Create clone of MellowPriceFeed contract and initialize it. + /// @dev Clone method returns address of created clone. + /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. + /// @param _priceFeedBase Base asset of PriceFeed, should be set to asset symbol ticker. + /// @param _priceFeedQuote Quote asset of PriceFeed, should be set to asset symbol ticker. + function createMellowPriceFeed( + uint8 _priceFeedDecimals, + string calldata _priceFeedBase, + string calldata _priceFeedQuote + ) external { + address mellowPriceFeedCloneAddress = Clones.clone(implementationAddress); + MellowPriceFeed(mellowPriceFeedCloneAddress).initialize(_priceFeedDecimals, _priceFeedBase, _priceFeedQuote); + MellowPriceFeedCloneAddresses[msg.sender] = mellowPriceFeedCloneAddress; + emit MellowPriceFeedCloneCreated(mellowPriceFeedCloneAddress); + } +} diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol index ed9ec65..3fcba65 100644 --- a/contracts/mellowpricefeed/MellowPriceFeed.sol +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -8,7 +8,7 @@ import "../mellow-lrt/interfaces/IVault.sol"; /// @title Contract for retreiving a Mellow LRT Vault's exchange rate value with chainlink's AggregatorV3Interface /// implemented. /// @author Ojo Network (https://docs.ojo.network/) -contract PriceFeedQuoted is Initializable, AggregatorV3Interface { +contract MellowPriceFeed is Initializable, AggregatorV3Interface { uint8 private priceFeedDecimals; string private priceFeedBase; From 23104a48554f04aad1c9de68618a8cca47322fbd Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 10 Oct 2024 12:35:37 -0400 Subject: [PATCH 3/7] scripts and contract bug fixes --- .env.example | 2 + contracts/mellowpricefeed/CloneFactory.sol | 9 ++- contracts/mellowpricefeed/MellowPriceFeed.sol | 23 +++--- mainnet_chains.json | 16 +++- scripts/createMellowPriceFeeds.ts | 61 ++++++++++++++++ scripts/deployMellowCloneFactory.ts | 38 ++++++++++ .../deployMellowPriceFeedImplementation.ts | 38 ++++++++++ testnet_chains.json | 73 ++++++++++++++----- 8 files changed, 224 insertions(+), 36 deletions(-) create mode 100644 scripts/createMellowPriceFeeds.ts create mode 100644 scripts/deployMellowCloneFactory.ts create mode 100644 scripts/deployMellowPriceFeedImplementation.ts diff --git a/.env.example b/.env.example index 8b19180..eb4d0a1 100644 --- a/.env.example +++ b/.env.example @@ -9,3 +9,5 @@ ASSET_LIMIT=5 PRICE_FEED_DECIMALS=9 PRICE_FEED_DESCRIPTIONS=["steakLRT", "Re7LRT", "amphrETH", "rstETH"] QUOTED_PRICE_FEEDS=["WETH/ETH", "WETH/USDC"] +MELLOW_PRICE_FEEDS=["amphrETH/wstETH"] +MELLOW_VAULTS=["0x5fD13359Ba15A84B76f7F87568309040176167cd"] diff --git a/contracts/mellowpricefeed/CloneFactory.sol b/contracts/mellowpricefeed/CloneFactory.sol index 76da69e..db13554 100644 --- a/contracts/mellowpricefeed/CloneFactory.sol +++ b/contracts/mellowpricefeed/CloneFactory.sol @@ -22,16 +22,23 @@ contract CloneFactory { /// @notice Create clone of MellowPriceFeed contract and initialize it. /// @dev Clone method returns address of created clone. + /// @param _vault Address of Mellow LRT vault. /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. /// @param _priceFeedBase Base asset of PriceFeed, should be set to asset symbol ticker. /// @param _priceFeedQuote Quote asset of PriceFeed, should be set to asset symbol ticker. function createMellowPriceFeed( + address _vault, uint8 _priceFeedDecimals, string calldata _priceFeedBase, string calldata _priceFeedQuote ) external { address mellowPriceFeedCloneAddress = Clones.clone(implementationAddress); - MellowPriceFeed(mellowPriceFeedCloneAddress).initialize(_priceFeedDecimals, _priceFeedBase, _priceFeedQuote); + MellowPriceFeed(mellowPriceFeedCloneAddress).initialize( + _vault, + _priceFeedDecimals, + _priceFeedBase, + _priceFeedQuote + ); MellowPriceFeedCloneAddresses[msg.sender] = mellowPriceFeedCloneAddress; emit MellowPriceFeedCloneCreated(mellowPriceFeedCloneAddress); } diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol index 3fcba65..2871cd1 100644 --- a/contracts/mellowpricefeed/MellowPriceFeed.sol +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -15,7 +15,7 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { string private priceFeedQuote; - IVault public immutable vault; + IVault public vault; uint80 constant DEFAULT_ROUND = 1; @@ -25,20 +25,19 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); - error UnsafeUintToIntConversion(uint256 value); - - constructor(address vault_) { - vault = IVault(vault_); - } - /// @notice Initialize clone of this contract. /// @dev This function is used in place of a constructor in proxy contracts. + /// @param _vault Address of Mellow LRT vault. /// @param _priceFeedDecimals Amount of decimals a PriceFeed is denominiated in. /// @param _priceFeedBase Base asset of PriceFeed. /// @param _priceFeedQuote Quote asset of PriceFeed. - function initialize(uint8 _priceFeedDecimals, string calldata _priceFeedBase, string calldata _priceFeedQuote) - external - initializer { + function initialize( + address _vault, + uint8 _priceFeedDecimals, + string calldata _priceFeedBase, + string calldata _priceFeedQuote + ) external initializer { + vault = IVault(_vault); priceFeedDecimals = _priceFeedDecimals; priceFeedBase = _priceFeedBase; priceFeedQuote = _priceFeedQuote; @@ -105,14 +104,12 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { uint80 answeredInRound ) { roundId = latestRound(); - bytes32 baseAssetName = bytes32(bytes(priceFeedBase)); - bytes32 quoteAssetName = bytes32(bytes(priceFeedQuote)); IVault.ProcessWithdrawalsStack memory processWithdrawalsStack = vault.calculateStack(); answer = 0; if (processWithdrawalsStack.totalSupply != 0) { - answer = int256(processWithdrawalsStack.totalSupply) * 1e18 / int256(processWithdrawalsStack.totalSupply); + answer = int256(processWithdrawalsStack.totalValue) * 1e18 / int256(processWithdrawalsStack.totalSupply); } // These values are equal after chainlink’s OCR update diff --git a/mainnet_chains.json b/mainnet_chains.json index 4e9f187..8bf1cac 100644 --- a/mainnet_chains.json +++ b/mainnet_chains.json @@ -10,8 +10,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xa1aB70C0F3725AcA1D1e85Bd4402Dd2d5F6AFf19", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xd285A4F0Ad1BB6b1Db8cD3dD839E9f423938ef9E", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Optimism", @@ -24,8 +26,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x02Ed15B70D4dE1209c3Dd5a75195CB3f3dDB8B07", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base", @@ -38,8 +42,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Ethereum", @@ -52,7 +58,9 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0xde471274F1B684476d341eB131224F389AD4A270", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x710C8a3c8CB393cA24748849de3585b5C48D4D0c", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" } ] diff --git a/scripts/createMellowPriceFeeds.ts b/scripts/createMellowPriceFeeds.ts new file mode 100644 index 0000000..3e1a05d --- /dev/null +++ b/scripts/createMellowPriceFeeds.ts @@ -0,0 +1,61 @@ +import { Wallet, ethers } from "ethers"; +import CloneFactory from '../artifacts/contracts/mellowpricefeed/CloneFactory.sol/CloneFactory.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main() { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + const mellowPriceFeedDecimals = process.env.PRICE_FEED_DECIMALS as any; + const mellowPriceFeeds = JSON.parse(process.env.MELLOW_PRICE_FEEDS!); + const mellowVaults = JSON.parse(process.env.MELLOW_VAULTS!); + + if (mellowPriceFeeds.length !== mellowVaults.length) { + throw new Error('unequal amount of mellowVaults associated with mellowPriceFeeds'); + } + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const cloneFactoryMellowContract = new ethers.Contract(chain.cloneFactoryMellow, CloneFactory.abi, wallet) + let i = 0 + for (const mellowPriceFeed of mellowPriceFeeds) { + console.log(`Deploying ${mellowPriceFeed} price feed on ${chain.name}`); + try { + const [baseAsset, quoteAsset] = mellowPriceFeed.split('/'); + + console.log("baseAsset", baseAsset) + console.log("quoteAsset", quoteAsset) + const tx = await cloneFactoryMellowContract.createMellowPriceFeed(mellowPriceFeedDecimals, mellowVaults[i], baseAsset, quoteAsset); + console.log(`Transaction sent: ${tx.hash}`); + + const receipt = await tx.wait(); + console.log(`Transaction mined: ${receipt.transactionHash}`); + } catch (error) { + console.error(`Failed to deploy ${mellowPriceFeed} on ${chain.name}:`, error); + } + i += 1 + } + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployMellowCloneFactory.ts b/scripts/deployMellowCloneFactory.ts new file mode 100644 index 0000000..8be6397 --- /dev/null +++ b/scripts/deployMellowCloneFactory.ts @@ -0,0 +1,38 @@ +import { Wallet, ethers } from "ethers"; +import CloneFactoryQuoted from '../artifacts/contracts/mellowpricefeed/CloneFactory.sol/CloneFactory.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main () { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const cloneFactoryQuotedFactory = new ethers.ContractFactory(CloneFactoryQuoted.abi, CloneFactoryQuoted.bytecode, wallet) + const cloneFactoryQuoted = await cloneFactoryQuotedFactory.deploy(chain.mellowPriceFeedImplementation) + console.log(`${chain.name}, address: ${await cloneFactoryQuoted.getAddress()}`); + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/deployMellowPriceFeedImplementation.ts b/scripts/deployMellowPriceFeedImplementation.ts new file mode 100644 index 0000000..9661333 --- /dev/null +++ b/scripts/deployMellowPriceFeedImplementation.ts @@ -0,0 +1,38 @@ +import { Wallet, ethers } from "ethers"; +import MellowPriceFeed from '../artifacts/contracts/mellowpricefeed/MellowPriceFeed.sol/MellowPriceFeed.json'; +import testnet_chains from '../testnet_chains.json'; +import mainnet_chains from '../mainnet_chains.json'; + +async function main() { + const evmChains = JSON.parse(process.env.EVM_CHAINS!); + + const privateKey = process.env.PRIVATE_KEY; + + if (!privateKey) { + throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); + } + + const mainnet = process.env.MAINNET as string + let chains = testnet_chains.map((chain) => ({ ...chain })); + if (mainnet === "TRUE") { + chains = mainnet_chains.map((chain) => ({ ...chain })); + } + + for (const chain of chains) { + if (evmChains.includes(chain.name)) { + const provider = new ethers.JsonRpcProvider(chain.rpc) + const wallet = new Wallet(privateKey, provider); + const balance = await provider.getBalance(wallet.address) + console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); + + const mellowPriceFeedFactory = new ethers.ContractFactory(MellowPriceFeed.abi, MellowPriceFeed.bytecode, wallet) + const mellowPriceFeed = await mellowPriceFeedFactory.deploy() + console.log(`${chain.name}, address: ${await mellowPriceFeed.getAddress()}`); + } + } +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/testnet_chains.json b/testnet_chains.json index 9e5c8bf..d57f5c5 100644 --- a/testnet_chains.json +++ b/testnet_chains.json @@ -10,8 +10,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Ethereum Sepolia", @@ -24,8 +26,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "0x1A069010D7F572c97925E83a1298Df8f96893c60", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "0x694723e8Fe9945CffDB671b02175DC55DeDf7F29" + "cloneFactoryQuoted": "0x694723e8Fe9945CffDB671b02175DC55DeDf7F29", + "cloneFactoryMellow": "" }, { "name": "BNB Chain", @@ -38,8 +42,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Polygon Mumbai", @@ -52,8 +58,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Polygon zkEVM", @@ -66,8 +74,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Avalanche Fuji C-Chain", @@ -80,8 +90,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Fantom", @@ -94,8 +106,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Moonbase", @@ -108,8 +122,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Arbitrum Goerli", @@ -122,6 +138,7 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", "cloneFactoryQuoted": "" }, @@ -136,8 +153,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x3DB6DF9EDfDcfE97D574Aa6f106C767051561Be2", "priceFeedQuotedImplementation": "0x2Babd8D4BCE072e78aA288c639Ef4516fCe26d89", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xab2c7cc090A45836fae04501e0454413ECA96611", - "cloneFactoryQuoted": "0x4f5E3B2d64670cd8EA2329c4B028a4c07832F846" + "cloneFactoryQuoted": "0x4f5E3B2d64670cd8EA2329c4B028a4c07832F846", + "cloneFactoryMellow": "" }, { "name": "Optimism Goerli", @@ -150,8 +169,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Optimism Sepolia", @@ -164,8 +185,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x48B10B538B7E5af4CbFd93B1C4d36668e8F6F644", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0xe9c4145FCeDdc19bc9B788C5d16cF08AD70d3850", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base Goerli", @@ -178,8 +201,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Base Sepolia", @@ -192,8 +217,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "0x02Ed15B70D4dE1209c3Dd5a75195CB3f3dDB8B07", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Mantle", @@ -206,8 +233,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Alfajores", @@ -220,8 +249,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Kava", @@ -234,8 +265,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Filecoin Calibration", @@ -248,8 +281,10 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" }, { "name": "Linea Goerli", @@ -262,7 +297,9 @@ "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", + "mellowPriceFeedImplementation": "", "cloneFactory": "", - "cloneFactoryQuoted": "" + "cloneFactoryQuoted": "", + "cloneFactoryMellow": "" } ] From 182974baea680b8efd342978436f721035f7be6c Mon Sep 17 00:00:00 2001 From: rbajollari Date: Thu, 10 Oct 2024 19:21:07 -0400 Subject: [PATCH 4/7] script fix --- scripts/createMellowPriceFeeds.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/createMellowPriceFeeds.ts b/scripts/createMellowPriceFeeds.ts index 3e1a05d..ce77a0a 100644 --- a/scripts/createMellowPriceFeeds.ts +++ b/scripts/createMellowPriceFeeds.ts @@ -41,7 +41,7 @@ async function main() { console.log("baseAsset", baseAsset) console.log("quoteAsset", quoteAsset) - const tx = await cloneFactoryMellowContract.createMellowPriceFeed(mellowPriceFeedDecimals, mellowVaults[i], baseAsset, quoteAsset); + const tx = await cloneFactoryMellowContract.createMellowPriceFeed(mellowVaults[i], mellowPriceFeedDecimals, baseAsset, quoteAsset); console.log(`Transaction sent: ${tx.hash}`); const receipt = await tx.wait(); From 622648001798ecf5953f1cb60839fed1f44ea6de Mon Sep 17 00:00:00 2001 From: rbajollari Date: Fri, 11 Oct 2024 17:07:10 -0400 Subject: [PATCH 5/7] managed ratio oracle --- contracts/mellowpricefeed/MellowPriceFeed.sol | 20 ++++++++++++------- mainnet_chains.json | 4 ++++ .../deployMellowPriceFeedImplementation.ts | 2 +- testnet_chains.json | 19 ++++++++++++++++++ 4 files changed, 37 insertions(+), 8 deletions(-) diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol index 2871cd1..fbf473c 100644 --- a/contracts/mellowpricefeed/MellowPriceFeed.sol +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.20; import "@openzeppelin/contracts/proxy/utils/Initializable.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; -import "../mellow-lrt/interfaces/IVault.sol"; +import "../mellow-lrt/interfaces/oracles/IManagedRatiosOracle.sol"; /// @title Contract for retreiving a Mellow LRT Vault's exchange rate value with chainlink's AggregatorV3Interface /// implemented. @@ -15,7 +15,9 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { string private priceFeedQuote; - IVault public vault; + IManagedRatiosOracle public managedRatiosOracle; + + address public vault; uint80 constant DEFAULT_ROUND = 1; @@ -25,6 +27,10 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { error GetRoundDataCanBeOnlyCalledWithLatestRound(uint80 requestedRoundId); + constructor(address managedRatiosOracle_) { + managedRatiosOracle = IManagedRatiosOracle(managedRatiosOracle_); + } + /// @notice Initialize clone of this contract. /// @dev This function is used in place of a constructor in proxy contracts. /// @param _vault Address of Mellow LRT vault. @@ -37,7 +43,7 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { string calldata _priceFeedBase, string calldata _priceFeedQuote ) external initializer { - vault = IVault(_vault); + vault = _vault; priceFeedDecimals = _priceFeedDecimals; priceFeedBase = _priceFeedBase; priceFeedQuote = _priceFeedQuote; @@ -105,15 +111,15 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { ) { roundId = latestRound(); - IVault.ProcessWithdrawalsStack memory processWithdrawalsStack = vault.calculateStack(); + uint128[] memory ratiosX96 = managedRatiosOracle.getTargetRatiosX96(vault, true); answer = 0; - if (processWithdrawalsStack.totalSupply != 0) { - answer = int256(processWithdrawalsStack.totalValue) * 1e18 / int256(processWithdrawalsStack.totalSupply); + if (ratiosX96.length != 0) { + answer = int256(uint256(ratiosX96[0])) * 1e18 / int256(managedRatiosOracle.Q96()); } // These values are equal after chainlink’s OCR update - startedAt = processWithdrawalsStack.timestamp; + startedAt = block.timestamp; updatedAt = startedAt; // roundId is always equal to answeredInRound diff --git a/mainnet_chains.json b/mainnet_chains.json index 8bf1cac..489e6b5 100644 --- a/mainnet_chains.json +++ b/mainnet_chains.json @@ -8,6 +8,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0xa1aB70C0F3725AcA1D1e85Bd4402Dd2d5F6AFf19", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -24,6 +25,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "0x955Ff4Cc738cDC009d2903196d1c94C8Cfb4D55d", "priceFeedImplementation": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -40,6 +42,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -56,6 +59,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0xde471274F1B684476d341eB131224F389AD4A270", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", diff --git a/scripts/deployMellowPriceFeedImplementation.ts b/scripts/deployMellowPriceFeedImplementation.ts index 9661333..d0b9e4c 100644 --- a/scripts/deployMellowPriceFeedImplementation.ts +++ b/scripts/deployMellowPriceFeedImplementation.ts @@ -26,7 +26,7 @@ async function main() { console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); const mellowPriceFeedFactory = new ethers.ContractFactory(MellowPriceFeed.abi, MellowPriceFeed.bytecode, wallet) - const mellowPriceFeed = await mellowPriceFeedFactory.deploy() + const mellowPriceFeed = await mellowPriceFeedFactory.deploy(chain.managedRatiosOracle) console.log(`${chain.name}, address: ${await mellowPriceFeed.getAddress()}`); } } diff --git a/testnet_chains.json b/testnet_chains.json index d57f5c5..67e9824 100644 --- a/testnet_chains.json +++ b/testnet_chains.json @@ -8,6 +8,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -24,6 +25,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "0x1A069010D7F572c97925E83a1298Df8f96893c60", "mellowPriceFeedImplementation": "", @@ -40,6 +42,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -56,6 +59,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -72,6 +76,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -88,6 +93,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -104,6 +110,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -120,6 +127,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -136,6 +144,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -151,6 +160,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0x3DB6DF9EDfDcfE97D574Aa6f106C767051561Be2", "priceFeedQuotedImplementation": "0x2Babd8D4BCE072e78aA288c639Ef4516fCe26d89", "mellowPriceFeedImplementation": "", @@ -167,6 +177,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -183,6 +194,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0x48B10B538B7E5af4CbFd93B1C4d36668e8F6F644", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -199,6 +211,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -215,6 +228,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "0x09d43904C8ABd470df1B793df68904A9714558CF", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -231,6 +245,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -247,6 +262,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -263,6 +279,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -279,6 +296,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -295,6 +313,7 @@ "gasReceiver": "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", + "managedRatiosOracle": "", "priceFeedImplementation": "", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", From a7b0f6f03addbb382bd5d870c86b222932e43142 Mon Sep 17 00:00:00 2001 From: rbajollari Date: Fri, 11 Oct 2024 20:15:02 -0400 Subject: [PATCH 6/7] immutable fix --- contracts/mellowpricefeed/MellowPriceFeed.sol | 2 +- mainnet_chains.json | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/mellowpricefeed/MellowPriceFeed.sol b/contracts/mellowpricefeed/MellowPriceFeed.sol index fbf473c..f6494a1 100644 --- a/contracts/mellowpricefeed/MellowPriceFeed.sol +++ b/contracts/mellowpricefeed/MellowPriceFeed.sol @@ -15,7 +15,7 @@ contract MellowPriceFeed is Initializable, AggregatorV3Interface { string private priceFeedQuote; - IManagedRatiosOracle public managedRatiosOracle; + IManagedRatiosOracle public immutable managedRatiosOracle; address public vault; diff --git a/mainnet_chains.json b/mainnet_chains.json index 489e6b5..1e328b3 100644 --- a/mainnet_chains.json +++ b/mainnet_chains.json @@ -25,7 +25,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", - "managedRatiosOracle": "0x955Ff4Cc738cDC009d2903196d1c94C8Cfb4D55d", + "managedRatiosOracle": "", "priceFeedImplementation": "0xfaC9d315b9b558e10eBdb0462aA42577aADe6601", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", @@ -59,7 +59,7 @@ "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", "create2Deployer": "0x98b2920d53612483f91f12ed7754e51b4a77919e", - "managedRatiosOracle": "", + "managedRatiosOracle": "0x955Ff4Cc738cDC009d2903196d1c94C8Cfb4D55d", "priceFeedImplementation": "0xde471274F1B684476d341eB131224F389AD4A270", "priceFeedQuotedImplementation": "", "mellowPriceFeedImplementation": "", From cd12982977f5fb5933e115fba0dfa7b23c84763c Mon Sep 17 00:00:00 2001 From: rbajollari Date: Fri, 11 Oct 2024 20:24:08 -0400 Subject: [PATCH 7/7] update mainnet chains addresses --- mainnet_chains.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mainnet_chains.json b/mainnet_chains.json index 1e328b3..61e6607 100644 --- a/mainnet_chains.json +++ b/mainnet_chains.json @@ -54,7 +54,7 @@ "name": "Ethereum", "chainId": 1, "gateway": "0x4F4495243837681061C4743b74B3eEdf548D56A5", - "rpc": "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161", + "rpc": "https://mainnet.infura.io/v3/55b264f8825c40fbb14833c75b453ded", "tokenSymbol": "ETH", "gasReceiver": "0x2d5d7d31F671F86C782533cc367F14109a082712", "ojoContract": "0x5BB3E85f91D08fe92a3D123EE35050b763D6E6A7", @@ -62,9 +62,9 @@ "managedRatiosOracle": "0x955Ff4Cc738cDC009d2903196d1c94C8Cfb4D55d", "priceFeedImplementation": "0xde471274F1B684476d341eB131224F389AD4A270", "priceFeedQuotedImplementation": "", - "mellowPriceFeedImplementation": "", + "mellowPriceFeedImplementation": "0xd93274d286574Dab0BA6D8363F0F31E74df5814c", "cloneFactory": "0x710C8a3c8CB393cA24748849de3585b5C48D4D0c", "cloneFactoryQuoted": "", - "cloneFactoryMellow": "" + "cloneFactoryMellow": "0x48B10B538B7E5af4CbFd93B1C4d36668e8F6F644" } ]