ConvexRewardManager

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/rewards/ConvexRewardManager.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { AbstractRewardManager } from "./AbstractRewardManager.sol";
import { RewardPoolStorage } from "../interfaces/IRewardManager.sol";
import { IConvexRewardPool, IConvexBooster } from "../interfaces/Curve/IConvex.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { TokenUtils } from "../utils/TokenUtils.sol";

contract ConvexRewardManager is AbstractRewardManager {
    using TokenUtils for ERC20;

    function _executeClaim() internal override {
        address rewardPool = _getRewardPoolSlot().rewardPool;
        require(IConvexRewardPool(rewardPool).getReward(address(this), true));
    }

    function _withdrawFromPreviousRewardPool(RewardPoolStorage memory oldRewardPool) internal override {
        uint256 boosterBalance = ERC20(oldRewardPool.rewardPool).balanceOf(address(this));
        require(IConvexRewardPool(oldRewardPool.rewardPool).withdrawAndUnwrap(boosterBalance, true));
    }

    function _depositIntoNewRewardPool(
        address poolToken,
        uint256 poolTokens,
        RewardPoolStorage memory newRewardPool
    )
        internal
        override
    {
        uint256 poolId = IConvexRewardPool(newRewardPool.rewardPool).pid();
        address booster = IConvexRewardPool(newRewardPool.rewardPool).operator();
        ERC20(poolToken).checkApprove(booster, type(uint256).max);

        if (poolTokens > 0) {
            require(IConvexBooster(booster).deposit(poolId, poolTokens, true));
        }
    }
}
"
    },
    "src/rewards/AbstractRewardManager.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { ReentrancyGuardTransient } from "@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IRewardManager, RewardPoolStorage, VaultRewardState } from "../interfaces/IRewardManager.sol";
import { IYieldStrategy } from "../interfaces/IYieldStrategy.sol";
import { Unauthorized } from "../interfaces/Errors.sol";
import { SHARE_PRECISION, ADDRESS_REGISTRY, YEAR, VIRTUAL_SHARES } from "../utils/Constants.sol";
import { TypeConvert } from "../utils/TypeConvert.sol";
import { IEIP20NonStandard } from "../interfaces/IEIP20NonStandard.sol";
import { TokenUtils } from "../utils/TokenUtils.sol";

abstract contract AbstractRewardManager is IRewardManager, ReentrancyGuardTransient {
    using TypeConvert for uint256;
    using TokenUtils for ERC20;

    modifier onlyUpgradeAdmin() {
        if (msg.sender != ADDRESS_REGISTRY.upgradeAdmin()) revert Unauthorized(msg.sender);
        _;
    }

    // Uses custom storage slots to avoid collisions with other contracts
    // keccak256("notional.rewardManager.rewardPool")
    uint256 private constant REWARD_POOL_SLOT = 0xb1630c9ab375319506d0354f42326eb3b0f3cdb4a34062a84700ebef1ece57f6;
    // keccak256("notional.rewardManager.vaultRewardState")
    uint256 private constant VAULT_REWARD_STATE_SLOT =
        0x5a7d8c54ba64221684d78e9f27720908d8deb4705b6f1336ef8721b7b4329023;
    // keccak256("notional.rewardManager.accountRewardDebt")
    uint256 private constant ACCOUNT_REWARD_DEBT_SLOT =
        0x61d38dd3c50f8b8521fba47d2736bc704c86cafb37f95969db772cc6ddecd0e5;

    function _getRewardPoolSlot() internal pure returns (RewardPoolStorage storage store) {
        assembly {
            store.slot := REWARD_POOL_SLOT
        }
    }

    function _getVaultRewardStateSlot() internal pure returns (VaultRewardState[] storage store) {
        assembly {
            store.slot := VAULT_REWARD_STATE_SLOT
        }
    }

    function _getAccountRewardDebtSlot()
        internal
        pure
        returns (mapping(address rewardToken => mapping(address account => uint256 rewardDebt)) storage store)
    {
        assembly {
            store.slot := ACCOUNT_REWARD_DEBT_SLOT
        }
    }

    /// @inheritdoc IRewardManager
    function migrateRewardPool(
        address poolToken,
        RewardPoolStorage memory newRewardPool
    )
        external
        override
        onlyUpgradeAdmin
        nonReentrant
    {
        // Claim all rewards from the previous reward pool before withdrawing
        uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
        // Clear the force claim timestamp to ensure that we claim all rewards before migration.
        // This value will be set to a new value at the end  of this method.
        _getRewardPoolSlot().forceClaimAfter = 0;
        _claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
        RewardPoolStorage memory oldRewardPool = _getRewardPoolSlot();

        if (oldRewardPool.rewardPool != address(0)) {
            _withdrawFromPreviousRewardPool(oldRewardPool);

            // Clear approvals on the old pool.
            ERC20(poolToken).checkRevoke(address(oldRewardPool.rewardPool));
        }

        uint256 poolTokens = ERC20(poolToken).balanceOf(address(this));
        _depositIntoNewRewardPool(poolToken, poolTokens, newRewardPool);

        // Set the last claim timestamp to the current block timestamp since we're claiming all the rewards
        // earlier in this method.
        _getRewardPoolSlot().lastClaimTimestamp = uint32(block.timestamp);
        _getRewardPoolSlot().rewardPool = newRewardPool.rewardPool;
        _getRewardPoolSlot().forceClaimAfter = newRewardPool.forceClaimAfter;
    }

    /// @inheritdoc IRewardManager
    function getRewardSettings()
        external
        view
        override
        returns (VaultRewardState[] memory rewardStates, RewardPoolStorage memory rewardPool)
    {
        rewardStates = _getVaultRewardStateSlot();
        rewardPool = _getRewardPoolSlot();
    }

    /// @inheritdoc IRewardManager
    function getRewardDebt(address rewardToken, address account) external view override returns (uint256) {
        return _getAccountRewardDebtSlot()[rewardToken][account];
    }

    /// @inheritdoc IRewardManager
    function updateRewardToken(
        uint256 index,
        address rewardToken,
        uint128 emissionRatePerYear,
        uint32 endTime
    )
        external
        override
        onlyUpgradeAdmin
    {
        uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
        uint256 numRewardStates = _getVaultRewardStateSlot().length;

        if (index < numRewardStates) {
            VaultRewardState memory state = _getVaultRewardStateSlot()[index];
            // Safety check to ensure that the correct token is specified, we can never change the
            // token address once set.
            require(state.rewardToken == rewardToken);
            // Modifies the emission rate on an existing token, direct claims of the token will
            // not be affected by the emission rate.
            // First accumulate under the old regime up to the current time. Even if the previous
            // emissionRatePerYear is zero this will still set the lastAccumulatedTime to the current
            // blockTime.
            _accumulateSecondaryRewardViaEmissionRate(index, state, effectiveSupplyBefore);

            // Save the new emission rates
            state.emissionRatePerYear = emissionRatePerYear;
            if (state.emissionRatePerYear == 0) {
                state.endTime = 0;
            } else {
                require(block.timestamp < endTime);
                state.endTime = endTime;
            }
            _getVaultRewardStateSlot()[index] = state;
        } else if (index == numRewardStates) {
            // This sets a new reward token, ensure that the current slot is empty
            VaultRewardState[] storage states = _getVaultRewardStateSlot();
            // If no emission rate is set then governance is just adding a token that can be claimed
            // via the LP tokens without an emission rate. These settings will be left empty and the
            // subsequent _claimVaultRewards method will set the initial accumulatedRewardPerVaultShare.
            if (0 < emissionRatePerYear) require(block.timestamp < endTime);

            states.push(
                VaultRewardState({
                    rewardToken: rewardToken,
                    lastAccumulatedTime: uint32(block.timestamp),
                    endTime: endTime,
                    emissionRatePerYear: emissionRatePerYear,
                    accumulatedRewardPerVaultShare: 0
                })
            );
        } else {
            // Can only append or modify existing tokens
            revert();
        }

        // Claim all vault rewards up to the current time
        _claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
        emit VaultRewardUpdate(rewardToken, emissionRatePerYear, endTime);
    }

    /// @notice Claims all the rewards for the entire vault and updates the accumulators. Does not
    /// update emission rewarders since those are automatically updated on every account claim.
    function claimRewardTokens() external nonReentrant {
        // This method is not executed from inside enter or exit vault positions, so this total
        // vault shares value is valid.
        uint256 effectiveSupplyBefore = IYieldStrategy(address(this)).effectiveSupply();
        _claimVaultRewards(effectiveSupplyBefore, _getVaultRewardStateSlot());
    }

    /// @inheritdoc IRewardManager
    function updateAccountRewards(
        address account,
        uint256 effectiveSupplyBefore,
        uint256 accountSharesBefore,
        uint256 accountSharesAfter,
        bool sharesInEscrow
    )
        external
        returns (uint256[] memory rewards)
    {
        // Short circuit in this case, no rewards to claim
        if (sharesInEscrow && accountSharesAfter > 0) return rewards;

        VaultRewardState[] memory state = _getVaultRewardStateSlot();
        _claimVaultRewards(effectiveSupplyBefore, state);
        rewards = new uint256[](state.length);

        for (uint256 i; i < state.length; i++) {
            if (sharesInEscrow && accountSharesAfter == 0) {
                delete _getAccountRewardDebtSlot()[state[i].rewardToken][account];
                continue;
            }

            if (0 < state[i].emissionRatePerYear) {
                // Accumulate any rewards with an emission rate here
                _accumulateSecondaryRewardViaEmissionRate(i, state[i], effectiveSupplyBefore);
            }

            rewards[i] = _claimRewardToken(
                state[i].rewardToken,
                account,
                accountSharesBefore,
                accountSharesAfter,
                state[i].accumulatedRewardPerVaultShare
            );
        }
    }

    /// @notice Executes a claim against the given reward pool type and updates internal
    /// rewarder accumulators.
    function _claimVaultRewards(uint256 effectiveSupplyBefore, VaultRewardState[] memory state) internal {
        RewardPoolStorage memory rewardPool = _getRewardPoolSlot();
        if (rewardPool.rewardPool == address(0)) return;
        if (block.timestamp < rewardPool.lastClaimTimestamp + rewardPool.forceClaimAfter) return;

        uint256[] memory balancesBefore = new uint256[](state.length);
        // Run a generic call against the reward pool and then do a balance
        // before and after check.
        for (uint256 i; i < state.length; i++) {
            // Presumes that ETH will never be given out as a reward token.
            balancesBefore[i] = ERC20(state[i].rewardToken).balanceOf(address(this));
        }

        _executeClaim();

        _getRewardPoolSlot().lastClaimTimestamp = uint32(block.timestamp);

        // This only accumulates rewards claimed, it does not accumulate any secondary emissions
        // that are streamed to vault users.
        for (uint256 i; i < state.length; i++) {
            uint256 balanceAfter = ERC20(state[i].rewardToken).balanceOf(address(this));
            _accumulateSecondaryRewardViaClaim(
                i,
                state[i],
                // balanceAfter should never be less than balanceBefore
                balanceAfter - balancesBefore[i],
                effectiveSupplyBefore
            );
        }
    }

    /**
     * Reward Claim Methods *
     */
    function _claimRewardToken(
        address rewardToken,
        address account,
        uint256 accountSharesBefore,
        uint256 accountSharesAfter,
        uint256 rewardsPerVaultShare
    )
        internal
        returns (uint256 rewardToClaim)
    {
        // Vault shares are always in SHARE_PRECISION
        uint256 rewardDebt = _getAccountRewardDebtSlot()[rewardToken][account];
        rewardToClaim = ((accountSharesBefore * rewardsPerVaultShare) / SHARE_PRECISION) - rewardDebt;
        _getAccountRewardDebtSlot()[rewardToken][account] =
            ((accountSharesAfter * rewardsPerVaultShare) / SHARE_PRECISION);

        if (0 < rewardToClaim) {
            // Ignore transfer errors here so that any strange failures here do not
            // prevent normal vault operations from working. Failures may include a
            // lack of balances or some sort of blacklist that prevents an account
            // from receiving tokens.
            if (rewardToken.code.length > 0) {
                try IEIP20NonStandard(rewardToken).transfer(account, rewardToClaim) {
                    bool success = TokenUtils.checkReturnCode();
                    if (success) {
                        emit VaultRewardTransfer(rewardToken, account, rewardToClaim);
                    } else {
                        emit VaultRewardTransfer(rewardToken, account, 0);
                    }
                    // Emits zero tokens transferred if the transfer fails.
                } catch {
                    emit VaultRewardTransfer(rewardToken, account, 0);
                }
            }
        }
    }

    /**
     * ACCUMULATORS  **
     */
    function _accumulateSecondaryRewardViaClaim(
        uint256 index,
        VaultRewardState memory state,
        uint256 tokensClaimed,
        uint256 effectiveSupplyBefore
    )
        private
    {
        if (tokensClaimed == 0) return;

        state.accumulatedRewardPerVaultShare += ((tokensClaimed * SHARE_PRECISION) / effectiveSupplyBefore).toUint128();

        _getVaultRewardStateSlot()[index] = state;
    }

    function _accumulateSecondaryRewardViaEmissionRate(
        uint256 index,
        VaultRewardState memory state,
        uint256 effectiveSupplyBefore
    )
        private
    {
        state.accumulatedRewardPerVaultShare =
            _getAccumulatedRewardViaEmissionRate(state, effectiveSupplyBefore, block.timestamp).toUint128();
        state.lastAccumulatedTime = uint32(block.timestamp);

        _getVaultRewardStateSlot()[index] = state;
    }

    function _getAccumulatedRewardViaEmissionRate(
        VaultRewardState memory state,
        uint256 effectiveSupplyBefore,
        uint256 blockTime
    )
        private
        pure
        returns (uint256)
    {
        // Short circuit the method with no emission rate
        if (state.emissionRatePerYear == 0) return state.accumulatedRewardPerVaultShare;
        require(0 < state.endTime);
        uint256 time = blockTime < state.endTime ? blockTime : state.endTime;

        uint256 additionalIncentiveAccumulatedPerVaultShare;
        if (state.lastAccumulatedTime < time && VIRTUAL_SHARES < effectiveSupplyBefore) {
            // NOTE: no underflow, checked in if statement
            uint256 timeSinceLastAccumulation = time - state.lastAccumulatedTime;
            // Precision here is:
            //  timeSinceLastAccumulation (SECONDS)
            //  emissionRatePerYear (REWARD_TOKEN_PRECISION)
            //  SHARE_PRECISION (1e24)
            // DIVIDE BY
            //  YEAR (SECONDS)
            //  SHARE_PRECISION (1e24)
            // => Precision = REWARD_TOKEN_PRECISION
            additionalIncentiveAccumulatedPerVaultShare = (
                timeSinceLastAccumulation * SHARE_PRECISION * state.emissionRatePerYear
            ) / (YEAR * effectiveSupplyBefore);
        }

        return state.accumulatedRewardPerVaultShare + additionalIncentiveAccumulatedPerVaultShare;
    }

    /// @notice Executes the proper call for various rewarder types.
    function _executeClaim() internal virtual;
    function _withdrawFromPreviousRewardPool(RewardPoolStorage memory oldRewardPool) internal virtual;
    function _depositIntoNewRewardPool(
        address poolToken,
        uint256 poolTokens,
        RewardPoolStorage memory newRewardPool
    )
        internal
        virtual;
}
"
    },
    "src/interfaces/IRewardManager.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

struct VaultRewardState {
    address rewardToken;
    uint32 lastAccumulatedTime;
    uint32 endTime;
    // Slot #2
    // If secondary rewards are enabled, they will be streamed to the accounts via
    // an annual emission rate. If the same reward token is also issued by the LP pool,
    // those tokens will be added on top of the annual emission rate. If the vault is under
    // automatic reinvestment mode, the secondary reward token cannot be sold.
    uint128 emissionRatePerYear; // in internal token precision
    uint128 accumulatedRewardPerVaultShare;
}

enum RewardPoolType {
    _UNUSED,
    AURA,
    CONVEX_MAINNET,
    CONVEX_ARBITRUM
}

struct RewardPoolStorage {
    address rewardPool;
    uint32 lastClaimTimestamp;
    uint32 forceClaimAfter;
}

/// Each reward manager is responsible for claiming rewards for a given protocol.
/// it will be called through a delegatecall from the vault to avoid token transfers
/// of staked tokens.
interface IRewardManager {
    event VaultRewardTransfer(address indexed token, address indexed account, uint256 amount);
    event VaultRewardUpdate(address indexed rewardToken, uint128 emissionRatePerYear, uint32 endTime);

    /// @notice Returns the current reward claim method and reward state
    /// @return rewardStates Array of vault reward states
    /// @return rewardPool Reward pool storage
    function getRewardSettings()
        external
        view
        returns (VaultRewardState[] memory rewardStates, RewardPoolStorage memory rewardPool);

    /// @notice Returns the reward debt for the given reward token and account
    /// @param rewardToken Address of the reward token
    /// @param account Address of the account
    /// @return rewardDebt The reward debt for the account
    function getRewardDebt(address rewardToken, address account) external view returns (uint256 rewardDebt);

    /// @notice Updates account rewards during enter and exit vault operations, only
    /// callable via delegatecall from inside the vault
    /// @param account Address of the account
    /// @param effectiveSupplyBefore Total vault shares before the operation
    /// @param accountSharesBefore Number of shares before the operation
    /// @param accountSharesAfter Number of shares after the operation
    /// @param sharesInEscrow Whether the shares are in escrow
    function updateAccountRewards(
        address account,
        uint256 effectiveSupplyBefore,
        uint256 accountSharesBefore,
        uint256 accountSharesAfter,
        bool sharesInEscrow
    )
        external
        returns (uint256[] memory rewards);

    /// @notice Sets a secondary reward rate for a given token, only callable via the owner
    /// @param index Index of the reward token
    /// @param rewardToken Address of the reward token
    /// @param emissionRatePerYear Emission rate per year for the token
    /// @param endTime End time for the emission rate
    function updateRewardToken(
        uint256 index,
        address rewardToken,
        uint128 emissionRatePerYear,
        uint32 endTime
    )
        external;

    /// @notice Migrates the reward pool to a new reward pool, needs to be called initially
    /// to set the reward pool storage and when the reward pool is updated.
    /// @param poolToken The pool token to migrate
    /// @param newRewardPool The new reward pool storage configuration
    function migrateRewardPool(address poolToken, RewardPoolStorage memory newRewardPool) external;

    /// @notice Claims all the rewards for the entire vault and updates the accumulators
    function claimRewardTokens() external;
}
"
    },
    "src/interfaces/Curve/IConvex.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.28;

interface IConvexRewardPool {
    function withdrawAndUnwrap(uint256 amount, bool claim) external returns (bool);
    function getReward(address _account, bool _claimExtras) external returns (bool);
    function balanceOf(address _account) external view returns (uint256);
    function pid() external view returns (uint256);
    function operator() external view returns (address);
    function extraRewards(uint256 idx) external view returns (address);
    function extraRewardsLength() external view returns (uint256);
}

interface IConvexBooster {
    function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool);
    function stakerRewards() external view returns (address);
}

interface IConvexBoosterArbitrum {
    function deposit(uint256 _pid, uint256 _amount) external returns (bool);
}
"
    },
    "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`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance < type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}
"
    },
    "src/utils/TokenUtils.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ETH_ADDRESS, ALT_ETH_ADDRESS } from "./Constants.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

library TokenUtils {
    using SafeERC20 for ERC20;

    function getDecimals(address token) internal view returns (uint8 decimals) {
        decimals = (token == ETH_ADDRESS || token == ALT_ETH_ADDRESS) ? 18 : ERC20(token).decimals();
        require(decimals <= 18);
    }

    function tokenBalance(address token) internal view returns (uint256) {
        return token == ETH_ADDRESS ? address(this).balance : ERC20(token).balanceOf(address(this));
    }

    function checkApprove(ERC20 token, address spender, uint256 amount) internal {
        if (address(token) == address(0)) return;

        token.forceApprove(spender, amount);
    }

    function checkRevoke(ERC20 token, address spender) internal {
        if (address(token) == address(0)) return;
        token.forceApprove(spender, 0);
    }

    function checkReturnCode() internal pure returns (bool success) {
        uint256[1] memory result;
        assembly {
            switch returndatasize()
            case 0 {
                // This is a non-standard ERC-20
                success := 1 // set success to true
            }
            case 32 {
                // This is a compliant ERC-20
                returndatacopy(result, 0, 32)
                success := mload(result) // Set `success = returndata` of external call
            }
            default {
                // This is an excessively non-compliant ERC-20, revert.
                revert(0, 0)
            }
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/ReentrancyGuardTransient.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuardTransient.sol)

pragma solidity ^0.8.24;

import {TransientSlot} from "./TransientSlot.sol";

/**
 * @dev Variant of {ReentrancyGuard} that uses transient storage.
 *
 * NOTE: This variant only works on networks where EIP-1153 is available.
 *
 * _Available since v5.1._
 */
abstract contract ReentrancyGuardTransient {
    using TransientSlot for *;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
    }

    function _nonReentrantAfter() private {
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
    }
}
"
    },
    "src/interfaces/IYieldStrategy.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { IERC20Metadata } from "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import { IOracle } from "./Morpho/IOracle.sol";

/**
 * @notice A strategy vault that is specifically designed for leveraged yield
 * strategies. Minting and burning shares are restricted to the `enterPosition`
 * and `exitPosition` functions respectively. This means that shares will be
 * exclusively held on lending markets as collateral unless the LendingMarket is
 * set to NONE. In this case, the user will just be holding the yield token without
 * any leverage.
 *
 * The `transfer` function is non-standard in that transfers off of a lending market
 * are restricted to ensure that liquidation conditions are met.
 *
 * This contract also serves as its own oracle.
 */
interface IYieldStrategy is IERC20, IERC20Metadata, IOracle {
    event VaultCreated(address indexed vault);

    // These can be emitted by the reward manager
    event VaultRewardTransfer(address indexed token, address indexed account, uint256 amount);
    event VaultRewardUpdate(address indexed rewardToken, uint128 emissionRatePerYear, uint32 endTime);

    // This is emitted by the trading module
    event TradeExecuted(address indexed sellToken, address indexed buyToken, uint256 sellAmount, uint256 buyAmount);

    event FeesCollected(uint256 feesCollected);

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the address of the accounting asset used for the
     * to mark the price of the yield token excluding any market profit and loss.
     * This is only used for off chain accounting.
     */
    function accountingAsset() external view returns (address accountingAssetAddress);

    /**
     * @dev Returns the name of the strategy.
     */
    function strategy() external view returns (string memory strategyName);

    /**
     * @dev Returns the address of the yield token held by the vault. Does not equal the share token,
     * which represents each user's share of the yield tokens held by the vault.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function yieldToken() external view returns (address yieldTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the effective supply which excludes any escrowed shares.
     */
    function effectiveSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the amount of yield tokens that the Vault would exchange for the amount of shares provided, in an
     * ideal
     * scenario where all the conditions are met.
     */
    function convertSharesToYieldToken(uint256 shares) external view returns (uint256 yieldTokens);

    /**
     * @dev Returns the amount of yield tokens that the account would receive for the amount of shares provided.
     */
    function convertYieldTokenToShares(uint256 shares) external view returns (uint256 yieldTokens);

    /**
     * @dev Returns the oracle price of a yield token in terms of the asset token.
     */
    function convertYieldTokenToAsset() external view returns (uint256 price);

    /**
     * @dev Returns the fee rate of the vault where 100% = 1e18.
     */
    function feeRate() external view returns (uint256 feeRate);

    /**
     * @dev Returns the balance of yield tokens accrued by the vault.
     */
    function feesAccrued() external view returns (uint256 feesAccruedInYieldToken);

    /**
     * @dev Collects the fees accrued by the vault. Only callable by the owner.
     */
    function collectFees() external returns (uint256 feesCollected);

    /**
     * @dev Returns the price of a yield token in terms of the asset token for the
     * given borrower taking into account withdrawals.
     */
    function price(address borrower) external returns (uint256 price);

    /**
     * @notice Mints shares for a given number of assets.
     *
     * @param assets The amount of assets to mint shares for.
     * @param receiver The address to mint the shares to.
     * @param depositData calldata used to deposit the assets.
     */
    function mintShares(
        uint256 assets,
        address receiver,
        bytes memory depositData
    )
        external
        returns (uint256 sharesMinted);

    /**
     * @notice Burns shares for a given number of shares.
     *
     * @param sharesOwner The address of the account to burn the shares for.
     * @param sharesToBurn The amount of shares to burn.
     * @param redeemData calldata used to redeem the yield token.
     */
    function burnShares(
        address sharesOwner,
        uint256 sharesToBurn,
        uint256 sharesHeld,
        bytes memory redeemData
    )
        external
        returns (uint256 assetsWithdrawn);

    /**
     * @notice Allows the lending market to transfer shares on exit position
     * or liquidation.
     *
     * @param to The address to allow the transfer to.
     * @param amount The amount of shares to allow the transfer of.
     * @param currentAccount The address of the current account.
     */
    function allowTransfer(address to, uint256 amount, address currentAccount) external;

    /**
     * @notice Pre-liquidation function.
     *
     * @param liquidator The address of the liquidator.
     * @param liquidateAccount The address of the account to liquidate.
     * @param sharesToLiquidate The amount of shares to liquidate.
     * @param accountSharesHeld The amount of shares the account holds.
     */
    function preLiquidation(
        address liquidator,
        address liquidateAccount,
        uint256 sharesToLiquidate,
        uint256 accountSharesHeld
    )
        external;

    /**
     * @notice Post-liquidation function.
     *
     * @param liquidator The address of the liquidator.
     * @param liquidateAccount The address of the account to liquidate.
     * @param sharesToLiquidator The amount of shares to liquidate.
     */
    function postLiquidation(address liquidator, address liquidateAccount, uint256 sharesToLiquidator) external;

    /**
     * @notice Redeems shares for assets for a native token.
     *
     * @param sharesToRedeem The amount of shares to redeem.
     * @param redeemData calldata used to redeem the yield token.
     */
    function redeemNative(uint256 sharesToRedeem, bytes memory redeemData) external returns (uint256 assetsWithdrawn);

    /**
     * @notice Initiates a withdraw for a given number of shares.
     *
     * @param account The address of the account to initiate the withdraw for.
     * @param sharesHeld The number of shares the account holds.
     * @param data calldata used to initiate the withdraw.
     */
    function initiateWithdraw(
        address account,
        uint256 sharesHeld,
        bytes calldata data,
        address forceWithdrawFrom
    )
        external
        returns (uint256 requestId);

    /**
     * @notice Initiates a withdraw for the native balance of the account.
     *
     * @param data calldata used to initiate the withdraw.
     */
    function initiateWithdrawNative(bytes calldata data) external returns (uint256 requestId);

    /**
     * @notice Clears the current account.
     */
    function clearCurrentAccount() external;
}
"
    },
    "src/interfaces/Errors.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.28;

error NotAuthorized(address operator, address user);
error Unauthorized(address caller);
error UnauthorizedLendingMarketTransfer(address from, address to, uint256 value);
error InsufficientYieldTokenBalance();
error InsufficientAssetsForRepayment(uint256 assetsToRepay, uint256 assetsWithdrawn);
error CannotLiquidate(uint256 maxLiquidateShares, uint256 seizedAssets);
error CannotLiquidateZeroShares();
error Paused();
error CannotExitPositionWithinCooldownPeriod();
error CannotTokenizeWithdrawRequest();
error CurrentAccountAlreadySet();
error InvalidVault(address vault);

error WithdrawRequestNotFinalized(uint256 requestId);
error CannotInitiateWithdraw(address account);
error CannotForceWithdraw(address account);
error InsufficientSharesHeld();
error SlippageTooHigh(uint256 actualTokensOut, uint256 minTokensOut);

error CannotEnterPosition();
error NoExistingPosition();
error LiquidatorHasPosition();
error InvalidUpgrade();
error InvalidInitialization();
error InvalidLendingRouter();

error ExistingWithdrawRequest(address vault, address account, uint256 requestId);
error NoWithdrawRequest(address vault, address account);
error InvalidWithdrawRequestTokenization();

error InvalidPrice(uint256 oraclePrice, uint256 spotPrice);
error PoolShareTooHigh(uint256 poolClaim, uint256 maxSupplyThreshold);
error AssetRemaining(uint256 assetRemaining);
"
    },
    "src/utils/Constants.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

import { WETH9 } from "../interfaces/IWETH.sol";
import { AddressRegistry } from "../proxy/AddressRegistry.sol";

address constant ETH_ADDRESS = address(0);
address constant ALT_ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
uint256 constant DEFAULT_PRECISION = 1e18;
uint256 constant DEFAULT_DECIMALS = 18;
uint256 constant SHARE_PRECISION = 1e24;
uint256 constant VIRTUAL_SHARES = 1e6;

uint256 constant COOLDOWN_PERIOD = 5 minutes;
uint256 constant YEAR = 365 days;

// Will move these to a deployment file when we go to multiple chains
uint256 constant CHAIN_ID_MAINNET = 1;
WETH9 constant WETH = WETH9(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
AddressRegistry constant ADDRESS_REGISTRY = AddressRegistry(0xe335d314BD4eF7DD44F103dC124FEFb7Ce63eC95);
"
    },
    "src/utils/TypeConvert.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.29;

library TypeConvert {
    function toUint(int256 x) internal pure returns (uint256) {
        require(x >= 0);
        return uint256(x);
    }

    function toInt(uint256 x) internal pure returns (int256) {
        require(x <= uint256(type(int256).max)); // dev: toInt overflow
        return int256(x);
    }

    function toUint128(uint256 x) internal pure returns (uint128) {
        require(x <= uint128(type(uint128).max)); // dev: toUint128 overflow
        return uint128(x);
    }

    function toUint120(uint256 x) internal pure returns (uint120) {
        require(x <= uint120(type(uint120).max)); // dev: toUint120 overflow
        return uint120(x);
    }
}
"
    },
    "src/interfaces/IEIP20NonStandard.sol": {
      "content": "// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.8.28;

/**
 * @title EIP20NonStandardInterface
 * @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
 *  See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
 */
interface IEIP20NonStandard {
    /**
     * @notice Get the total number of tokens in circulation
     * @return The supply of tokens
     */
    function totalSupply() external view returns (uint256);

    /**
     * @notice Gets the balance of the specified address
     * @param owner The address from which the balance will be retrieved
     * @return balance
     */
    function balanceOf(address owner) external view returns (uint256 balance);

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
     * @notice Transfer `amount` tokens from `msg.sender` to `dst`
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     */
    function transfer(address dst, uint256 amount) external;

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
     * @notice Transfer `amount` tokens from `src` to `dst`
     * @param src The address of the source account
     * @param dst The address of the destination account
     * @param amount The number of tokens to transfer
     */
    function transferFrom(address src, address dst, uint256 amount) external;

    ///
    /// !!!!!!!!!!!!!!
    /// !!! NOTICE !!! `approve` does not return a value, in violation of the ERC-20 specification
    /// !!!!!!!!!!!!!!
    ///

    /**
     * @notice Approve `spender` to transfer up to `amount` from `src`
     * @dev This will overwrite the approval amount for `spender`
     *  and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
     * @param spender The address of the account which may transfer tokens
     * @param amount The number of tokens that are approved
     */
    function approve(address spender, uint256 amount) external;

    /**
     * @notice Get the current allowance from `owner` for `spender`
     * @param owner The address of the account which owns the tokens to be spent
     * @param spender The address of the account which may transfer tokens
     * @return remaining The number of tokens allowed to be spent
     */
    function allowance(address owner, address spender) external view returns (uint256 remaining);

    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 amount);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}
"
    },
    "node_modules/@openzeppelin/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}
"
    },
    "node_modules/@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and 

Tags:
ERC20, ERC165, Multisig, Swap, Staking, Yield, Voting, Timelock, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x35a24f2b672dd3bffa63c8b3528118247dfed464|verified:true|block:23471127|tx:0x72257b6749f37565fcf34d733fdd7aa46aa66e2ea3b48e3365c544e5c7df8aa3|first_check:1759219702

Submitted on: 2025-09-30 10:08:22

Comments

Log in to comment.

No comments yet.