BoxAdapterFactory

Description:

Decentralized Finance (DeFi) protocol contract providing Mintable, Swap, Liquidity, Yield, Factory, Oracle functionality.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "src/factories/BoxAdapterFactory.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association, Steakhouse Financial
pragma solidity ^0.8.28;

import {BoxAdapter} from "./../BoxAdapter.sol";
import {IBox} from "./../interfaces/IBox.sol";
import {IBoxAdapter} from "./../interfaces/IBoxAdapter.sol";
import {IBoxAdapterFactory} from "./../interfaces/IBoxAdapterFactory.sol";

contract BoxAdapterFactory is IBoxAdapterFactory {
    /* STORAGE */

    mapping(address parentVault => mapping(IBox box => IBoxAdapter)) public boxAdapter;
    mapping(address account => bool) public isBoxAdapter;

    /* FUNCTIONS */

    /// @dev Returns the address of the deployed BoxAdapter.
    function createBoxAdapter(address parentVault, IBox box) external returns (IBoxAdapter) {
        BoxAdapter _boxAdapter = new BoxAdapter(parentVault, box);
        boxAdapter[parentVault][box] = _boxAdapter;
        isBoxAdapter[address(_boxAdapter)] = true;
        emit CreateBoxAdapter(parentVault, address(box), _boxAdapter);
        return _boxAdapter;
    }
}
"
    },
    "src/BoxAdapter.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association, Steakhouse Financial
pragma solidity ^0.8.28;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IVaultV2} from "./../lib/vault-v2/src/interfaces/IVaultV2.sol";
import {MathLib} from "./../lib/vault-v2/src/libraries/MathLib.sol";
import {SafeERC20Lib} from "./../lib/vault-v2/src/libraries/SafeERC20Lib.sol";
import {IBox} from "./interfaces/IBox.sol";
import {IBoxAdapter} from "./interfaces/IBoxAdapter.sol";

contract BoxAdapter is IBoxAdapter {
    using MathLib for uint256;

    /* IMMUTABLES */

    address public immutable factory;
    address public immutable parentVault;
    IBox public immutable box;
    bytes32 public immutable adapterId;

    /* STORAGE */

    address public skimRecipient;

    /* FUNCTIONS */

    constructor(address _parentVault, IBox _box) {
        factory = msg.sender;
        parentVault = _parentVault;
        box = _box;
        adapterId = keccak256(abi.encode("this", address(this)));
        address asset = IVaultV2(_parentVault).asset();
        require(asset == _box.asset(), AssetMismatch());
        SafeERC20Lib.safeApprove(asset, _parentVault, type(uint256).max);
        SafeERC20Lib.safeApprove(asset, address(_box), type(uint256).max);
    }

    function setSkimRecipient(address newSkimRecipient) external {
        require(msg.sender == IVaultV2(parentVault).owner(), NotAuthorized());
        skimRecipient = newSkimRecipient;
        emit SetSkimRecipient(newSkimRecipient);
    }

    /// @dev Skims the adapter's balance of `token` and sends it to `skimRecipient`.
    /// @dev This is useful to handle rewards that the adapter has earned.
    function skim(address token) external {
        require(msg.sender == skimRecipient, NotAuthorized());
        require(token != address(box), CannotSkimBoxShares());
        uint256 balance = IERC20(token).balanceOf(address(this));
        SafeERC20Lib.safeTransfer(token, skimRecipient, balance);
        emit Skim(token, balance);
    }

    /// @dev Does not log anything because the ids (logged in the parent vault) are enough.
    /// @dev Returns the ids of the allocation and the change in allocation.
    function allocate(bytes memory data, uint256 assets, bytes4, address) external returns (bytes32[] memory, int256) {
        require(data.length == 0, InvalidData());
        require(msg.sender == parentVault, NotAuthorized());

        if (assets > 0) IERC4626(box).deposit(assets, address(this));
        // Safe casts because Box bounds the total supply of the underlying token, and allocation is less than the
        // max total assets of the box.
        int256 newAllocation = int256(box.previewRedeem(box.balanceOf(address(this))));
        int256 oldAllocation = int256(allocation());

        return (ids(), newAllocation - oldAllocation);
    }

    /// @dev Does not log anything because the ids (logged in the parent vault) are enough.
    /// @dev Returns the ids of the deallocation and the change in allocation.
    function deallocate(bytes memory data, uint256 assets, bytes4, address) external returns (bytes32[] memory, int256) {
        require(data.length == 0, InvalidData());
        require(msg.sender == parentVault, NotAuthorized());

        if (assets > 0) IERC4626(box).withdraw(assets, address(this), address(this));
        // Safe casts because Box bounds the total supply of the underlying token, and allocation is less than the
        // max total assets of the vault.
        int256 newAllocation = int256(box.previewRedeem(box.balanceOf(address(this))));
        int256 oldAllocation = int256(allocation());

        return (ids(), newAllocation - oldAllocation);
    }

    /// @dev Returns adapter's ids.
    function ids() public view returns (bytes32[] memory) {
        bytes32[] memory ids_ = new bytes32[](1);
        ids_[0] = adapterId;
        return ids_;
    }

    function allocation() public view returns (uint256) {
        return IVaultV2(parentVault).allocation(adapterId);
    }

    function realAssets() external view returns (uint256) {
        return allocation() != 0 ? box.previewRedeem(box.balanceOf(address(this))) : 0;
    }

    function adapterData() external view returns (bytes memory) {
        return abi.encode("this", address(this));
    }
}
"
    },
    "src/interfaces/IBox.sol": {
      "content": "// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Steakhouse
pragma solidity >=0.8.0;

import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IFunding} from "./IFunding.sol";
import {IOracle} from "./IOracle.sol";
import {ISwapper} from "./ISwapper.sol";

/// @notice Callback interface for Box flash loans
interface IBoxFlashCallback {
    function onBoxFlash(IERC20 token, uint256 amount, bytes calldata data) external;
}

interface IBox is IERC4626 {
    /* FUNCTIONS */

    // ========== STATE FUNCTIONS ==========
    function asset() external view returns (address);
    function slippageEpochDuration() external view returns (uint256);
    function shutdownSlippageDuration() external view returns (uint256);
    function shutdownWarmup() external view returns (uint256);
    function owner() external view returns (address);
    function curator() external view returns (address);
    function guardian() external view returns (address);
    function shutdownTime() external view returns (uint256);
    function skimRecipient() external view returns (address);
    function isAllocator(address account) external view returns (bool);
    function isFeeder(address account) external view returns (bool);
    function tokens(uint256 index) external view returns (IERC20);
    function oracles(IERC20 token) external view returns (IOracle);
    function maxSlippage() external view returns (uint256);
    function accumulatedSlippage() external view returns (uint256);
    function slippageEpochStart() external view returns (uint256);
    function executableAt(bytes calldata data) external view returns (uint256);

    // ========== INVESTMENT MANAGEMENT ==========
    function skim(IERC20 token) external;
    function allocate(IERC20 token, uint256 assetsAmount, ISwapper swapper, bytes calldata data) external;
    function deallocate(IERC20 token, uint256 tokensAmount, ISwapper swapper, bytes calldata data) external;
    function reallocate(IERC20 from, IERC20 to, uint256 tokensAmount, ISwapper swapper, bytes calldata data) external;

    // ========== EMERGENCY ==========
    function shutdown() external;
    function recover() external;

    // ========== ADMIN FUNCTIONS ==========
    function setSkimRecipient(address newSkimRecipient) external;
    function transferOwnership(address newOwner) external;
    function setCurator(address newCurator) external;
    function setGuardian(address newGuardian) external;
    function setIsAllocator(address account, bool newIsAllocator) external;

    // ========== TIMELOCK GOVERNANCE ==========
    function submit(bytes calldata data) external;
    function timelock(bytes4 selector) external view returns (uint256);
    function revoke(bytes calldata data) external;
    function increaseTimelock(bytes4 selector, uint256 newDuration) external;
    function decreaseTimelock(bytes4 selector, uint256 newDuration) external;
    function abdicateTimelock(bytes4 selector) external;

    // ========== TIMELOCKED FUNCTIONS ==========
    function setIsFeeder(address account, bool newIsFeeder) external;
    function setMaxSlippage(uint256 newMaxSlippage) external;
    function addToken(IERC20 token, IOracle oracle) external;
    function removeToken(IERC20 token) external;
    function changeTokenOracle(IERC20 token, IOracle oracle) external;

    // ========== VIEW FUNCTIONS ==========
    function isToken(IERC20 token) external view returns (bool);
    function isTokenOrAsset(IERC20 token) external view returns (bool);
    function tokensLength() external view returns (uint256);
    function isShutdown() external view returns (bool);
    function isWinddown() external view returns (bool);

    // ========== FUNDING ADMIN FUNCTIONS ==========
    function addFunding(IFunding fundingModule) external;
    function addFundingFacility(IFunding fundingModule, bytes calldata facilityData) external;
    function addFundingCollateral(IFunding fundingModule, IERC20 collateralToken) external;
    function addFundingDebt(IFunding fundingModule, IERC20 debtToken) external;
    function removeFunding(IFunding fundingModule) external;
    function removeFundingFacility(IFunding fundingModule, bytes calldata facilityData) external;
    function removeFundingCollateral(IFunding fundingModule, IERC20 collateralToken) external;
    function removeFundingDebt(IFunding fundingModule, IERC20 debtToken) external;

    // ========== FUNDING VIEW FUNCTIONS ==========
    function fundings(uint256 index) external view returns (IFunding);
    function fundingsLength() external view returns (uint256);
    function isFunding(IFunding fundingModule) external view returns (bool);

    // ========== SIMPLE FUNDING OPERATIONS ==========
    function pledge(IFunding fundingModule, bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external;
    function depledge(IFunding fundingModule, bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external;
    function borrow(IFunding fundingModule, bytes calldata facilityData, IERC20 debtToken, uint256 borrowAmount) external;
    function repay(IFunding fundingModule, bytes calldata facilityData, IERC20 debtToken, uint256 repayAmount) external;

    // ========== COMPLEX FUNDING OPERATIONS WITH FLASHLOAN AND SWAPPER ==========

    function flash(IERC20 flashToken, uint256 flashAmount, bytes calldata data) external;
}
"
    },
    "src/interfaces/IBoxAdapter.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse
pragma solidity >=0.8.0;

import {IAdapter} from "../../lib/vault-v2/src/interfaces/IAdapter.sol";
import {IBox} from "./IBox.sol";

interface IBoxAdapter is IAdapter {
    /* EVENTS */

    event SetSkimRecipient(address indexed newSkimRecipient);
    event Skim(address indexed token, uint256 assets);

    /* ERRORS */

    error AssetMismatch();
    error CannotSkimBoxShares();
    error InvalidData();
    error NotAuthorized();

    /* FUNCTIONS */

    function factory() external view returns (address);
    function setSkimRecipient(address newSkimRecipient) external;
    function skim(address token) external;
    function parentVault() external view returns (address);
    function box() external view returns (IBox);
    function skimRecipient() external view returns (address);
    function allocation() external view returns (uint256);
    function ids() external view returns (bytes32[] memory);

    // Added for BoxAdapter
    function adapterId() external view returns (bytes32);
    function adapterData() external view returns (bytes memory);
}
"
    },
    "src/interfaces/IBoxAdapterFactory.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association, Steakhouse Financial
pragma solidity >=0.8.0;

import {IBox} from "./IBox.sol";
import {IBoxAdapter} from "./IBoxAdapter.sol";

/// @notice Factory interface for BoxAdapter to link a Morpho Vault V2 and a Box
interface IBoxAdapterFactory {
    /* EVENTS */

    event CreateBoxAdapter(address indexed parentVault, address indexed box, IBoxAdapter indexed boxAdapter);

    /* FUNCTIONS */

    function boxAdapter(address parentVault, IBox box) external view returns (IBoxAdapter);
    function isBoxAdapter(address account) external view returns (bool);
    function createBoxAdapter(address parentVault, IBox box) external returns (IBoxAdapter boxAdapter);
}
"
    },
    "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC4626.sol)

pragma solidity >=0.6.2;

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

/**
 * @dev Interface of the ERC-4626 "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 redemption 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);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @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);
}
"
    },
    "lib/vault-v2/src/interfaces/IVaultV2.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;

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

struct Caps {
    uint256 allocation;
    uint128 absoluteCap;
    uint128 relativeCap;
}

interface IVaultV2 is IERC4626, IERC2612 {
    // State variables
    function virtualShares() external view returns (uint256);
    function owner() external view returns (address);
    function curator() external view returns (address);
    function receiveSharesGate() external view returns (address);
    function sendSharesGate() external view returns (address);
    function receiveAssetsGate() external view returns (address);
    function sendAssetsGate() external view returns (address);
    function adapterRegistry() external view returns (address);
    function isSentinel(address account) external view returns (bool);
    function isAllocator(address account) external view returns (bool);
    function firstTotalAssets() external view returns (uint256);
    function _totalAssets() external view returns (uint128);
    function lastUpdate() external view returns (uint64);
    function maxRate() external view returns (uint64);
    function adapters(uint256 index) external view returns (address);
    function adaptersLength() external view returns (uint256);
    function isAdapter(address account) external view returns (bool);
    function allocation(bytes32 id) external view returns (uint256);
    function absoluteCap(bytes32 id) external view returns (uint256);
    function relativeCap(bytes32 id) external view returns (uint256);
    function forceDeallocatePenalty(address adapter) external view returns (uint256);
    function liquidityAdapter() external view returns (address);
    function liquidityData() external view returns (bytes memory);
    function timelock(bytes4 selector) external view returns (uint256);
    function abdicated(bytes4 selector) external view returns (bool);
    function executableAt(bytes memory data) external view returns (uint256);
    function performanceFee() external view returns (uint96);
    function performanceFeeRecipient() external view returns (address);
    function managementFee() external view returns (uint96);
    function managementFeeRecipient() external view returns (address);

    // Gating
    function canSendShares(address account) external view returns (bool);
    function canReceiveShares(address account) external view returns (bool);
    function canSendAssets(address account) external view returns (bool);
    function canReceiveAssets(address account) external view returns (bool);

    // Multicall
    function multicall(bytes[] memory data) external;

    // Owner functions
    function setOwner(address newOwner) external;
    function setCurator(address newCurator) external;
    function setIsSentinel(address account, bool isSentinel) external;
    function setName(string memory newName) external;
    function setSymbol(string memory newSymbol) external;

    // Timelocks for curator functions
    function submit(bytes memory data) external;
    function revoke(bytes memory data) external;

    // Curator functions
    function setIsAllocator(address account, bool newIsAllocator) external;
    function setReceiveSharesGate(address newReceiveSharesGate) external;
    function setSendSharesGate(address newSendSharesGate) external;
    function setReceiveAssetsGate(address newReceiveAssetsGate) external;
    function setSendAssetsGate(address newSendAssetsGate) external;
    function setAdapterRegistry(address newAdapterRegistry) external;
    function addAdapter(address account) external;
    function removeAdapter(address account) external;
    function increaseTimelock(bytes4 selector, uint256 newDuration) external;
    function decreaseTimelock(bytes4 selector, uint256 newDuration) external;
    function abdicate(bytes4 selector) external;
    function setPerformanceFee(uint256 newPerformanceFee) external;
    function setManagementFee(uint256 newManagementFee) external;
    function setPerformanceFeeRecipient(address newPerformanceFeeRecipient) external;
    function setManagementFeeRecipient(address newManagementFeeRecipient) external;
    function increaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external;
    function decreaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external;
    function increaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external;
    function decreaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external;
    function setMaxRate(uint256 newMaxRate) external;
    function setForceDeallocatePenalty(address adapter, uint256 newForceDeallocatePenalty) external;

    // Allocator functions
    function allocate(address adapter, bytes memory data, uint256 assets) external;
    function deallocate(address adapter, bytes memory data, uint256 assets) external;
    function setLiquidityAdapterAndData(address newLiquidityAdapter, bytes memory newLiquidityData) external;

    // Exchange rate
    function accrueInterest() external;
    function accrueInterestView()
        external
        view
        returns (uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares);

    // Force deallocate
    function forceDeallocate(address adapter, bytes memory data, uint256 assets, address onBehalf)
        external
        returns (uint256 penaltyShares);
}
"
    },
    "lib/vault-v2/src/libraries/MathLib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity ^0.8.0;

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

library MathLib {
    /// @dev Returns (x * y) / d rounded down.
    function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y) / d;
    }

    /// @dev Returns (x * y) / d rounded up.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
        return (x * y + (d - 1)) / d;
    }

    /// @dev Returns max(0, x - y).
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Casts from uint256 to uint128, reverting if input number is too large.
    function toUint128(uint256 x) internal pure returns (uint128) {
        require(x <= type(uint128).max, ErrorsLib.CastOverflow());
        return uint128(x);
    }

    /// @dev Casts from int256 to uint256, reverting if input number is negative.
    function toUint256(int256 x) internal pure returns (uint256) {
        require(x >= 0, ErrorsLib.CastOverflow());
        return uint256(x);
    }

    /// @dev Returns min(x, y).
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }
}
"
    },
    "lib/vault-v2/src/libraries/SafeERC20Lib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity ^0.8.0;

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

library SafeERC20Lib {
    function safeTransfer(address token, address to, uint256 value) internal {
        require(token.code.length > 0, ErrorsLib.NoCode());

        (bool success, bytes memory returndata) = token.call(abi.encodeCall(IERC20.transfer, (to, value)));
        require(success, ErrorsLib.TransferReverted());
        require(returndata.length == 0 || abi.decode(returndata, (bool)), ErrorsLib.TransferReturnedFalse());
    }

    function safeTransferFrom(address token, address from, address to, uint256 value) internal {
        require(token.code.length > 0, ErrorsLib.NoCode());

        (bool success, bytes memory returndata) = token.call(abi.encodeCall(IERC20.transferFrom, (from, to, value)));
        require(success, ErrorsLib.TransferFromReverted());
        require(returndata.length == 0 || abi.decode(returndata, (bool)), ErrorsLib.TransferFromReturnedFalse());
    }

    function safeApprove(address token, address spender, uint256 value) internal {
        require(token.code.length > 0, ErrorsLib.NoCode());

        (bool success, bytes memory returndata) = token.call(abi.encodeCall(IERC20.approve, (spender, value)));
        require(success, ErrorsLib.ApproveReverted());
        require(returndata.length == 0 || abi.decode(returndata, (bool)), ErrorsLib.ApproveReturnedFalse());
    }
}
"
    },
    "src/interfaces/IFunding.sol": {
      "content": "// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Steakhouse
pragma solidity >=0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IOracle} from "./IOracle.sol";

interface IOracleCallback {
    /// @dev RIs considered to have a price of ORACLE_PRECISION
    function asset() external view returns (address);

    /// @dev Returns an oracle for tokens that are not the asset
    function oracles(IERC20 token) external view returns (IOracle);
}

/**
 * @notice Interface for a funding module
 * @notice `nav` should never revert
 */
interface IFunding {
    // ========== ADMIN ==========
    function addFacility(bytes calldata facilityData) external;
    function removeFacility(bytes calldata facilityData) external;
    function isFacility(bytes calldata facilityData) external view returns (bool);
    function facilities(uint256 index) external view returns (bytes memory);
    function facilitiesLength() external view returns (uint256);

    function addCollateralToken(IERC20 collateralToken) external;
    function removeCollateralToken(IERC20 collateralToken) external;
    function isCollateralToken(IERC20 collateralToken) external view returns (bool);
    function collateralTokens(uint256 index) external view returns (IERC20);
    function collateralTokensLength() external view returns (uint256);

    function addDebtToken(IERC20 debtToken) external;
    function removeDebtToken(IERC20 debtToken) external;
    function isDebtToken(IERC20 debtToken) external view returns (bool);
    function debtTokens(uint256 index) external view returns (IERC20);
    function debtTokensLength() external view returns (uint256);

    // ========== ACTIONS ==========
    function pledge(bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external;
    function depledge(bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external;
    function borrow(bytes calldata facilityData, IERC20 debtToken, uint256 borrowAmount) external;
    function repay(bytes calldata facilityData, IERC20 debtToken, uint256 repayAmount) external;

    // ========== POSITION ==========
    function ltv(bytes calldata facilityData) external view returns (uint256);
    function debtBalance(bytes calldata facilityData, IERC20 debtToken) external view returns (uint256);
    function collateralBalance(bytes calldata facilityData, IERC20 collateralToken) external view returns (uint256);
    function debtBalance(IERC20 debtToken) external view returns (uint256);
    function collateralBalance(IERC20 collateralToken) external view returns (uint256);
    function nav(IOracleCallback oraclesProvider) external view returns (uint256);
}
"
    },
    "src/interfaces/IOracle.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

/// @notice Oracle interface to get a token price with 36 decimals of precision
interface IOracle {
    function price() external view returns (uint256);
}
"
    },
    "src/interfaces/ISwapper.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

interface ISwapper {
    /// @dev Sells `amountIn` of `input` tokens for `output` tokens.
    /// @param input The address of the input token.
    /// @param output The address of the output token.
    /// @param amountIn The amount of input tokens to sell.
    /// @param data Additional data to pass to the swapper.
    function sell(IERC20 input, IERC20 output, uint256 amountIn, bytes calldata data) external;
}
"
    },
    "lib/vault-v2/src/interfaces/IAdapter.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;

/// @dev See VaultV2 NatSpec comments for more details on adapter's spec.
interface IAdapter {
    /// @dev Returns the market' ids and the change in assets on this market.
    function allocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
        external
        returns (bytes32[] memory ids, int256 change);

    /// @dev Returns the market' ids and the change in assets on this market.
    function deallocate(bytes memory data, uint256 assets, bytes4 selector, address sender)
        external
        returns (bytes32[] memory ids, int256 change);

    /// @dev Returns the current value of the investments of the adapter (in underlying asset).
    function realAssets() external view returns (uint256 assets);
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity >=0.6.2;

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);
}
"
    },
    "lib/vault-v2/src/interfaces/IERC20.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;

interface IERC20 {
    function decimals() external view returns (uint8);
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 shares) external returns (bool success);
    function transferFrom(address from, address to, uint256 shares) external returns (bool success);
    function approve(address spender, uint256 shares) external returns (bool success);
    function allowance(address owner, address spender) external view returns (uint256);
}
"
    },
    "lib/vault-v2/src/interfaces/IERC4626.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;

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

interface IERC4626 is IERC20 {
    function asset() external view returns (address);
    function totalAssets() external view returns (uint256);
    function convertToAssets(uint256 shares) external view returns (uint256 assets);
    function convertToShares(uint256 assets) external view returns (uint256 shares);
    function deposit(uint256 assets, address onBehalf) external returns (uint256 shares);
    function mint(uint256 shares, address onBehalf) external returns (uint256 assets);
    function withdraw(uint256 assets, address onBehalf, address receiver) external returns (uint256 shares);
    function redeem(uint256 shares, address onBehalf, address receiver) external returns (uint256 assets);
    function previewDeposit(uint256 assets) external view returns (uint256 shares);
    function previewMint(uint256 shares) external view returns (uint256 assets);
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);
    function previewRedeem(uint256 shares) external view returns (uint256 assets);
    function maxDeposit(address onBehalf) external view returns (uint256 assets);
    function maxMint(address onBehalf) external view returns (uint256 shares);
    function maxWithdraw(address onBehalf) external view returns (uint256 assets);
    function maxRedeem(address onBehalf) external view returns (uint256 shares);
}
"
    },
    "lib/vault-v2/src/interfaces/IERC2612.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;

interface IERC2612 {
    function permit(address owner, address spender, uint256 shares, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
        external;
    function nonces(address owner) external view returns (uint256);
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}
"
    },
    "lib/vault-v2/src/libraries/ErrorsLib.sol": {
      "content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity ^0.8.0;

library ErrorsLib {
    error Abdicated();
    error AbsoluteCapExceeded();
    error AbsoluteCapNotDecreasing();
    error AbsoluteCapNotIncreasing();
    error ApproveReturnedFalse();
    error ApproveReverted();
    error CannotReceiveShares();
    error CannotReceiveAssets();
    error CannotSendShares();
    error CannotSendAssets();
    error CapExceeded();
    error CastOverflow();
    error DataAlreadyPending();
    error DataNotTimelocked();
    error FeeInvariantBroken();
    error FeeTooHigh();
    error InvalidSigner();
    error MaxRateTooHigh();
    error NoCode();
    error NotAdapter();
    error NotInAdapterRegistry();
    error PenaltyTooHigh();
    error PermitDeadlineExpired();
    error RelativeCapAboveOne();
    error RelativeCapExceeded();
    error RelativeCapNotDecreasing();
    error RelativeCapNotIncreasing();
    error AutomaticallyTimelocked();
    error TimelockNotDecreasing();
    error TimelockNotExpired();
    error TimelockNotIncreasing();
    error TransferFromReturnedFalse();
    error TransferFromReverted();
    error TransferReturnedFalse();
    error TransferReverted();
    error Unauthorized();
    error ZeroAbsoluteCap();
    error ZeroAddress();
    error ZeroAllocation();
}
"
    }
  },
  "settings": {
    "remappings": [
      "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
      "@vault-v2/=lib/vault-v2/",
      "@morpho-blue/=lib/morpho-blue/src/",
      "ds-test/=lib/morpho-blue/lib/forge-std/lib/ds-test/src/",
      "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
      "forge-std/=lib/forge-std/src/",
      "halmos-cheatcodes/=lib/morpho-blue/lib/halmos-cheatcodes/src/",
      "metamorpho-v1.1/=lib/vault-v2/lib/metamorpho-v1.1/",
      "metamorpho/=lib/vault-v2/lib/metamorpho/",
      "morpho-blue-irm/=lib/vault-v2/lib/metamorpho/lib/morpho-blue-irm/src/",
      "morpho-blue/=lib/morpho-blue/",
      "openzeppelin-contracts/=lib/openzeppelin-contracts/",
      "solmate/=lib/vault-v2/lib/metamorpho/lib/morpho-blue-irm/lib/solmate/src/",
      "vault-v2/=lib/vault-v2/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 100
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "prague",
    "viaIR": true
  }
}}

Tags:
ERC20, DeFi, Mintable, Swap, Liquidity, Yield, Factory, Oracle|addr:0x767fa3827d7aa8f69c937689c5ab09412ffc1a2a|verified:true|block:23404442|tx:0xf658f4ce0e1c50002c0476500384ed2747e5408ba39bac3da2c08367c3949441|first_check:1758374208

Submitted on: 2025-09-20 15:16:50

Comments

Log in to comment.

No comments yet.