MorphoMarket

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/contracts/markets/MorphoMarket.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {Abstract4626MarketWrapper} from "./Abstract4626MarketWrapper.sol";

/**
 * @title Morpho lending market wrapper so rewards can be collected.
 * @author Origin Protocol Inc
 */
contract MorphoMarket is Abstract4626MarketWrapper {
    /// @notice The address of the Morpho Token contract.
    IERC20 public constant MORPHO_TOKEN = IERC20(0x58D97B57BB95320F9a05dC918Aef65434969c2B2);

    /// @notice Constructor to set immutable storage variables.
    /// @param _arm The address of the ARM contract.
    /// @param _market The address of the Morpho lending market.
    constructor(address _arm, address _market) Abstract4626MarketWrapper(_arm, _market) {}

    /// @notice Claim all Morpho tokens and send them to the Harvester.
    function _collectRewards() internal override returns (address[] memory, uint256[] memory) {
        address[] memory tokens = new address[](1);
        uint256[] memory amounts = new uint256[](1);
        tokens[0] = address(MORPHO_TOKEN);
        amounts[0] = MORPHO_TOKEN.balanceOf(address(this));

        if (amounts[0] > 0) {
            // Transfer the Morpho tokens to the Harvester
            MORPHO_TOKEN.transfer(harvester, amounts[0]);
        }

        emit CollectedRewards(tokens, amounts);

        return (tokens, amounts);
    }
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2-5.0.2/contracts/interfaces/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
"
    },
    "src/contracts/markets/Abstract4626MarketWrapper.sol": {
      "content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.23;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20} from "@openzeppelin/contracts/interfaces/IERC20.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {Ownable} from "../Ownable.sol";
import {IDistributor} from "../Interfaces.sol";
/**
 * @title Abstract 4626 lending market wrapper
 * @author Origin Protocol Inc
 */

contract Abstract4626MarketWrapper is Initializable, Ownable {
    /// @notice The address of the asset deposited in the lending market.
    address public immutable asset;
    /// @notice The address of the linked Automated Redemption Manager (ARM).
    address public immutable arm;
    /// @notice The address of the 4626 lending market.
    address public immutable market;

    /// @notice The address of the Harvester contract that collects token rewards.
    address public harvester;

    /// @notice The address of the Merkle Distributor contract.
    IDistributor public merkleDistributor;

    uint256[48] private _gap;

    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
    event Withdraw(
        address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
    );
    event HarvesterUpdated(address harvester);
    event MerkleDistributorUpdated(address merkleDistributor);
    event CollectedRewards(address[] tokens, uint256[] amounts);

    /// @notice Constructor to set immutable storage variables.
    /// @param _arm The address of the ARM contract.
    /// @param _market The address of the lending market.
    constructor(address _arm, address _market) {
        arm = _arm;
        market = _market;

        asset = IERC4626(_market).asset();
    }

    /// @notice Initialize the proxy contract with the Harvester address.
    /// @param _harvester The address of the Harvester contract.
    function initialize(address _harvester, address _merkleDistributor) external initializer onlyOwner {
        _setHarvester(_harvester);
        _setMerkleDistributor(_merkleDistributor);
    }

    /// @notice Deposit an exact amount of asset tokens to the lending market
    /// and mint a variable amount of lending market shares to this contract.
    /// @param assets The exact amount of asset tokens to deposit.
    /// @param receiver The receiver has to be the address of the ARM contract.
    /// @return shares The amount of lending market shares that were minted.
    function deposit(uint256 assets, address receiver) external returns (uint256 shares) {
        require(msg.sender == arm && receiver == arm, "Only ARM can deposit");

        // Transfer liquidity from the ARM to this contract
        IERC20(asset).transferFrom(arm, address(this), assets);

        // Approve the lending market to spend the asset tokens
        IERC20(asset).approve(market, assets);
        // Deposit assets to the lending market from this contract
        // and mint shares to this contract
        shares = IERC4626(market).deposit(assets, address(this));

        emit Deposit(arm, arm, assets, shares);
    }

    /// @notice Get the max amount of asset tokens that can be withdrawn from the lending market
    /// from the lending market shares owned by this contract.
    /// @param owner The owner account has to be the address of the ARM contract.
    /// @return maxAssets The max amount of asset tokens that can be withdrawn.
    function maxWithdraw(address owner) external view returns (uint256 maxAssets) {
        if (owner != arm) return 0;

        maxAssets = IERC4626(market).maxWithdraw(address(this));
    }

    /// @notice Withdraw an exact amount of asset tokens from the lending market
    /// from the lending market shares owned by this contract.
    /// @param assets The exact amount of asset tokens to withdraw.
    /// @param receiver The receiver has to be the address of the ARM contract.
    /// @param owner The owner has to be the address of the ARM contract.
    /// @return shares The amount of lending market shares that were burnt.
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares) {
        require(msg.sender == arm && receiver == arm && owner == arm, "Only ARM can withdraw");

        // Withdraw assets from the lending market to the ARM
        shares = IERC4626(market).withdraw(assets, arm, address(this));

        emit Withdraw(arm, arm, arm, assets, shares);
    }

    /// @notice Get the amount of asset tokens that can be received
    /// from burning an exact amount of lending market shares.
    /// @param shares The exact amount of lending market shares to burn.
    /// @return assets The amount of asset tokens that will be received.
    function previewRedeem(uint256 shares) external view returns (uint256 assets) {
        // Preview the amount of assets that can be redeemed for a given number of shares
        assets = IERC4626(market).previewRedeem(shares);
    }

    /// @notice Get the max amount of lending market shares that can be redeemed
    /// from the lending market shares owned by this contract.
    /// @dev This can return a smaller amount than balanceOf() if there is not enough liquidity
    /// in the lending market.
    /// @param owner The owner account has to be the address of the ARM contract.
    /// @return maxShares The max amount of lending market shares in this contract that can be burnt.
    function maxRedeem(address owner) external view returns (uint256 maxShares) {
        if (owner != arm) return 0;

        maxShares = IERC4626(market).maxRedeem(address(this));
    }

    /// @notice Burn an exact amount of lending market shares from this contract
    /// and sends the asset tokens to the ARM.
    /// @param shares The exact amount of lending market shares to burn.
    /// @param receiver The receiver has to be the address of the ARM contract.
    /// @param owner The owner has to be the address of the ARM contract.
    /// @return assets The amount of asset tokens that were sent to the ARM.
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets) {
        require(msg.sender == arm && receiver == arm && owner == arm, "Only ARM can redeem");

        // Redeem shares for assets from the lending market to the ARM
        assets = IERC4626(market).redeem(shares, arm, address(this));

        emit Withdraw(arm, arm, arm, assets, shares);
    }

    /// @notice Claim all reward tokens from the market and send them to the Harvester.
    /// @return tokens The address of the reward tokens.
    /// @return amounts The amount of reward tokens to be collected.
    function collectRewards() external returns (address[] memory, uint256[] memory) {
        require(msg.sender == harvester, "Only harvester can collect");

        return _collectRewards();
    }

    /// @notice Collect rewards from the lending market. Override this function to implement the logic.
    /// @return tokens The address of the reward tokens.
    /// @return amounts The amount of reward tokens to be collected.
    function _collectRewards() internal virtual returns (address[] memory, uint256[] memory) {
        revert("Not implemented");
    }

    /// @notice Claim tokens from the Merkle Distributor
    /// @param token The address of the token to claim.
    /// @param amount The amount of tokens to claim.
    /// @param proof The Merkle proof to validate the claim.
    function merkleClaim(address token, uint256 amount, bytes32[] calldata proof) external {
        address[] memory users = new address[](1);
        users[0] = address(this);

        address[] memory tokens = new address[](1);
        tokens[0] = token;

        uint256[] memory amounts = new uint256[](1);
        amounts[0] = amount;

        bytes32[][] memory proofs = new bytes32[][](1);
        proofs[0] = proof;

        merkleDistributor.claim(users, tokens, amounts, proofs);
    }

    ////////////////////////////////////////////////////
    ///         View Functions
    ////////////////////////////////////////////////////

    /// @notice Get the amount of Market shares owned by this contract.
    /// @param owner The owner has to be the address of the ARM contract.
    /// @return shares The amount of lending market shares owned by this contract.
    function balanceOf(address owner) external view returns (uint256) {
        if (owner != arm) return 0;

        // Get the balance of shares in the lending market
        return IERC4626(market).balanceOf(address(this));
    }

    /// @notice The amount of shares that would exchanged for the amount of assets provided.
    /// @param assets The amount of asset tokens to convert to shares.
    /// @return shares The amount of lending market shares that would be received.
    function convertToShares(uint256 assets) external view returns (uint256 shares) {
        shares = IERC4626(market).convertToShares(assets);
    }

    /// @notice The amount of assets that would be exchanged for the amount of shares provided.
    /// @param shares The amount of lending market shares to convert to assets.
    /// @return assets The amount of asset tokens that would be received.
    function convertToAssets(uint256 shares) external view returns (uint256 assets) {
        assets = IERC4626(market).convertToAssets(shares);
    }

    ////////////////////////////////////////////////////
    ///         Admin Functions
    ////////////////////////////////////////////////////

    /// @notice The contract owner sets the address of the Harvester contract.
    /// @param _harvester The address of the Harvester contract.
    function setHarvester(address _harvester) external onlyOwner {
        _setHarvester(_harvester);
    }

    function _setHarvester(address _harvester) internal {
        require(_harvester != address(0), "Harvester cannot be zero address");
        require(harvester != _harvester, "Harvester already set");

        harvester = _harvester;

        emit HarvesterUpdated(_harvester);
    }

    /// @notice The contract owner sets the address of the Merkle Distributor contract.
    /// @param _merkleDistributor The address of the Merkle Distributor contract.
    function setMerkleDistributor(address _merkleDistributor) external onlyOwner {
        _setMerkleDistributor(_merkleDistributor);
    }

    function _setMerkleDistributor(address _merkleDistributor) internal {
        require(_merkleDistributor != address(0), "Invalid MerkleDistributor addr");
        merkleDistributor = IDistributor(_merkleDistributor);

        emit MerkleDistributorUpdated(_merkleDistributor);
    }

    /**
     * @dev Helps recovering any tokens accidentally sent to this contract.
     * @param token Token to transfer. 0x0 to transfer Native token.
     * @param to Address to transfer the tokens to.
     * @param amount Amount to transfer. 0 to transfer all balance.
     */
    function transferTokens(address token, address to, uint256 amount) external onlyOwner {
        require(token != asset && token != market, "Cannot transfer asset or market token");
        require(to != address(0), "Invalid address");
        require(to == owner() || to == harvester, "Cannot transfer to non-owner or non-harvester");

        if (address(token) == address(0)) {
            // Move ETH
            amount = amount > 0 ? amount : address(this).balance;
            payable(to).transfer(amount);
            return;
        }

        // Move all balance if amount set to 0
        amount = amount > 0 ? amount : IERC20(token).balanceOf(address(this));

        // Transfer to owner
        IERC20(token).transfer(to, amount);
    }
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2-5.0.2/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
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);
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2-5.0.2/contracts/interfaces/IERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @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 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 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.
     * - 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 convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}
"
    },
    "dependencies/@openzeppelin-contracts-upgradeable-5.0.2-5.0.2/contracts/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}
"
    },
    "src/contracts/Ownable.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

/**
 * @title Base contract that provides ownership control
 * @author Origin Protocol Inc
 */
contract Ownable {
    /// @notice The slot used to store the owner of the contract.
    /// This is also used as the proxy admin.
    /// keccak256(“eip1967.proxy.admin”) - 1 per EIP 1967
    bytes32 internal constant OWNER_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;

    event AdminChanged(address previousAdmin, address newAdmin);

    constructor() {
        assert(OWNER_SLOT == bytes32(uint256(keccak256("eip1967.proxy.admin")) - 1));
        _setOwner(msg.sender);
    }

    /// @notice The contract owner and proxy admin.
    function owner() public view returns (address) {
        return _owner();
    }

    /// @notice Set the owner and proxy admin of the contract.
    /// @param newOwner The address of the new owner.
    function setOwner(address newOwner) external onlyOwner {
        _setOwner(newOwner);
    }

    function _owner() internal view returns (address ownerOut) {
        bytes32 position = OWNER_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            ownerOut := sload(position)
        }
    }

    function _setOwner(address newOwner) internal {
        emit AdminChanged(_owner(), newOwner);
        bytes32 position = OWNER_SLOT;
        // solhint-disable-next-line no-inline-assembly
        assembly {
            sstore(position, newOwner)
        }
    }

    function _onlyOwner() internal view {
        require(msg.sender == _owner(), "ARM: Only owner can call this function.");
    }

    modifier onlyOwner() {
        _onlyOwner();
        _;
    }
}
"
    },
    "src/contracts/Interfaces.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
    function decimals() external view returns (uint8);

    event Transfer(address indexed from, address indexed to, uint256 value);
}

interface IOethARM {
    function token0() external returns (address);
    function token1() external returns (address);
    function owner() external returns (address);

    /**
     * @notice Swaps an exact amount of input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param to Recipient of the output tokens.
     */
    function swapExactTokensForTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountIn,
        uint256 amountOutMin,
        address to
    ) external;

    /**
     * @notice Uniswap V2 Router compatible interface. Swaps an exact amount of
     * input tokens for as many output tokens as possible.
     * msg.sender should have already given the ARM contract an allowance of
     * at least amountIn on the input token.
     *
     * @param amountIn The amount of input tokens to send.
     * @param amountOutMin The minimum amount of output tokens that must be received for the transaction not to revert.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapExactTokensForTokens(
        uint256 amountIn,
        uint256 amountOutMin,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    /**
     * @notice Receive an exact amount of output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param inToken Input token.
     * @param outToken Output token.
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param to Recipient of the output tokens.
     */
    function swapTokensForExactTokens(
        IERC20 inToken,
        IERC20 outToken,
        uint256 amountOut,
        uint256 amountInMax,
        address to
    ) external;

    /**
     * @notice Uniswap V2 Router compatible interface. Receive an exact amount of
     * output tokens for as few input tokens as possible.
     * msg.sender should have already given the router an allowance of
     * at least amountInMax on the input token.
     *
     * @param amountOut The amount of output tokens to receive.
     * @param amountInMax The maximum amount of input tokens that can be required before the transaction reverts.
     * @param path The input and output token addresses.
     * @param to Recipient of the output tokens.
     * @param deadline Unix timestamp after which the transaction will revert.
     * @return amounts The input and output token amounts.
     */
    function swapTokensForExactTokens(
        uint256 amountOut,
        uint256 amountInMax,
        address[] calldata path,
        address to,
        uint256 deadline
    ) external returns (uint256[] memory amounts);

    function setOwner(address newOwner) external;
    function transferToken(address token, address to, uint256 amount) external;

    // From OethLiquidityManager
    function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);
    function claimWithdrawal(uint256 requestId) external;
    function claimWithdrawals(uint256[] calldata requestIds) external;
}

interface ILiquidityProviderARM is IERC20 {
    function previewDeposit(uint256 assets) external returns (uint256 shares);
    function deposit(uint256 assets) external returns (uint256 shares);
    function deposit(uint256 assets, address liquidityProvider) external returns (uint256 shares);

    function previewRedeem(uint256 shares) external returns (uint256 assets);
    function requestRedeem(uint256 shares) external returns (uint256 requestId, uint256 assets);
    function claimRedeem(uint256 requestId) external returns (uint256 assets);

    function totalAssets() external returns (uint256 assets);
    function convertToShares(uint256 assets) external returns (uint256 shares);
    function convertToAssets(uint256 shares) external returns (uint256 assets);
    function lastTotalAssets() external returns (uint256 assets);
}

interface ICapManager {
    function postDepositHook(address liquidityProvider, uint256 assets) external;
}

interface LegacyAMM {
    function transferToken(address tokenOut, address to, uint256 amount) external;
}

interface IOriginVault {
    function mint(address _asset, uint256 _amount, uint256 _minimumOusdAmount) external;

    function redeem(uint256 _amount, uint256 _minimumUnitAmount) external;

    function requestWithdrawal(uint256 amount) external returns (uint256 requestId, uint256 queued);

    function claimWithdrawal(uint256 requestId) external returns (uint256 amount);

    function claimWithdrawals(uint256[] memory requestIds)
        external
        returns (uint256[] memory amounts, uint256 totalAmount);

    function addWithdrawalQueueLiquidity() external;

    function setMaxSupplyDiff(uint256 _maxSupplyDiff) external;

    function governor() external view returns (address);

    function dripper() external view returns (address);

    function withdrawalQueueMetadata()
        external
        view
        returns (uint128 queued, uint128 claimable, uint128 claimed, uint128 nextWithdrawalIndex);

    function withdrawalRequests(uint256 requestId)
        external
        view
        returns (address withdrawer, bool claimed, uint40 timestamp, uint128 amount, uint128 queued);

    function withdrawalClaimDelay() external view returns (uint256);
}

interface IGovernance {
    enum ProposalState {
        Pending,
        Active,
        Canceled,
        Defeated,
        Succeeded,
        Queued,
        Expired,
        Executed
    }

    function state(uint256 proposalId) external view returns (ProposalState);

    function proposalSnapshot(uint256 proposalId) external view returns (uint256);

    function proposalDeadline(uint256 proposalId) external view returns (uint256);

    function proposalEta(uint256 proposalId) external view returns (uint256);

    function votingDelay() external view returns (uint256);

    function castVote(uint256 proposalId, uint8 support) external returns (uint256 balance);

    function queue(uint256 proposalId) external;

    function execute(uint256 proposalId) external;
}

interface IWETH is IERC20 {
    event Deposit(address indexed dst, uint256 wad);
    event Withdrawal(address indexed src, uint256 wad);

    function deposit() external payable;
    function withdraw(uint256 wad) external;
}

interface ISTETH is IERC20 {
    event Submitted(address indexed sender, uint256 amount, address referral);

    // function() external payable;
    function submit(address _referral) external payable returns (uint256);
}

interface IStETHWithdrawal {
    event WithdrawalRequested(
        uint256 indexed requestId,
        address indexed requestor,
        address indexed owner,
        uint256 amountOfStETH,
        uint256 amountOfShares
    );
    event WithdrawalsFinalized(
        uint256 indexed from, uint256 indexed to, uint256 amountOfETHLocked, uint256 sharesToBurn, uint256 timestamp
    );
    event WithdrawalClaimed(
        uint256 indexed requestId, address indexed owner, address indexed receiver, uint256 amountOfETH
    );

    struct WithdrawalRequestStatus {
        /// @notice stETH token amount that was locked on withdrawal queue for this request
        uint256 amountOfStETH;
        /// @notice amount of stETH shares locked on withdrawal queue for this request
        uint256 amountOfShares;
        /// @notice address that can claim or transfer this request
        address owner;
        /// @notice timestamp of when the request was created, in seconds
        uint256 timestamp;
        /// @notice true, if request is finalized
        bool isFinalized;
        /// @notice true, if request is claimed. Request is claimable if (isFinalized && !isClaimed)
        bool isClaimed;
    }

    function transferFrom(address _from, address _to, uint256 _requestId) external;
    function ownerOf(uint256 _requestId) external returns (address);
    function requestWithdrawals(uint256[] calldata _amounts, address _owner)
        external
        returns (uint256[] memory requestIds);
    function getLastCheckpointIndex() external view returns (uint256);
    function findCheckpointHints(uint256[] calldata _requestIds, uint256 _firstIndex, uint256 _lastIndex)
        external
        view
        returns (uint256[] memory hintIds);
    function claimWithdrawals(uint256[] calldata _requestIds, uint256[] calldata _hints) external;
    function getWithdrawalStatus(uint256[] calldata _requestIds)
        external
        view
        returns (WithdrawalRequestStatus[] memory statuses);
    function getWithdrawalRequests(address _owner) external view returns (uint256[] memory requestsIds);
    function getLastRequestId() external view returns (uint256);
}

interface IOracle {
    function price(address asset) external view returns (uint256 price);
}

interface IHarvestable {
    function collectRewards() external returns (address[] memory tokens, uint256[] memory rewards);
}

interface IMagpieRouter {
    function swapWithMagpieSignature(bytes calldata) external payable returns (uint256 amountOut);
}

library DistributionTypes {
    struct IncentivesProgramCreationInput {
        string name;
        address rewardToken;
        uint104 emissionPerSecond;
        uint40 distributionEnd;
    }
}

library IDistributionManager {
    struct AccruedRewards {
        uint256 amount;
        bytes32 programId;
        address rewardToken;
    }
}

interface SiloIncentivesControllerGaugeLike {
    function claimRewards(address _to) external returns (IDistributionManager.AccruedRewards[] memory accruedRewards);
    function createIncentivesProgram(DistributionTypes.IncentivesProgramCreationInput memory _incentivesProgramInput)
        external;
    function getAllProgramsNames() external view returns (string[] memory programsNames);
    function getRewardsBalance(address _user, string memory _programName)
        external
        view
        returns (uint256 unclaimedRewards);
    function incentivesPrograms(bytes32)
        external
        view
        returns (
            uint256 index,
            address rewardToken,
            uint104 emissionPerSecond,
            uint40 lastUpdateTimestamp,
            uint40 distributionEnd
        );
    function owner() external view returns (address);
}

interface IEETHWithdrawal {
    function requestWithdraw(address receipient, uint256 amount) external returns (uint256 requestId);
}

interface IEETHWithdrawalNFT {
    function finalizeRequests(uint256 requestId) external;
    function claimWithdraw(uint256 requestId) external;
    function batchClaimWithdraw(uint256[] calldata requestIds) external;
}

interface IEETHRedemptionManager {
    function redeemWeEth(uint256 amount, address receiver) external;
    function canRedeem(uint256 amount) external view returns (bool);
}

interface IDistributor {
    function claim(
        address[] calldata users,
        address[] calldata tokens,
        uint256[] calldata amounts,
        bytes32[][] calldata proofs
    ) external;
}
"
    },
    "dependencies/@openzeppelin-contracts-5.0.2-5.0.2/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20 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);
}
"
    }
  },
  "settings": {
    "remappings": [
      "dependencies/@pendle-sy-1.0.0-1.0.0/:@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-4.9.3-4.9.3/contracts/",
      "contracts/=src/contracts/",
      "script/=script/",
      "test/=test/",
      "utils/=src/contracts/utils/",
      "@solmate/=dependencies/solmate-6.7.0/src/",
      "forge-std/=dependencies/forge-std-1.9.7/src/",
      "@pendle-sy/=dependencies/@pendle-sy-1.0.0-1.0.0/contracts/",
      "@openzeppelin/contracts/=dependencies/@openzeppelin-contracts-5.0.2-5.0.2/contracts/",
      "@openzeppelin/contracts-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.0.2-5.0.2/contracts/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 200
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "shanghai",
    "viaIR": false
  }
}}

Tags:
ERC20, Multisig, Mintable, Swap, Liquidity, Yield, Upgradeable, Multi-Signature, Factory|addr:0x2a1b59870f7806e60df58415b0c220c096f57658|verified:true|block:23689032|tx:0x7bfc1e3e0ac9fe7b19244882c236d2e80328b03c022eede03abc6f01dc96f338|first_check:1761829637

Submitted on: 2025-10-30 14:07:20

Comments

Log in to comment.

No comments yet.