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/BoxAdapterCachedFactory.sol": {
"content": "// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association, Steakhouse Financial
pragma solidity ^0.8.28;
import {BoxAdapterCached} from "./../BoxAdapterCached.sol";
import {IBox} from "./../interfaces/IBox.sol";
import {IBoxAdapter} from "./../interfaces/IBoxAdapter.sol";
import {IBoxAdapterFactory} from "./../interfaces/IBoxAdapterFactory.sol";
contract BoxAdapterCachedFactory 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) {
BoxAdapterCached _boxAdapter = new BoxAdapterCached(parentVault, box);
boxAdapter[parentVault][box] = _boxAdapter;
isBoxAdapter[address(_boxAdapter)] = true;
emit CreateBoxAdapter(parentVault, address(box), _boxAdapter);
return _boxAdapter;
}
}
"
},
"src/BoxAdapterCached.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 BoxAdapterCached is IBoxAdapter {
using MathLib for uint256;
/* EVENTS */
event UpdateTotalAsset(uint256 oldTotalAssets, uint256 totalAssets);
/* IMMUTABLES */
address public immutable factory;
address public immutable parentVault;
IBox public immutable box;
bytes32 public immutable adapterId;
/* STORAGE */
address public skimRecipient;
uint256 public totalAssets;
uint256 public totalAssetsTimestamp;
/* 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.
_updateTotalAssets();
int256 newAllocation = int256(totalAssets);
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.
_updateTotalAssets();
int256 newAllocation = int256(totalAssets);
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 ? totalAssets : 0;
}
/// @dev Updates the cached total assets of the adapter.
/// @dev Allowed: Vault allocator, sentinel or anyone after 24 hours of inactivity
function updateTotalAssets() external {
require(
IVaultV2(parentVault).isAllocator(msg.sender) ||
IVaultV2(parentVault).isSentinel(msg.sender) ||
totalAssetsTimestamp + 1 days < block.timestamp,
NotAuthorized()
);
uint256 oldTotalAssets = totalAssets;
_updateTotalAssets();
emit UpdateTotalAsset(oldTotalAssets, totalAssets);
}
function _updateTotalAssets() internal {
totalAssets = box.previewRedeem(box.balanceOf(address(this)));
totalAssetsTimestamp = block.timestamp;
}
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
}
}}
Submitted on: 2025-09-20 15:17:07
Comments
Log in to comment.
No comments yet.