MorphoStrategyWrapper

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/wrappers/MorphoStrategyWrapper.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {StrategyWrapper, IERC20Metadata} from "src/wrappers/StrategyWrapper.sol";
import {IMorpho, Id} from "@shared/src/interfaces/IMorpho.sol";
import {IRewardVault} from "src/interfaces/IRewardVault.sol";

contract MorphoStrategyWrapper is StrategyWrapper {
    constructor(IRewardVault rewardVault, address lendingProtocol, address _owner)
        StrategyWrapper(rewardVault, lendingProtocol, _owner)
    {}

    function initialize(bytes32 marketId) public override onlyOwner {
        super.initialize(marketId);

        require(
            IMorpho(LENDING_PROTOCOL).idToMarketParams(Id.wrap(marketId)).collateralToken == address(this),
            InvalidMarket()
        );
    }

    ///////////////////////////////////////////////////////////////
    // --- INTERNAL FUNCTIONS
    ///////////////////////////////////////////////////////////////

    function _supplyLendingProtocol(address account, uint256 amount) internal override {
        IMorpho(LENDING_PROTOCOL)
            .supplyCollateral(IMorpho(LENDING_PROTOCOL).idToMarketParams(Id.wrap(lendingMarketId)), amount, account, "");
    }

    function _withdrawLendingProtocol(address account, uint256 amount) internal override {
        IMorpho(LENDING_PROTOCOL)
            .withdrawCollateral(
                IMorpho(LENDING_PROTOCOL).idToMarketParams(Id.wrap(lendingMarketId)), amount, account, account
            );
    }

    function _getWorkingBalance(address account) internal view override returns (uint256) {
        return IMorpho(LENDING_PROTOCOL).position(Id.wrap(lendingMarketId), account).collateral;
    }

    ///////////////////////////////////////////////////////////////
    // --- GETTERS FUNCTIONS
    ///////////////////////////////////////////////////////////////

    function name() public view override onlyInitialized returns (string memory) {
        return string.concat("Stake DAO ", IERC20Metadata(REWARD_VAULT.asset()).name());
    }

    function symbol() public view override onlyInitialized returns (string memory) {
        return string.concat("stakedao-", IERC20Metadata(REWARD_VAULT.asset()).symbol());
    }
}
"
    },
    "src/wrappers/StrategyWrapper.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {IRewardVault} from "src/interfaces/IRewardVault.sol";
import {ReentrancyGuardTransient} from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import {IStrategyWrapper} from "src/interfaces/IStrategyWrapper.sol";

interface IAccountant {
    /// @notice Tracks reward and supply data for each vault.
    /// @dev    Packed struct to minimize storage costs (3 storage slots).
    ///         This structure maintains the core accounting state for reward distribution.
    struct VaultData {
        uint256 integral; // Cumulative reward per token (scaled by SCALING_FACTOR)
        uint128 supply; // Total supply of vault tokens
        uint128 feeSubjectAmount; // Amount of rewards subject to fees
        uint128 totalAmount; // Total reward amount including fee-exempt rewards
        uint128 netCredited; // Net rewards already credited to users (after fees)
        uint128 reservedHarvestFee; // Harvest fees reserved but not yet paid out
        uint128 reservedProtocolFee; // Protocol fees reserved but not yet accrued
    }

    /// @notice Tracks individual user positions within a vault.
    /// @dev    Integral tracking enables O(1) reward calculations by storing the last known integral
    ///         value for each user, allowing efficient computation of rewards earned since last update.
    struct AccountData {
        uint128 balance; // User's token balance in the vault
        uint256 integral; // Last integral value when user's rewards were updated
        uint256 pendingRewards; // Rewards earned but not yet claimed
    }

    function claim(address[] calldata _gauges, bytes[] calldata harvestData) external;
    function vaults(address vault) external view returns (VaultData memory);
    function accounts(address vault, address account) external view returns (AccountData memory);
    function REWARD_TOKEN() external view returns (address);
    function SCALING_FACTOR() external view returns (uint128);
}

/// @title Stake DAO Strategy Wrapper
/// @notice Non-transferable ERC20 wrapper for Stake DAO RewardVault shares. It is designed for use as collateral in lending markets.
/// @dev   - Allows users to deposit RewardVault shares or underlying LP tokens, and receive non-transferable tokens (1:1 ratio)
///        - While the ERC20 tokens are held (even as collateral in lending markets), users can claim both main protocol rewards and extra rewards
///        - Handles the edge case where the main reward token is also listed as an extra reward
///        - Integrates with Stake DAO's reward and checkpointing logic to ensure users always receive the correct rewards, regardless of the custody
/// @dev   CRITICAL SECURITY ASSUMPTION: This contract assumes that no external
///        contract can claim rewards on behalf of this contract. The RewardVault
///        and Accountant must never implement claim-on-behalf-of functionality
///        that could target this contract, as it would break internal accounting.
/// @author Stake DAO
/// @custom:contact contact@stakedao.org
/// @custom:github https://github.com/stake-dao/contracts-monorepo
contract StrategyWrapper is ERC20, IStrategyWrapper, Ownable, ReentrancyGuardTransient {
    using SafeERC20 for IERC20;

    ///////////////////////////////////////////////////////////////
    // --- IMMUTABLES/CONSTANTS
    ///////////////////////////////////////////////////////////////

    /// @notice The slot of the main reward token in the user checkpoint
    /// @dev    In order to optimize the storage, the main reward token is tracked in the same
    ///         mapping as the extra reward tokens by using the `address(0)` slot.
    ///         This internal value that is never supposed to be exposed must be used to read/write
    ///         the main reward token state.
    /// @dev    `address(0)` has been chosen to avoid collisions with the extra reward tokens.
    address internal constant MAIN_REWARD_TOKEN_SLOT = address(0);

    /// @notice Precision factor for reward calculations (18 decimals)
    uint256 internal constant PRECISION = 1e18;

    /// @notice Gauge backing the wrapped RewardVault
    address internal immutable GAUGE;

    /// @notice The address of the accountant contract
    IAccountant internal immutable ACCOUNTANT;

    /// @dev Precision used by Accountant.integral (usually 1e27)
    uint128 internal immutable ACCOUNTANT_SCALING_FACTOR;

    /// @notice The address of the token that is being wrapped
    IRewardVault public immutable REWARD_VAULT;

    /// @notice The address of the lending protocol
    address public immutable LENDING_PROTOCOL;

    /// @notice Reward token emitted by the Accountant (e.g. CRV)
    IERC20 public immutable MAIN_REWARD_TOKEN;

    /// @notice Tracks user deposit data and reward checkpoints
    /// @param balance User's wrapped-share balance
    /// @param rewardPerTokenPaid Last per-token accumulator seen
    struct UserCheckpoint {
        uint256 balance;
        mapping(address token => uint256 amount) rewardPerTokenPaid;
    }

    /// @notice The ID of the lending market this token is expected to interact with
    bytes32 public lendingMarketId;

    /// @notice User checkpoint data including his deposit balance and the amount of rewards he has claimed for each token
    ///         Both the main reward token and the extra reward tokens are tracked in the same mapping.
    /// @dev    The main reward token is tracked in `rewardPerTokenPaid[address(0)]`
    mapping(address user => UserCheckpoint checkpoint) private userCheckpoints;

    /// @notice Accumulated rewards per token. Track the extra reward tokens only.
    /// @dev    The main reward token is tracked by fetching the latest value from the Accountant.
    mapping(address token => uint256 amount) public extraRewardPerToken;

    /// @dev The purpose of this transient variable is to ensure that this token can only be
    /// used as collateral in the predefined lending market. When this variable is set to
    /// false (the default value), transfers to the lending protocol are unauthorized.
    /// The only way to change this variable to true is by entering the flow of the deposit
    /// functions in this contract.
    bool private transient depositUnlocked;

    ///////////////////////////////////////////////////////////////
    // --- EVENTS/ERRORS
    ///////////////////////////////////////////////////////////////

    event Claimed(address indexed token, address indexed account, uint256 amount);
    event Deposited(address indexed user, uint256 amount, bytes32 marketId);
    event Withdrawn(address indexed user, uint256 amount);
    event Liquidated(address indexed liquidator, address indexed victim, uint256 amount);
    event MarketAdded(bytes32 marketId);

    error ZeroAmount();
    error ZeroAddress();
    error InvalidTokens();
    error InvalidMarket();
    error MarketAlreadySet();
    error WrapperNotInitialized();
    error InvalidLiquidation();
    /// @dev Only the lending protocol can transfer in/out the wrapped tokens
    error TransferUnauthorized();
    /// @dev Deposit collateral to the lending protocol is only allowed through the StrategyWrapperContract
    ///      Do not interact directly with the lending protocol to deposit collateral. Instead, call one of the `deposit`
    ///      functions defined in the StrategyWrapperContract instead.
    error InvalidDepositFlow();
    /// @dev The caller must call `setAuthorization` on the lending protocol to authorize this contract to withdraw on behalf of the caller
    error NotAuthorizedToWithdrawCollateral();

    /// @param rewardVault The reward vault contract
    /// @param lendingProtocol The lending protocol contract
    /// @param _owner The owner of the contract
    /// @custom:reverts ZeroAddress if the reward vault or _owner address are the zero address
    constructor(IRewardVault rewardVault, address lendingProtocol, address _owner) ERC20("", "") Ownable(_owner) {
        require(lendingProtocol != address(0), ZeroAddress());

        IAccountant accountant = IAccountant(address(rewardVault.ACCOUNTANT()));

        // Store the immutable variables
        GAUGE = rewardVault.gauge();
        ACCOUNTANT = accountant;
        REWARD_VAULT = rewardVault;
        MAIN_REWARD_TOKEN = IERC20(accountant.REWARD_TOKEN());
        ACCOUNTANT_SCALING_FACTOR = accountant.SCALING_FACTOR();
        LENDING_PROTOCOL = lendingProtocol;

        // Approve the asset token to the reward vault and the wrapped token to the lending protocol
        IERC20(rewardVault.asset()).approve(address(rewardVault), type(uint256).max);
        _approve(address(this), lendingProtocol, type(uint256).max, true);
    }

    ///////////////////////////////////////////////////////////////
    // --- INITIALIZATION
    ///////////////////////////////////////////////////////////////

    /// @notice Set the market ID for the wrapper
    /// @param marketId The ID of the market to set
    /// @custom:reverts MarketAlreadySet if the market ID is already set
    /// @custom:reverts InvalidMarket if the market ID is the invalid
    function initialize(bytes32 marketId) public virtual onlyOwner {
        require(lendingMarketId == bytes32(0), MarketAlreadySet());
        require(marketId != bytes32(0), InvalidMarket());

        lendingMarketId = marketId;
        emit MarketAdded(marketId);
    }

    modifier onlyInitialized() {
        require(lendingMarketId != bytes32(0), WrapperNotInitialized());
        _;
    }

    ///////////////////////////////////////////////////////////////
    // --- DEPOSIT
    ///////////////////////////////////////////////////////////////

    /// @notice Wrap **all** RewardVault shares owned by the caller into wrapper tokens (1:1 ratio)
    /// @dev Use this when you ALREADY hold RewardVault shares.
    ///      If you already have wrapped tokens, any pending main and extra rewards will be
    ///      automatically claimed before the new deposit to prevent reward loss.
    /// @custom:reverts ZeroAmount If the caller's share balance is zero
    function depositShares() external {
        depositShares(REWARD_VAULT.balanceOf(msg.sender));
    }

    /// @notice Wrap `amount` RewardVault shares into wrapper tokens (1:1 ratio)
    /// @param amount Number of RewardVault shares the caller wants to wrap
    /// @dev Use this when you ALREADY hold RewardVault shares and wish to wrap a specific portion of them.
    ///      If you already have wrapped tokens, any pending main and extra rewards will be
    ///      automatically claimed before the new deposit to prevent reward loss.
    /// @custom:reverts ZeroAmount If `amount` is zero
    function depositShares(uint256 amount) public nonReentrant onlyInitialized {
        require(amount > 0, ZeroAmount());

        // 1. Transfer caller's shares to this contract (checkpoint)
        IERC20(address(REWARD_VAULT)).safeTransferFrom(msg.sender, address(this), amount);

        _deposit(msg.sender, amount);
    }

    /// @notice Convert **all** underlying LP tokens owned by the caller into RewardVault
    ///         shares and immediately wrap those shares into wrapper tokens (LP → share → wrapper)
    /// @dev Use this when you DO NOT own RewardVault shares yet, only the raw LP tokens.
    ///      If you already have wrapped tokens, any pending main and extra rewards will be
    ///      automatically claimed before the new deposit to prevent reward loss.
    /// @custom:reverts ZeroAmount If the caller's LP balance is zero
    function depositAssets() external {
        depositAssets(IERC20(REWARD_VAULT.asset()).balanceOf(msg.sender));
    }

    /// @notice Convert `amount` underlying LP tokens into RewardVault shares and
    ///         immediately wrap those shares into wrapper tokens (LP → share → wrapper)
    /// @param amount Amount of underlying LP tokens provided by the caller
    /// @dev Use this when you DO NOT own RewardVault shares yet, only the raw LP tokens,
    ///      and wish to wrap a specific portion of them.
    ///      If you already have wrapped tokens, any pending main and extra rewards will be
    ///      automatically claimed before the new deposit to prevent reward loss.
    /// @custom:reverts ZeroAmount If `amount` is zero
    function depositAssets(uint256 amount) public nonReentrant onlyInitialized {
        require(amount > 0, ZeroAmount());

        // 1. Get RewardVault's shares by depositing the underlying protocol LP tokens (checkpoint)
        IERC20(REWARD_VAULT.asset()).safeTransferFrom(msg.sender, address(this), amount);
        uint256 shares = REWARD_VAULT.deposit(amount, address(this), address(this));

        _deposit(msg.sender, shares);
    }

    function _deposit(address account, uint256 amount) internal {
        // 1. Update the user checkpoint
        _updateUserCheckpoint(account, amount);

        // 2. Mint wrapped tokens (1:1)
        _update(address(0), address(this), amount);

        // 3. Supply the minted tokens as collateral to the expected lending market
        depositUnlocked = true;
        _supplyLendingProtocol(account, amount);
        depositUnlocked = false;

        emit Deposited(account, amount, lendingMarketId);
    }

    ///////////////////////////////////////////////////////////////
    // --- WITHDRAW
    ///////////////////////////////////////////////////////////////

    /// @notice Withdraws the collateral directly from the lending protocol for the caller
    /// @param amount Amount of wrapped tokens put in collateral to withdraw
    /// @custom:reverts NotAuthorizedToWithdrawCollateral if this contract is not authorized to withdraw
    ///                 from the lending protocol on behalf of the caller
    ///                 The caller must call `setAuthorization` on the lending protocol to authorize this
    ///                 contract to withdraw on behalf of the caller
    function withdrawCollateral(uint256 amount) external virtual {
        // Withdraw the collateral from the lending protocol for the caller
        _withdrawLendingProtocol(msg.sender, amount);
        withdraw(amount);
    }

    /// @notice Withdraws all the assets and claims pending rewards for the caller
    function withdraw() external {
        withdraw(balanceOf(msg.sender));
    }

    /// @notice Withdraws the given amount of assets and claims main/extra pending rewards
    /// @param amount Amount of wrapped tokens to burn
    /// @custom:reverts ZeroAmount if the given amount is zero
    function withdraw(uint256 amount) public nonReentrant {
        require(amount > 0, ZeroAmount());

        // 1. Claim all the pending rewards (main + extra)
        _claimAllRewards(msg.sender);

        // 2. Update the internal user checkpoint
        UserCheckpoint storage checkpoint = userCheckpoints[msg.sender];
        checkpoint.balance -= amount;

        // 4. Burn caller's wrapped tokens
        _burn(msg.sender, amount);

        // 5. Transfer the underlying RewardVault shares back to the caller
        IERC20(address(REWARD_VAULT)).safeTransfer(msg.sender, amount);

        emit Withdrawn(msg.sender, amount);
    }

    ///////////////////////////////////////////////////////////////
    // --- CLAIM
    ///////////////////////////////////////////////////////////////

    /// @notice Claims the caller's main reward token for himself
    /// @return amount Amount of main reward tokens claimed
    function claim() external nonReentrant returns (uint256 amount) {
        return _claim(msg.sender);
    }

    function _claim(address account) internal returns (uint256 amount) {
        require(account != address(0), ZeroAddress());

        // 1. Pull the latest data from the Accountant and the total supply of the wrapper
        IAccountant.AccountData memory wrapper = ACCOUNTANT.accounts(address(REWARD_VAULT), address(this));
        uint256 globalIntegral = _getGlobalIntegral();
        uint256 supply = totalSupply();

        // 2. Calculate the pending rewards for the wrapper
        uint256 claimable =
            wrapper.pendingRewards + Math.mulDiv(supply, globalIntegral - wrapper.integral, ACCOUNTANT_SCALING_FACTOR);

        // 3. If there are some pending rewards, claim them
        if (claimable > 0) {
            address[] memory gauges = new address[](1);
            gauges[0] = GAUGE;
            ACCOUNTANT.claim(gauges, new bytes[](0));
        }

        // 4. Transfer the part of the pending rewards that belongs to the account
        UserCheckpoint storage userCheckpoint = userCheckpoints[account];
        amount = _calculatePendingRewards(userCheckpoint);
        if (amount != 0) {
            userCheckpoint.rewardPerTokenPaid[MAIN_REWARD_TOKEN_SLOT] = globalIntegral;
            // ! safeTransfer would allow the account to block liquidation
            MAIN_REWARD_TOKEN.transfer(account, amount);
            emit Claimed(address(MAIN_REWARD_TOKEN), account, amount);
        }
    }

    /// @notice Claims all the caller's extra reward tokens for himself
    /// @return amounts Array of claimed amounts
    function claimExtraRewards() external nonReentrant returns (uint256[] memory amounts) {
        return _claimExtraRewards(REWARD_VAULT.getRewardTokens(), msg.sender);
    }

    /// @notice Claims the rewards for the given extra reward tokens for the caller
    /// @param tokens Array of extra reward tokens to claim
    /// @return amounts Array of claimed amounts
    function claimExtraRewards(address[] calldata tokens) external nonReentrant returns (uint256[] memory amounts) {
        return _claimExtraRewards(tokens, msg.sender);
    }

    function _claimExtraRewards(address[] memory tokens, address account) internal returns (uint256[] memory amounts) {
        require(tokens.length > 0, InvalidTokens());
        require(account != address(0), ZeroAddress());

        // 1. Update the reward state for all the extra reward tokens
        _updateExtraRewardState(tokens);

        amounts = new uint256[](tokens.length);
        UserCheckpoint storage checkpoint = userCheckpoints[account];

        // 2. Calculate the pending rewards for each extra reward token
        for (uint256 i; i < tokens.length; i++) {
            address token = tokens[i];
            uint256 reward = _calculatePendingExtraRewards(checkpoint, token);

            // 3. If there are pending rewards, update the user's checkpoint and transfer the rewards to the account
            if (reward > 0) {
                checkpoint.rewardPerTokenPaid[token] = extraRewardPerToken[token];
                amounts[i] = reward; // Store the claimed amount
                // ! safeTransfer would allow the account to block liquidation
                ERC20(token).transfer(account, reward);
                emit Claimed(token, account, reward);
            }
        }

        return amounts;
    }

    /// @notice Claims pending main + extra rewards for the account
    function _claimAllRewards(address account) internal {
        _claim(account);
        _claimExtraRewards(REWARD_VAULT.getRewardTokens(), account);
    }

    ///////////////////////////////////////////////////////////////
    // --- INTERNAL FUNCTIONS
    ///////////////////////////////////////////////////////////////

    /// @notice Updates the user checkpoint with the new deposit amount
    function _updateUserCheckpoint(address account, uint256 amount) internal {
        // 1. Update the internal account checkpoint
        UserCheckpoint storage checkpoint = userCheckpoints[account];
        bool hasExistingBalance = checkpoint.balance != 0;

        // 2. For existing users, claim rewards first (this already updates checkpoints)
        if (hasExistingBalance) _claimAllRewards(account);

        checkpoint.balance += amount;

        // 3. Set initial checkpoints for new users
        if (!hasExistingBalance) {
            // Keep track of the Main reward token checkpoint
            checkpoint.rewardPerTokenPaid[MAIN_REWARD_TOKEN_SLOT] = _getGlobalIntegral();

            // Keep track of the Extra reward tokens checkpoints at deposit time
            address[] memory rewardTokens = REWARD_VAULT.getRewardTokens();
            _updateExtraRewardState(rewardTokens);
            for (uint256 i; i < rewardTokens.length; i++) {
                checkpoint.rewardPerTokenPaid[rewardTokens[i]] = extraRewardPerToken[rewardTokens[i]];
            }
        }
    }

    /// @notice Calculates the pending amount of main reward token for a user
    /// @param checkpoint The user checkpoint
    /// @return The amount of pending main reward token
    function _calculatePendingRewards(UserCheckpoint storage checkpoint) internal view returns (uint256) {
        uint256 globalIntegral = _getGlobalIntegral();
        uint256 userRewardPerTokenPaid = checkpoint.rewardPerTokenPaid[MAIN_REWARD_TOKEN_SLOT];

        return Math.mulDiv(checkpoint.balance, globalIntegral - userRewardPerTokenPaid, ACCOUNTANT_SCALING_FACTOR);
    }

    /// @notice Calculates the pending amount of a specific extra reward token for a user
    /// @param checkpoint The user checkpoint
    /// @param token The address of the extra reward token
    /// @return The amount of pending extra reward token
    function _calculatePendingExtraRewards(UserCheckpoint storage checkpoint, address token)
        internal
        view
        returns (uint256)
    {
        uint256 supply = totalSupply();
        if (supply == 0) return 0;

        uint256 currentRewardPerToken = extraRewardPerToken[token];
        uint256 newRewards = uint256(REWARD_VAULT.earned(address(this), token));
        if (newRewards > 0) currentRewardPerToken += Math.mulDiv(newRewards, PRECISION, supply);

        uint256 userRewardPerTokenPaid = checkpoint.rewardPerTokenPaid[token];
        return Math.mulDiv(checkpoint.balance, currentRewardPerToken - userRewardPerTokenPaid, PRECISION);
    }

    /// @notice Updates the reward state for all the extra reward tokens
    /// @dev    Sweeping hypothetical sleeping rewards would require supply to be non-zero due to the safe early return
    /// @param tokens Array of extra reward tokens to update
    function _updateExtraRewardState(address[] memory tokens) internal {
        // 1. Get the total supply of the wrapped tokens (shares)
        uint256 supply = totalSupply();
        if (supply == 0) return;

        // 2. Claim the rewards from the RewardVault
        uint256[] memory amounts = REWARD_VAULT.claim(tokens, address(this));

        // 3. Update the stored rewards for each extra reward token
        for (uint256 i; i < tokens.length; i++) {
            uint256 amount = amounts[i];
            if (amount > 0) extraRewardPerToken[tokens[i]] += Math.mulDiv(amount, PRECISION, supply);
        }
    }

    /// @notice Get the global integral from the Accountant
    /// @return integral The global integral for the RewardVault as stored in the Accountant
    function _getGlobalIntegral() internal view returns (uint256 integral) {
        integral = ACCOUNTANT.vaults(address(REWARD_VAULT)).integral;
    }

    ///////////////////////////////////////////////////////////////
    // --- TRANSFER
    ///////////////////////////////////////////////////////////////

    /// @dev Only the lending protocol can transfer in/out the wrapped tokens
    function transfer(address to, uint256 value) public virtual override(IERC20, ERC20) returns (bool) {
        require(msg.sender == LENDING_PROTOCOL, TransferUnauthorized());
        return super.transfer(to, value);
    }

    /// @dev Only the lending protocol can transfer in/out the wrapped tokens
    function transferFrom(address from, address to, uint256 value)
        public
        virtual
        override(IERC20, ERC20)
        returns (bool)
    {
        require(msg.sender == LENDING_PROTOCOL, TransferUnauthorized());

        // Only authorize deposits to the lending protocol when the deposit is made through this contract
        if (to == LENDING_PROTOCOL) require(depositUnlocked, InvalidDepositFlow());

        return super.transferFrom(from, to, value);
    }

    ///////////////////////////////////////////////////////////////
    // --- LIQUIDATION
    ///////////////////////////////////////////////////////////////

    /// @notice Claims liquidation rights after receiving tokens from a liquidation event on the lending protocol
    /// @param liquidator The address that received the liquidated tokens from Morpho
    /// @param victim The address whose position was liquidated
    /// @param liquidatedAmount The exact amount of tokens that were seized during liquidation
    ///
    /// @dev This function MUST be called by liquidators after receiving wrapped tokens from a liquidation to receive
    ///      the liquidated amount of underlying RewardVault shares.
    ///      Without calling this function, liquidators will:
    ///      - Be unable to withdraw tokens (will revert due to insufficient internal balance)
    ///      - Leave the victim earning rewards on tokens they no longer own
    /// Example:
    ///   1. Alice has 1000 wrapped tokens used as collateral in Morpho
    ///   2. Bob liquidates Alice's position, receiving 300 tokens from Morpho
    ///   3. Bob's state: ERC20 balance = 300, internal balance = 0 (off-track!)
    ///   4. Bob calls: claimLiquidation(bob, alice, 300)
    ///   5. Result: Bob received 300 underlying RewardVault shares, Alice stops earning on liquidated amount
    ///
    /// @dev This function must also be called if you withdraw your collateral on the lending protocol to a
    /// different receiver. We advise against this scenario and recommend withdrawing your collateral to the
    /// same receiver who deposited it. However, if you choose to proceed, you must call this function to
    /// re-synchronize the internal accountability. In this case, the liquidator will be the receiver of
    /// the collateral, while the victim will be the one who deposited it. From the perspective of this
    /// contract, withdrawing to a different receiver is equivalent to liquidation.
    /// Example:
    ///   1. Alice has 1000 wrapped tokens used as collateral in Morpho
    ///   2. Alice withdraws her collateral to Bob, receiving 1000 tokens from Morpho
    ///   3. Bob's state: ERC20 balance = 1000, internal balance = 0 (off-track!)
    ///   4. Bob calls: claimLiquidation(bob, alice, 1000)
    ///   5. Result: Bob received 1000 underlying RewardVault shares, Alice stops earning on liquidated amount
    function claimLiquidation(address liquidator, address victim, uint256 liquidatedAmount) external nonReentrant {
        require(liquidator != address(0) && victim != address(0), ZeroAddress());
        require(liquidatedAmount > 0, ZeroAmount());

        // 1. Check that the liquidator own the claimed amount and that it's off-track
        //    Because token is untransferable (except by the lending protocol), the only way
        //    to have an off-track balance is to get it from liquidation executed by the lending protocol.
        //    We have to be sure the claimed amount is off-track, otherwise any holders will be able to liquidate any positions.
        require(balanceOf(liquidator) >= userCheckpoints[liquidator].balance + liquidatedAmount, InvalidLiquidation());

        // 2. Check that the victim has enough tokens registered internally
        uint256 internalVictimBalance = userCheckpoints[victim].balance;
        require(internalVictimBalance >= liquidatedAmount, InvalidLiquidation());

        // 3. Check that the victim as a real holding hole of at least the claimed amount
        //    This is done by summing up the collateral supplied by the victim with his balance.
        //    Because token is untransferable (except by the lending protocol), the only way
        //    to have an off-track balance is to get liquidated.
        uint256 victimBalance = balanceOf(victim) + _getWorkingBalance(victim);
        require(internalVictimBalance - victimBalance >= liquidatedAmount, InvalidLiquidation());

        // 4. Claim the accrued rewards for the victim and reduce his internal balance
        _claimAllRewards(victim);
        userCheckpoints[victim].balance -= liquidatedAmount;

        // 5. Burn the liquidated amount and transfer the underlying RewardVault shares back to the liquidator
        _burn(liquidator, liquidatedAmount);
        IERC20(address(REWARD_VAULT)).safeTransfer(liquidator, liquidatedAmount);

        emit Liquidated(liquidator, victim, liquidatedAmount);
    }

    ///////////////////////////////////////////////////////////////
    // --- GETTERS
    ///////////////////////////////////////////////////////////////

    /// @notice Calculate the pending amount of main reward token for a user
    /// @param user The address of the user
    /// @return rewards The amount of pending main reward token
    function getPendingRewards(address user) external view returns (uint256 rewards) {
        UserCheckpoint storage checkpoint = userCheckpoints[user];
        rewards = _calculatePendingRewards(checkpoint);
    }

    /// @notice Calculate the pending amount of all the extra reward tokens for a user.
    /// @param user The address of the user
    /// @return rewards The amount of pending rewards
    function getPendingExtraRewards(address user) external view returns (uint256[] memory rewards) {
        address[] memory extraRewardTokens = REWARD_VAULT.getRewardTokens();
        UserCheckpoint storage checkpoint = userCheckpoints[user];
        rewards = new uint256[](extraRewardTokens.length);

        for (uint256 i; i < extraRewardTokens.length; i++) {
            rewards[i] = _calculatePendingExtraRewards(checkpoint, extraRewardTokens[i]);
        }
    }

    /// @notice Calculate the pending amount of a specific extra reward token for a user.
    ///         If you're looking for the pending amount of the main reward token, call `getPendingRewards` instead.
    /// @dev    The only reason to pass the address of the main reward token to this function is if
    ///         the main reward token is also listed as an extra reward token. It can happen in some cases.
    /// @param user The address of the user
    /// @param token The address of the extra reward token
    /// @return rewards The amount of pending rewards
    /// @custom:reverts ZeroAddress if the token address is the main reward token address (address(0))
    function getPendingExtraRewards(address user, address token) external view returns (uint256 rewards) {
        require(token != MAIN_REWARD_TOKEN_SLOT, ZeroAddress());

        UserCheckpoint storage checkpoint = userCheckpoints[user];
        rewards = _calculatePendingExtraRewards(checkpoint, token);
    }

    /// @notice Get the decimals of the wrapped token
    /// @return The decimals of the wrapped token
    function decimals() public view override(IERC20Metadata, ERC20) returns (uint8) {
        return REWARD_VAULT.decimals();
    }

    ///////////////////////////////////////////////////////////////
    // --- VIRTUAL FUNCTIONS
    ///////////////////////////////////////////////////////////////

    function _supplyLendingProtocol(address account, uint256 amount) internal virtual {}
    function _withdrawLendingProtocol(address account, uint256 amount) internal virtual {}
    function _getWorkingBalance(address account) internal view virtual returns (uint256) {}
    function name() public view virtual override(IERC20Metadata, ERC20) onlyInitialized returns (string memory) {}
    function symbol() public view virtual override(IERC20Metadata, ERC20) onlyInitialized returns (string memory) {}
}
"
    },
    "node_modules/@stake-dao/shared/src/interfaces/IMorpho.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

type Id is bytes32;

struct MarketParams {
    address loanToken;
    address collateralToken;
    address oracle;
    address irm;
    uint256 lltv;
}

/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
    uint256 supplyShares;
    uint128 borrowShares;
    uint128 collateral;
}

/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
    uint128 totalSupplyAssets;
    uint128 totalSupplyShares;
    uint128 totalBorrowAssets;
    uint128 totalBorrowShares;
    uint128 lastUpdate;
    uint128 fee;
}

struct Authorization {
    address authorizer;
    address authorized;
    bool isAuthorized;
    uint256 nonce;
    uint256 deadline;
}

struct Signature {
    uint8 v;
    bytes32 r;
    bytes32 s;
}

/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
    /// @notice The EIP-712 domain separator.
    /// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
    /// same chain id and on forks because the domain separator would be the same.
    function DOMAIN_SEPARATOR() external view returns (bytes32);

    /// @notice The owner of the contract.
    /// @dev It has the power to change the owner.
    /// @dev It has the power to set fees on markets and set the fee recipient.
    /// @dev It has the power to enable but not disable IRMs and LLTVs.
    function owner() external view returns (address);

    /// @notice The fee recipient of all markets.
    /// @dev The recipient receives the fees of a given market through a supply position on that market.
    function feeRecipient() external view returns (address);

    /// @notice Whether the `irm` is enabled.
    function isIrmEnabled(address irm) external view returns (bool);

    /// @notice Whether the `lltv` is enabled.
    function isLltvEnabled(uint256 lltv) external view returns (bool);

    /// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
    /// @dev Anyone is authorized to modify their own positions, regardless of this variable.
    function isAuthorized(address authorizer, address authorized) external view returns (bool);

    /// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
    function nonce(address authorizer) external view returns (uint256);

    /// @notice Sets `newOwner` as `owner` of the contract.
    /// @dev Warning: No two-step transfer ownership.
    /// @dev Warning: The owner can be set to the zero address.
    function setOwner(address newOwner) external;

    /// @notice Enables `irm` as a possible IRM for market creation.
    /// @dev Warning: It is not possible to disable an IRM.
    function enableIrm(address irm) external;

    /// @notice Enables `lltv` as a possible LLTV for market creation.
    /// @dev Warning: It is not possible to disable a LLTV.
    function enableLltv(uint256 lltv) external;

    /// @notice Sets the `newFee` for the given market `marketParams`.
    /// @param newFee The new fee, scaled by WAD.
    /// @dev Warning: The recipient can be the zero address.
    function setFee(MarketParams memory marketParams, uint256 newFee) external;

    /// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
    /// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
    /// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
    /// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Creates the market `marketParams`.
    /// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
    /// Morpho behaves as expected:
    /// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
    /// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
    /// burn functions are not supported.
    /// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
    /// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
    /// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
    /// - The IRM should not re-enter Morpho.
    /// - The oracle should return a price with the correct scaling.
    /// - The oracle price should not be able to change instantly such that the new price is less than the old price
    /// multiplied by LLTV*LIF. In particular, if the loan asset is a vault that can receive donations, the oracle
    /// should not price its shares using the AUM.
    /// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
    /// properties (funds could get stuck):
    /// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
    /// - The amount of assets supplied and borrowed should not be too high (max ~1e32), otherwise the number of shares
    /// might not fit within 128 bits.
    /// - The IRM should not revert on `borrowRate`.
    /// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
    /// `_accrueInterest` can overflow).
    /// - The oracle should not revert `price`.
    /// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
    /// `assetsRepaid` in `liquidate` can overflow).
    /// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
    /// the point where `totalBorrowShares` is very large and borrowing overflows.
    function createMarket(MarketParams memory marketParams) external;

    /// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupply` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
    /// amount of shares is given for full compatibility and precision.
    /// @dev Supplying a large amount can revert for overflow.
    /// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to supply assets to.
    /// @param assets The amount of assets to supply.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased supply position.
    /// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
    /// @return assetsSupplied The amount of assets supplied.
    /// @return sharesSupplied The amount of shares minted.
    function supply(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsSupplied, uint256 sharesSupplied);

    /// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
    /// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
    /// conversion roundings between shares and assets.
    /// @param marketParams The market to withdraw assets from.
    /// @param assets The amount of assets to withdraw.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the supply position.
    /// @param receiver The address that will receive the withdrawn assets.
    /// @return assetsWithdrawn The amount of assets withdrawn.
    /// @return sharesWithdrawn The amount of shares burned.
    function withdraw(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);

    /// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
    /// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
    /// given for full compatibility and precision.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Borrowing a large amount can revert for overflow.
    /// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
    /// Consider using the `assets` parameter to avoid this.
    /// @param marketParams The market to borrow assets from.
    /// @param assets The amount of assets to borrow.
    /// @param shares The amount of shares to mint.
    /// @param onBehalf The address that will own the increased borrow position.
    /// @param receiver The address that will receive the borrowed assets.
    /// @return assetsBorrowed The amount of assets borrowed.
    /// @return sharesBorrowed The amount of shares minted.
    function borrow(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        address receiver
    ) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);

    /// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoRepay` function with the given `data`.
    /// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
    /// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
    /// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
    /// roundings between shares and assets.
    /// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
    /// @param marketParams The market to repay assets to.
    /// @param assets The amount of assets to repay.
    /// @param shares The amount of shares to burn.
    /// @param onBehalf The address of the owner of the debt position.
    /// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
    /// @return assetsRepaid The amount of assets repaid.
    /// @return sharesRepaid The amount of shares burned.
    function repay(
        MarketParams memory marketParams,
        uint256 assets,
        uint256 shares,
        address onBehalf,
        bytes memory data
    ) external returns (uint256 assetsRepaid, uint256 sharesRepaid);

    /// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
    /// `onMorphoSupplyCollateral` function with the given `data`.
    /// @dev Interest are not accrued since it's not required and it saves gas.
    /// @dev Supplying a large amount can revert for overflow.
    /// @param marketParams The market to supply collateral to.
    /// @param assets The amount of collateral to supply.
    /// @param onBehalf The address that will own the increased collateral position.
    /// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
    function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
        external;

    /// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
    /// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
    /// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
    /// @param marketParams The market to withdraw collateral from.
    /// @param assets The amount of collateral to withdraw.
    /// @param onBehalf The address of the owner of the collateral position.
    /// @param receiver The address that will receive the collateral assets.
    function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
        external;

    /// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
    /// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
    /// `onMorphoLiquidate` function with the given `data`.
    /// @dev Either `seizedAssets` or `repaidShares` should be zero.
    /// @dev Seizing more than the collateral balance will underflow and revert without any error message.
    /// @dev Repaying more than the borrow balance will underflow and revert without any error message.
    /// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
    /// @param marketParams The market of the position.
    /// @param borrower The owner of the position.
    /// @param seizedAssets The amount of collateral to seize.
    /// @param repaidShares The amount of shares to repay.
    /// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
    /// @return The amount of assets seized.
    /// @return The amount of assets repaid.
    function liquidate(
        MarketParams memory marketParams,
        address borrower,
        uint256 seizedAssets,
        uint256 repaidShares,
        bytes memory data
    ) external returns (uint256, uint256);

    /// @notice Executes a flash loan.
    /// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
    /// markets combined, plus donations).
    /// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
    /// - `flashFee` is zero.
    /// - `maxFlashLoan` is the token's balance of this contract.
    /// - The receiver of `assets` is the caller.
    /// @param token The token to flash loan.
    /// @param assets The amount of assets to flash loan.
    /// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
    function flashLoan(address token, uint256 assets, bytes calldata data) external;

    /// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
    /// @param authorized The authorized address.
    /// @param newIsAuthorized The new authorization status.
    function setAuthorization(address authorized, bool newIsAuthorized) external;

    /// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
    /// @dev Warning: Reverts if the signature has already been submitted.
    /// @dev The signature is malleable, but it has no impact on the security here.
    /// @dev The nonce is passed as argument to be able to revert with a different error message.
    /// @param authorization The `Authorization` struct.
    /// @param signature The signature.
    function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;

    /// @notice Accrues interest for the given market `marketParams`.
    function accrueInterest(MarketParams memory marketParams) external;

    /// @notice Returns the data stored on the different `slots`.
    function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}

/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user)
        external
        view
        returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
    /// accrual.
    function market(Id id)
        external
        view
        returns (
            uint128 totalSupplyAssets,
            uint128 totalSupplyShares,
            uint128 totalBorrowAssets,
            uint128 totalBorrowShares,
            uint128 lastUpdate,
            uint128 fee
        );

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id)
        external
        view
        returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}

/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
    /// @notice The state of the position of `user` on the market corresponding to `id`.
    /// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
    /// accrual.
    function position(Id id, address user) external view returns (Position memory p);

    /// @notice The state of the market corresponding to `id`.
    /// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
    /// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
    /// interest accrual.
    function market(Id id) external view returns (Market memory m);

    /// @notice The market params corresponding to `id`.
    /// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
    /// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
    function idToMarketParams(Id id) external view returns (MarketParams memory);
}

/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to convert a market to its id.
library MarketParamsLib {
    /// @notice The length of the data used to compute the id of a market.
    /// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
    uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;

    /// @notice Returns the id of the market `marketParams`.
    function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
        assembly ("memory-safe") {
            marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
        }
    }
}
"
    },
    "src/interfaces/IRewardVault.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IAccountant} from "src/interfaces/IAccountant.sol";

/// @title IRewardVault
/// @notice Interface for the RewardVault contract
interface IRewardVault is IERC4626 {
    function addRewardToken(address rewardsToken, address distributor) external;

    function depositRewards(address _rewardsToken, uint128 _amount) external;

    function deposit(uint256 assets, address receiver, address referrer) external returns (uint256 shares);

    function deposit(address account, address receiver, uint256 assets, address referrer)
        external
        returns (uint256 shares);

    function claim(address[] calldata tokens, address receiver) external returns (uint256[] memory amounts);

    function claim(address account, address[] calldata tokens, address receiver)
        external
        returns (uint256[] memory amounts);

    function getRewardsDistributor(address token) external view returns (address);

    function getLastUpdateTime(address token) external view returns (uint32);

    function getPeriodFinish(address token) external view returns (uint32);

    function getRewardRate(address token) external view returns (uint128);

    function getRewardPerTokenStored(address token) external view returns (uint128);

    function getRewardPerTokenPaid(address token, address account) external view returns (uint128);

    function getClaimable(address token, address account) external view returns (uint128);

    function getRewardTokens() external view returns (address[] memory);

    function lastTimeRewardApplicable(address token) external view returns (uint256);

    function rewardPerToken(address token) external view returns (uint128);

    function earned(address account, address token) external view returns (uint128);

    function isRewardToken(address rewardToken) external view returns (bool);

    function resumeVault() external;

    function gauge() external view returns (address);

    function ACCOUNTANT() external view returns (IAccountant);

    function checkpoint(address account) external;

    function PROTOCOL_ID() external view returns (bytes4);
}
"
    },
    "node_modules/@openzeppelin/contracts/access/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */

Tags:
ERC20, ERC165, Multisig, Mintable, Liquidity, Yield, Multi-Signature, Factory, Oracle|addr:0xd7a7992989c5b20ef48452c96ac4c6b71a17d61e|verified:true|block:23719264|tx:0xb2b1c399b3934b0799b2fdc5ef47c7cc2a6fbbf7b0e80ab08f693102485ebdbe|first_check:1762182109

Submitted on: 2025-11-03 16:01:50

Comments

Log in to comment.

No comments yet.