Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Showing the last 25 transactions (View Advanced Filter)
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Finalize | 24497497 | 44 hrs ago | IN | 0 ETH | 0.00003389 | ||||
| Add Token | 24497495 | 44 hrs ago | IN | 0 ETH | 0.00000475 | ||||
| Add Token | 24497493 | 44 hrs ago | IN | 0 ETH | 0.00000474 | ||||
| Add Token | 24497492 | 44 hrs ago | IN | 0 ETH | 0.00000457 | ||||
| Add Token | 24497488 | 44 hrs ago | IN | 0 ETH | 0.00000533 | ||||
| Add Feeder | 24497486 | 44 hrs ago | IN | 0 ETH | 0.00000349 | ||||
| Create Standard ... | 24497484 | 44 hrs ago | IN | 0 ETH | 0.00022481 | ||||
| Finalize | 24497428 | 44 hrs ago | IN | 0 ETH | 0.00002976 | ||||
| Add Token | 24497427 | 44 hrs ago | IN | 0 ETH | 0.00000421 | ||||
| Add Token | 24497418 | 44 hrs ago | IN | 0 ETH | 0.00000436 | ||||
| Add Token | 24497416 | 44 hrs ago | IN | 0 ETH | 0.00000438 | ||||
| Add Token | 24497413 | 44 hrs ago | IN | 0 ETH | 0.00000558 | ||||
| Add Feeder | 24497412 | 44 hrs ago | IN | 0 ETH | 0.00000327 | ||||
| Create Standard ... | 24497408 | 44 hrs ago | IN | 0 ETH | 0.0002269 | ||||
| Finalize | 24496782 | 46 hrs ago | IN | 0 ETH | 0.00002885 | ||||
| Add Morpho Marke... | 24496781 | 46 hrs ago | IN | 0 ETH | 0.00001201 | ||||
| Add Morpho Marke... | 24496780 | 46 hrs ago | IN | 0 ETH | 0.00001091 | ||||
| Add Morpho Marke... | 24496779 | 46 hrs ago | IN | 0 ETH | 0.00001104 | ||||
| Add Morpho Marke... | 24496778 | 46 hrs ago | IN | 0 ETH | 0.00001164 | ||||
| Add Morpho Marke... | 24496777 | 46 hrs ago | IN | 0 ETH | 0.00001129 | ||||
| Add Morpho Marke... | 24496775 | 46 hrs ago | IN | 0 ETH | 0.00001107 | ||||
| Add Morpho Marke... | 24496773 | 46 hrs ago | IN | 0 ETH | 0.00001199 | ||||
| Add Funding Coll... | 24496772 | 46 hrs ago | IN | 0 ETH | 0.00000443 | ||||
| Add Funding Coll... | 24496771 | 46 hrs ago | IN | 0 ETH | 0.00000442 | ||||
| Add Funding Coll... | 24496769 | 46 hrs ago | IN | 0 ETH | 0.00000499 |
Showing the last 1 internal transaction (View Advanced Filter)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60e03461 | 24496753 | 46 hrs ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
BoxHelper
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 100 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity ^0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {MorphoVaultV1AdapterFactory} from "@vault-v2/src/adapters/MorphoVaultV1AdapterFactory.sol";
import {IVaultV2} from "@vault-v2/src/interfaces/IVaultV2.sol";
import "@vault-v2/src/libraries/ConstantsLib.sol";
import {VaultV2} from "@vault-v2/src/VaultV2.sol";
import {VaultV2Factory} from "@vault-v2/src/VaultV2Factory.sol";
import "./../interfaces/Aragon.sol";
import {Revoker} from "./Revoker.sol";
import {VaultV2Lib} from "./VaultV2Lib.sol";
import {Box} from "../Box.sol";
import {IBox} from "../interfaces/IBox.sol";
import {BoxFactory} from "../factories/BoxFactory.sol";
import {BoxAdapterFactory} from "../factories/BoxAdapterFactory.sol";
import {IOracle} from "../interfaces/IOracle.sol";
import {FundingMorpho} from "../FundingMorpho.sol";
import {IFunding} from "../interfaces/IFunding.sol";
import {IMorpho, Id, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
interface ISkimmable {
function setSkimRecipient(address recipient) external;
}
/**
* @title BoxHelper
* @notice Helper contract for Box configuration
* @dev This contract handles box configuration, adapter management, and other utility functions.
*
* Chain Support:
* - Ethereum Mainnet (chainId: 1)
* - Base (chainId: 8453)
* - Addresses configured per chain in constructor
*/
contract BoxHelper {
using MarketParamsLib for MarketParams;
/* ======== EVENTS ======== */
event BoxCreated(address indexed box, address indexed asset, string name, string symbol);
/* ======== VARIABLES ======== */
address public owner;
address public curator;
address public vaultAllocator;
address[] public allocators;
BoxFactory public boxFactory;
mapping(address => address) public boxCreator;
function _onlyBoxCreator(IBox box) private view {
require(boxCreator[address(box)] == msg.sender, "Not box creator");
}
/* ======== CONSTRUCTOR ======== */
constructor() {
// Base configuration (chain ID 8453) or local testing (31337)
if (block.chainid == 8453 || block.chainid == 31337) {
owner = 0x0A0e559bc3b0950a7e448F0d4894db195b9cf8DD;
curator = 0x827e86072B06674a077f592A531dcE4590aDeCdB;
vaultAllocator = 0x1be9b8C406120B2c2b8990d0522cD226A3D60e36;
allocators.push(0x0000aeB716a0DF7A9A1AAd119b772644Bc089dA8);
allocators.push(0xfeed46c11F57B7126a773EeC6ae9cA7aE1C03C9a);
boxFactory = BoxFactory(0x846365F9A09aeB7005127C6060876C82F7F70c0b);
}
// Ethereum Mainnet configuration (chain ID 1)
else if (block.chainid == 1) {
owner = 0x0A0e559bc3b0950a7e448F0d4894db195b9cf8DD;
curator = 0x827e86072B06674a077f592A531dcE4590aDeCdB;
vaultAllocator = 0xaaD84c80b013c34D70E54fB343D0C2f309F635E7;
allocators.push(0x0000aeB716a0DF7A9A1AAd119b772644Bc089dA8);
allocators.push(0xfeed46c11F57B7126a773EeC6ae9cA7aE1C03C9a);
boxFactory = BoxFactory(0xcF23d316e7C415a70836Ec9E68568C3cD82EBFc4);
}
// Arbitrum One configuration (chain ID 42161)
else if (block.chainid == 42161) {
owner = 0x0A0e559bc3b0950a7e448F0d4894db195b9cf8DD;
curator = 0x827e86072B06674a077f592A531dcE4590aDeCdB;
vaultAllocator = 0x257021F139DCB62A303571666d3718f25F4BF462;
allocators.push(0x0000aeB716a0DF7A9A1AAd119b772644Bc089dA8);
allocators.push(0xfeed46c11F57B7126a773EeC6ae9cA7aE1C03C9a);
boxFactory = BoxFactory(0x72576c537e25AeCb3026E5c8EF4B90436E22A333);
}
// Polygon PoS configuration (chain ID 137)
else if (block.chainid == 137) {
owner = 0x0A0e559bc3b0950a7e448F0d4894db195b9cf8DD;
curator = 0x827e86072B06674a077f592A531dcE4590aDeCdB;
vaultAllocator = 0xF4Fe5381E526e4e4FEe0572881F4D0C5382f9A75;
allocators.push(0x0000aeB716a0DF7A9A1AAd119b772644Bc089dA8);
allocators.push(0xfeed46c11F57B7126a773EeC6ae9cA7aE1C03C9a);
boxFactory = BoxFactory(address(0xa0a261B900D4F2179d6a5317b5cEA00fa2839230));
}
else {
revert("Unsupported chain");
}
}
/* ======= BOX SETUP ======== */
function createBox(
IERC20 asset,
string memory name,
string memory symbol,
uint256 maxSlippage,
uint256 slippageEpochDuration,
uint256 shutdownSlippageDuration,
uint256 shutdownWarmup,
bytes32 salt,
address creator) external returns (IBox box) {
return _createBox(asset, name, symbol, maxSlippage, slippageEpochDuration, shutdownSlippageDuration, shutdownWarmup, salt, msg.sender);
}
function createStandardBox(
IERC20 asset,
string memory name,
string memory symbol,
uint256 maxSlippage,
bytes32 salt) public returns (IBox box) {
return _createBox(
asset,
name,
symbol,
maxSlippage,
7 days,
10 days,
7 days,
salt,
msg.sender);
}
function _createBox(
IERC20 asset,
string memory name,
string memory symbol,
uint256 maxSlippage,
uint256 slippageEpochDuration,
uint256 shutdownSlippageDuration,
uint256 shutdownWarmup,
bytes32 salt,
address creator) internal returns (IBox box) {
// Create vault via factory
box = boxFactory.createBox(
asset,
address(this),
address(this),
name,
symbol,
maxSlippage,
slippageEpochDuration,
shutdownSlippageDuration,
shutdownWarmup,
salt
);
// register the creator and fail if already regiestered
require(boxCreator[address(box)] == address(0), "Box already registered");
boxCreator[address(box)] = creator;
// Add helper as allocator (needed for initial config)
//box.setIsAllocator(address(this), true);
// Add configured allocators
for (uint i = 0; i < allocators.length; i++) {
box.setIsAllocator(address(allocators[i]), true);
}
box.setIsAllocator(address(vaultAllocator), true);
box.setSkimRecipient(vaultAllocator);
_deadDeposit(box);
emit BoxCreated(address(box), address(asset), name, symbol);
}
function addFeeder(IBox box, address feeder) external {
_onlyBoxCreator(box);
box.submit(abi.encodeWithSelector(box.setIsFeeder.selector, feeder, true));
box.setIsFeeder(feeder, true);
}
function _deadDeposit(IBox box) internal {
IERC20 asset = IERC20(box.asset());
uint256 assets = box.previewMint(1e9);
asset.transferFrom(msg.sender, address(this), assets);
box.submit(abi.encodeWithSelector(box.setIsFeeder.selector, address(this), true));
box.setIsFeeder(address(this), true);
asset.approve(address(box), assets);
box.mint(1e9, 0x000000000000000000000000000000000000dEaD);
box.submit(abi.encodeWithSelector(box.setIsFeeder.selector, address(this), false));
box.setIsFeeder(address(this), false);
}
function addToken(IBox box, IERC20 token, IOracle oracle) external {
_onlyBoxCreator(box);
box.submit(abi.encodeWithSelector(box.addToken.selector, token, oracle));
box.addToken(token, oracle);
}
function addFundingMorpho(IBox box, address morpho, uint256 maxLltvRatio) external returns (FundingMorpho fundingMorpho) {
_onlyBoxCreator(box);
fundingMorpho = new FundingMorpho(address(box), address(morpho), maxLltvRatio);
box.submit(abi.encodeWithSelector(box.addFunding.selector, fundingMorpho));
box.addFunding(fundingMorpho);
}
function addFunding(IBox box, IFunding fundingModule) external {
_onlyBoxCreator(box);
box.submit(abi.encodeWithSelector(box.addFunding.selector, fundingModule));
box.addFunding(fundingModule);
}
function addFundingCollateral(IBox box, IFunding fundingModule, IERC20 collateral) external {
_onlyBoxCreator(box);
box.submit(abi.encodeWithSelector(box.addFundingCollateral.selector, fundingModule, collateral));
box.addFundingCollateral(fundingModule, collateral);
}
function addFundingDebt(IBox box, IFunding fundingModule, IERC20 debt) external {
_onlyBoxCreator(box);
box.submit(abi.encodeWithSelector(box.addFundingDebt.selector, fundingModule, debt));
box.addFundingDebt(fundingModule, debt);
}
function addMorphoMarket(IBox box, FundingMorpho funding, address morpho, IERC20 loan, IERC20 collateral, address oracle, address irm, uint256 lltv) external {
_onlyBoxCreator(box);
MarketParams memory fundingMarketParams = MarketParams({
loanToken: address(loan),
collateralToken: address(collateral),
oracle: oracle,
irm: irm,
lltv: lltv
});
MarketParams memory stored = IMorpho(morpho).idToMarketParams(fundingMarketParams.id());
require(stored.loanToken == fundingMarketParams.loanToken, "Market not listed on Morpho");
bytes memory facilityData = funding.encodeFacilityData(fundingMarketParams);
box.submit(abi.encodeWithSelector(box.addFundingFacility.selector, address(funding), facilityData));
box.addFundingFacility(funding, facilityData);
}
function finalize(IBox box, address guardian) external {
_onlyBoxCreator(box);
// set guardian
box.submit(abi.encodeWithSelector(box.setGuardian.selector, guardian));
box.setGuardian(guardian);
// 7 days timelock on critical functions
uint256 CRITICAL_TIMELOCK = 7 days;
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.addFunding.selector, CRITICAL_TIMELOCK));
box.increaseTimelock(box.addFunding.selector, CRITICAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.setGuardian.selector, CRITICAL_TIMELOCK));
box.increaseTimelock(box.setGuardian.selector, CRITICAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.decreaseTimelock.selector, CRITICAL_TIMELOCK));
box.increaseTimelock(box.decreaseTimelock.selector, CRITICAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.changeTokenOracle.selector, CRITICAL_TIMELOCK));
box.increaseTimelock(box.changeTokenOracle.selector, CRITICAL_TIMELOCK);
// 3 days timelock on usual management functions
uint256 NORMAL_TIMELOCK = 3 days;
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.addToken.selector, NORMAL_TIMELOCK));
box.increaseTimelock(box.addToken.selector, NORMAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.addFundingFacility.selector, NORMAL_TIMELOCK));
box.increaseTimelock(box.addFundingFacility.selector, NORMAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.addFundingCollateral.selector, NORMAL_TIMELOCK));
box.increaseTimelock(box.addFundingCollateral.selector, NORMAL_TIMELOCK);
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.addFundingDebt.selector, NORMAL_TIMELOCK));
box.increaseTimelock(box.addFundingDebt.selector, NORMAL_TIMELOCK);
// 1 hour timelock on non critical functions
uint256 SLIPPAGE_TIMELOCK = 1 days;
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.setMaxSlippage.selector, SLIPPAGE_TIMELOCK));
box.increaseTimelock(box.setMaxSlippage.selector, SLIPPAGE_TIMELOCK);
// 1 hour timelock on non critical functions
uint256 FEEDER_TIMELOCK = 1 hours;
box.submit(abi.encodeWithSelector(box.increaseTimelock.selector, box.setIsFeeder.selector, FEEDER_TIMELOCK));
box.increaseTimelock(box.setIsFeeder.selector, FEEDER_TIMELOCK);
// remove allocator not needed as we haven't added it
box.setCurator(curator);
box.transferOwnership(owner);
}
}// 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);
}// 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);
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity 0.8.28;
import {MorphoVaultV1Adapter} from "./MorphoVaultV1Adapter.sol";
import {IMorphoVaultV1AdapterFactory} from "./interfaces/IMorphoVaultV1AdapterFactory.sol";
contract MorphoVaultV1AdapterFactory is IMorphoVaultV1AdapterFactory {
/* STORAGE */
mapping(address parentVault => mapping(address morphoVaultV1 => address)) public morphoVaultV1Adapter;
mapping(address account => bool) public isMorphoVaultV1Adapter;
/* FUNCTIONS */
/// @dev Returns the address of the deployed MorphoVaultV1Adapter.
function createMorphoVaultV1Adapter(address parentVault, address morphoVaultV1) external returns (address) {
address _morphoVaultV1Adapter = address(new MorphoVaultV1Adapter{salt: bytes32(0)}(parentVault, morphoVaultV1));
morphoVaultV1Adapter[parentVault][morphoVaultV1] = _morphoVaultV1Adapter;
isMorphoVaultV1Adapter[_morphoVaultV1Adapter] = true;
emit CreateMorphoVaultV1Adapter(parentVault, morphoVaultV1, _morphoVaultV1Adapter);
return _morphoVaultV1Adapter;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
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);
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity ^0.8.0;
uint256 constant WAD = 1e18;
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
bytes32 constant PERMIT_TYPEHASH =
keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
uint256 constant MAX_MAX_RATE = 200e16 / uint256(365 days); // 200% APR
uint256 constant MAX_PERFORMANCE_FEE = 0.5e18; // 50%
uint256 constant MAX_MANAGEMENT_FEE = 0.05e18 / uint256(365 days); // 5%
uint256 constant MAX_FORCE_DEALLOCATE_PENALTY = 0.02e18; // 2%// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity 0.8.28;
import {IERC20} from "./interfaces/IERC20.sol";
import {IVaultV2, Caps} from "./interfaces/IVaultV2.sol";
import {IAdapter} from "./interfaces/IAdapter.sol";
import {IAdapterRegistry} from "./interfaces/IAdapterRegistry.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
import "./libraries/ConstantsLib.sol"; // forge-lint: disable-line(unaliased-plain-import)
import {MathLib} from "./libraries/MathLib.sol";
import {SafeERC20Lib} from "./libraries/SafeERC20Lib.sol";
import {IReceiveSharesGate, ISendSharesGate, IReceiveAssetsGate, ISendAssetsGate} from "./interfaces/IGate.sol";
/// ERC4626
/// @dev The vault is compliant with ERC-4626 and with ERC-2612 (permit extension). Though the vault has a
/// non-conventional behaviour on max functions: they always return zero.
/// @dev totalSupply is not updated to include shares minted to fee recipients. One can call accrueInterestView to
/// compute the updated totalSupply.
///
/// TOTAL ASSETS
/// @dev Adapters are responsible for reporting to the vault how much their investments are worth at any time, so that
/// the vault can accrue interest or realize losses.
/// @dev _totalAssets stores the last recorded total assets. Use totalAssets() for the updated total assets.
/// @dev Upon interest accrual, the vault loops through adapters' realAssets(). If there are too many adapters and/or
/// they consume too much gas on realAssets(), it could cause issues such as expensive interactions, even DOS.
///
/// LOSS REALIZATION
/// @dev Loss realization occurs in accrueInterest and decreases the total assets, causing shares to lose value.
/// @dev Vault shares should not be loanable to prevent shares shorting on loss realization. Shares can be flashloanable
/// because flashloan-based shorting is prevented as interests and losses are only accounted once per transaction.
///
/// SHARE PRICE
/// @dev The share price can go down if the vault incurs some losses.
/// @dev To get some additional bounds on the share price upon interactions, a check must be performed on top.
/// @dev Interest/loss are accounted only once per transaction (at the first interaction with the vault).
/// @dev Donations increase the share price but not faster than the maxRate.
/// @dev The vault has 1 virtual asset and a decimal offset of max(0, 18 - assetDecimals). In order to protect against
/// inflation attacks, the vault might need to be seeded with an initial deposit. See
/// https://docs.openzeppelin.com/contracts/5.x/erc4626#inflation-attack
/// @dev Donations and forceDeallocate penalties increase the rate, which can attract opportunistic depositors which
/// will dilute interest. This fact can be mitigated by reducing the maxRate.
///
/// CAPS
/// @dev Ids have an asset allocation, and can be absolutely capped and/or relatively capped.
/// @dev The allocation is not always up to date, because interest and losses are accounted only when (de)allocating in
/// the corresponding markets.
/// @dev The caps are checked on allocate (where allocations can increase) for the ids returned by the adapter.
/// @dev Relative caps are "soft" in the sense that they are not checked on exit.
/// @dev Caps can be exceeded because of interest and donations in adapters (if adapters do not prevent them).
/// @dev The relative cap is relative to firstTotalAssets, not realAssets.
/// @dev The relative cap unit is WAD.
/// @dev To track allocations using events, use the Allocate and Deallocate events only.
///
/// FIRST TOTAL ASSETS
/// @dev The variable firstTotalAssets tracks the total assets after the first interest accrual of the transaction.
/// @dev Used to implement a mechanism that prevents bypassing relative caps with flashloans. This mechanism makes the
/// caps conservative and can generate false positives, notably for big deposits that go through the liquidity adapter.
/// @dev Also used to accrue interest only once per transaction (see the "share price" section).
/// @dev Relative caps can still be manipulated by allocators (with short-term deposits), but it requires capital.
/// @dev The behavior of firstTotalAssets is different when the vault has totalAssets=0, but it does not matter
/// internally because in this case there are no investments to cap.
///
/// ADAPTERS
/// @dev Loose specification of adapters:
/// - They must enforce that only the vault can call allocate/deallocate.
/// - They must enter/exit markets only in allocate/deallocate.
/// - They must return the right ids on allocate/deallocate. Returned ids must not repeat.
/// - After a call to deallocate, the vault must have an approval to transfer at least `assets` from the adapter.
/// - They must make it possible to make deallocate possible (for in-kind redemptions).
/// - The totalAssets() calculation ignores markets for which the vault has no allocation.
/// - They must not re-enter (directly or indirectly) the vault. They might not statically prevent it, but the curator
/// must not interact with markets that can re-enter the vault.
/// - After an update, the sum of the changes returned after interactions with a given market must be exactly the
/// current estimated position.
/// @dev Ids being reused are useful to cap multiple investments that have a common property.
/// @dev Allocating is prevented if one of the ids' absolute cap is zero and deallocating is prevented if the id's
/// allocation is zero. This prevents interactions with zero assets with unknown markets. For markets that share all
/// their ids, it will be impossible to "disable" them (preventing any interaction) without disabling the others using
/// the same ids.
/// @dev On allocate or deallocate, the adapters might lose some assets (total realAssets decreases), for instance due
/// to roundings or entry/exit fees. This loss should stay negligible compared to gas. Adapters might not statically
/// ensure this, but the curators should not interact with markets that can create big entry/exit losses.
/// @dev Except particular scenarios, adapters should be removed only if they have no assets. In order to ensure no
/// allocator can allocate some assets in an adapter being removed, there should be an id exclusive to the adapter with
/// its cap set to zero.
///
/// ADAPTER REGISTRY
/// @dev An adapter registry can be added to restrict the adapters. This is useful to commit to using only a certain
/// type of adapters for example.
/// @dev If adapterRegistry is set to address(0), the vault can have any adapters.
/// @dev When an adapterRegistry is set, it retroactively checks already added adapters.
/// @dev If the adapterRegistry now returns false for an already added adapter, it doesn't impact the vault's
/// functioning.
/// @dev The invariant that adapters of the vault are all in the registry holds only if the registry cannot remove
/// adapters (is "add only").
///
/// LIQUIDITY ADAPTER
/// @dev Liquidity is allocated to the liquidityAdapter on deposit/mint, and deallocated from the liquidityAdapter on
/// withdraw/redeem if idle assets don't cover the withdrawal.
/// @dev The liquidity adapter is useful on exit, so that exit liquidity is available in addition to the idle assets.
/// But the same adapter/data is used for both entry and exit to have the property that in the general case looping
/// supply-withdraw or withdraw-supply should not change the allocation.
/// @dev If a cap (absolute or relative) associated with the ids returned by the liquidity adapter on the liquidity data
/// is reached, deposit/mint will revert. In particular, when the vault is empty or almost empty, the relative cap check
/// is likely to make deposits revert.
///
/// TOKEN REQUIREMENTS
/// @dev List of assumptions on the token that guarantees that the vault behaves as expected:
/// - It should be ERC-20 compliant, except that it can omit return values on transfer and transferFrom.
/// - The balance of the vault should only decrease on transfer and transferFrom.
/// - It should not re-enter the vault on transfer or transferFrom.
/// - The balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount on
/// transfer and transferFrom. In particular, tokens with fees on transfer are not supported.
///
/// LIVENESS REQUIREMENTS
/// @dev List of assumptions that guarantees the vault's liveness properties:
/// - Adapters should not revert on realAssets.
/// - The token should not revert on transfer and transferFrom if balances and approvals are right.
/// - The token should not revert on transfer to self.
/// - totalAssets and totalSupply must stay below ~10^35. Initially there are min(1, 10^(18-decimals)) shares per asset.
/// - The vault is pinged at least every 10 years.
/// - Adapters must not revert on deallocate if the underlying markets are liquid.
///
/// TIMELOCKS
/// @dev The timelock duration of decreaseTimelock is the timelock duration of the function whose timelock is being
/// decreased (e.g. the timelock of decreaseTimelock(addAdapter, ...) is timelock[addAdapter]).
/// @dev It is still possible to submit changes of the timelock duration of decreaseTimelock, but it won't have any
/// effect (and trying to execute this change will revert).
/// @dev If a function is abdicated, it cannot be called no matter its timelock and what executableAt[data] contains.
/// Otherwise, the minimum time at which a function can be called is the following:
/// min(
/// block.timestamp + timelock[selector],
/// executableAt[selector::_],
/// executableAt[decreaseTimelock::selector::newTimelock] + newTimelock
/// ).
/// @dev Nothing is checked on the timelocked data, so it could be not executable (function does not exist, argument
/// encoding is wrong, function' conditions are not met, etc.), or clashing (e.g. increaseTimelock and
/// decreaseTimelock for the same selector).
///
/// ABDICATION
/// @dev When a timelocked function is abdicated, it can't be called anymore.
/// @dev It is still possible to submit data for it or change its timelock, but it will not be executable / effective.
///
/// GATES
/// @dev Set to 0 to disable a gate.
/// @dev Gates must never revert, nor consume too much gas.
/// @dev receiveSharesGate:
/// - Gates receiving shares.
/// - Can lock users out of getting back their shares deposited on an other contract.
/// @dev sendSharesGate:
/// - Gates sending shares.
/// - Can lock users out of exiting the vault.
/// @dev receiveAssetsGate:
/// - Gates withdrawing assets from the vault.
/// - The vault itself (address(this)) is always allowed to receive assets, regardless of the gate configuration.
/// - Can lock users out of exiting the vault.
/// @dev sendAssetsGate:
/// - Gates depositing assets to the vault.
/// - This gate is not critical (cannot block users' funds), while still being able to gate supplies.
///
/// FEES
/// @dev Fees unit is WAD.
/// @dev This invariant holds for both fees: fee != 0 => recipient != address(0).
///
/// ROLES
/// @dev The owner cannot do actions that can directly hurt depositors. Though it can set the curator and sentinels.
/// @dev The curator cannot do actions that can directly hurt depositors without going through a timelock.
/// @dev Allocators can move funds between markets in the boundaries set by caps without going through timelocks. They
/// can also set the liquidity adapter and data, which can prevent deposits and/or withdrawals (it cannot prevent
/// "in-kind redemptions" with forceDeallocate though). Allocators also set the maxRate.
/// @dev Warning: if setIsAllocator is timelocked, removing an allocator will take time.
/// @dev Roles are not "two-step", so anyone can give a role to anyone, but it does not mean that they will exercise it.
///
/// MISC
/// @dev Zero checks are not systematically performed.
/// @dev No-ops are allowed.
/// @dev NatSpec comments are included only when they bring clarity.
/// @dev The contract uses transient storage.
/// @dev View calls made by this contract should not rely on the executableAt values.
/// @dev At creation, all settings are set to their default values. Notably, timelocks are zero which is useful to set
/// up the vault quickly. Also, there are no gates so anybody can interact with the vault. To prevent that, the gates
/// configuration can be batched with the vault creation.
contract VaultV2 is IVaultV2 {
using MathLib for uint256;
using MathLib for uint128;
using MathLib for int256;
/* IMMUTABLE */
address public immutable asset;
uint8 public immutable decimals;
uint256 public immutable virtualShares;
/* ROLES STORAGE */
address public owner;
address public curator;
address public receiveSharesGate;
address public sendSharesGate;
address public receiveAssetsGate;
address public sendAssetsGate;
address public adapterRegistry;
mapping(address account => bool) public isSentinel;
mapping(address account => bool) public isAllocator;
/* TOKEN STORAGE */
string public name;
string public symbol;
uint256 public totalSupply;
mapping(address account => uint256) public balanceOf;
mapping(address owner => mapping(address spender => uint256)) public allowance;
mapping(address account => uint256) public nonces;
/* INTEREST STORAGE */
uint256 public transient firstTotalAssets;
uint128 public _totalAssets;
uint64 public lastUpdate;
uint64 public maxRate;
/* CURATION STORAGE */
mapping(address account => bool) public isAdapter;
address[] public adapters;
mapping(bytes32 id => Caps) internal caps;
mapping(address adapter => uint256) public forceDeallocatePenalty;
/* LIQUIDITY ADAPTER STORAGE */
address public liquidityAdapter;
bytes public liquidityData;
/* TIMELOCKS STORAGE */
mapping(bytes4 selector => uint256) public timelock;
mapping(bytes4 selector => bool) public abdicated;
mapping(bytes data => uint256) public executableAt;
/* FEES STORAGE */
uint96 public performanceFee;
address public performanceFeeRecipient;
uint96 public managementFee;
address public managementFeeRecipient;
/* GETTERS */
function adaptersLength() external view returns (uint256) {
return adapters.length;
}
function totalAssets() external view returns (uint256) {
(uint256 newTotalAssets,,) = accrueInterestView();
return newTotalAssets;
}
/// forge-lint: disable-next-item(mixed-case-function)
function DOMAIN_SEPARATOR() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_TYPEHASH, block.chainid, address(this)));
}
function absoluteCap(bytes32 id) external view returns (uint256) {
return caps[id].absoluteCap;
}
function relativeCap(bytes32 id) external view returns (uint256) {
return caps[id].relativeCap;
}
function allocation(bytes32 id) external view returns (uint256) {
return caps[id].allocation;
}
/* MULTICALL */
/// @dev Useful for EOAs to batch admin calls.
/// @dev Does not return anything, because accounts who would use the return data would be contracts, which can do
/// the multicall themselves.
function multicall(bytes[] calldata data) external {
for (uint256 i = 0; i < data.length; i++) {
(bool success, bytes memory returnData) = address(this).delegatecall(data[i]);
if (!success) {
assembly ("memory-safe") {
revert(add(32, returnData), mload(returnData))
}
}
}
}
/* CONSTRUCTOR */
constructor(address _owner, address _asset) {
asset = _asset;
owner = _owner;
lastUpdate = uint64(block.timestamp);
uint256 assetDecimals = IERC20(_asset).decimals();
uint256 decimalOffset = uint256(18).zeroFloorSub(assetDecimals);
// forge-lint: disable-next-item(unsafe-typecast) safe because assetDecimals + decimalOffset <= 18.
decimals = uint8(assetDecimals + decimalOffset);
virtualShares = 10 ** decimalOffset;
emit EventsLib.Constructor(_owner, _asset);
}
/* OWNER FUNCTIONS */
function setOwner(address newOwner) external {
require(msg.sender == owner, ErrorsLib.Unauthorized());
owner = newOwner;
emit EventsLib.SetOwner(newOwner);
}
function setCurator(address newCurator) external {
require(msg.sender == owner, ErrorsLib.Unauthorized());
curator = newCurator;
emit EventsLib.SetCurator(newCurator);
}
function setIsSentinel(address account, bool newIsSentinel) external {
require(msg.sender == owner, ErrorsLib.Unauthorized());
isSentinel[account] = newIsSentinel;
emit EventsLib.SetIsSentinel(account, newIsSentinel);
}
function setName(string memory newName) external {
require(msg.sender == owner, ErrorsLib.Unauthorized());
name = newName;
emit EventsLib.SetName(newName);
}
function setSymbol(string memory newSymbol) external {
require(msg.sender == owner, ErrorsLib.Unauthorized());
symbol = newSymbol;
emit EventsLib.SetSymbol(newSymbol);
}
/* TIMELOCKS FOR CURATOR FUNCTIONS */
/// @dev Will revert if the timelock value is type(uint256).max or any value that overflows when added to the block
/// timestamp.
function submit(bytes calldata data) external {
require(msg.sender == curator, ErrorsLib.Unauthorized());
require(executableAt[data] == 0, ErrorsLib.DataAlreadyPending());
// forge-lint: disable-next-item(unsafe-typecast) we explicitly want only the first bytes4.
bytes4 selector = bytes4(data);
// forge-lint: disable-next-item(unsafe-typecast) we explicitly want only the second bytes4.
uint256 _timelock =
selector == IVaultV2.decreaseTimelock.selector ? timelock[bytes4(data[4:8])] : timelock[selector];
executableAt[data] = block.timestamp + _timelock;
emit EventsLib.Submit(selector, data, executableAt[data]);
}
function timelocked() internal {
bytes4 selector = bytes4(msg.data);
require(executableAt[msg.data] != 0, ErrorsLib.DataNotTimelocked());
require(block.timestamp >= executableAt[msg.data], ErrorsLib.TimelockNotExpired());
require(!abdicated[selector], ErrorsLib.Abdicated());
executableAt[msg.data] = 0;
emit EventsLib.Accept(selector, msg.data);
}
function revoke(bytes calldata data) external {
require(msg.sender == curator || isSentinel[msg.sender], ErrorsLib.Unauthorized());
require(executableAt[data] != 0, ErrorsLib.DataNotTimelocked());
executableAt[data] = 0;
// forge-lint: disable-next-item(unsafe-typecast) we explicitly want only the first bytes4.
bytes4 selector = bytes4(data);
emit EventsLib.Revoke(msg.sender, selector, data);
}
/* CURATOR FUNCTIONS */
function setIsAllocator(address account, bool newIsAllocator) external {
timelocked();
isAllocator[account] = newIsAllocator;
emit EventsLib.SetIsAllocator(account, newIsAllocator);
}
function setReceiveSharesGate(address newReceiveSharesGate) external {
timelocked();
receiveSharesGate = newReceiveSharesGate;
emit EventsLib.SetReceiveSharesGate(newReceiveSharesGate);
}
function setSendSharesGate(address newSendSharesGate) external {
timelocked();
sendSharesGate = newSendSharesGate;
emit EventsLib.SetSendSharesGate(newSendSharesGate);
}
function setReceiveAssetsGate(address newReceiveAssetsGate) external {
timelocked();
receiveAssetsGate = newReceiveAssetsGate;
emit EventsLib.SetReceiveAssetsGate(newReceiveAssetsGate);
}
function setSendAssetsGate(address newSendAssetsGate) external {
timelocked();
sendAssetsGate = newSendAssetsGate;
emit EventsLib.SetSendAssetsGate(newSendAssetsGate);
}
/// @dev The no-op will revert if the registry now returns false for an already added adapter.
function setAdapterRegistry(address newAdapterRegistry) external {
timelocked();
if (newAdapterRegistry != address(0)) {
for (uint256 i = 0; i < adapters.length; i++) {
require(
IAdapterRegistry(newAdapterRegistry).isInRegistry(adapters[i]), ErrorsLib.NotInAdapterRegistry()
);
}
}
adapterRegistry = newAdapterRegistry;
emit EventsLib.SetAdapterRegistry(newAdapterRegistry);
}
function addAdapter(address account) external {
timelocked();
require(
adapterRegistry == address(0) || IAdapterRegistry(adapterRegistry).isInRegistry(account),
ErrorsLib.NotInAdapterRegistry()
);
if (!isAdapter[account]) {
adapters.push(account);
isAdapter[account] = true;
}
emit EventsLib.AddAdapter(account);
}
function removeAdapter(address account) external {
timelocked();
if (isAdapter[account]) {
for (uint256 i = 0; i < adapters.length; i++) {
if (adapters[i] == account) {
adapters[i] = adapters[adapters.length - 1];
adapters.pop();
break;
}
}
isAdapter[account] = false;
}
emit EventsLib.RemoveAdapter(account);
}
/// @dev This function requires great caution because it can irreversibly disable submit for a selector.
/// @dev Existing pending operations submitted before increasing a timelock can still be executed at the initial
/// executableAt.
function increaseTimelock(bytes4 selector, uint256 newDuration) external {
timelocked();
require(selector != IVaultV2.decreaseTimelock.selector, ErrorsLib.AutomaticallyTimelocked());
require(newDuration >= timelock[selector], ErrorsLib.TimelockNotIncreasing());
timelock[selector] = newDuration;
emit EventsLib.IncreaseTimelock(selector, newDuration);
}
function decreaseTimelock(bytes4 selector, uint256 newDuration) external {
timelocked();
require(selector != IVaultV2.decreaseTimelock.selector, ErrorsLib.AutomaticallyTimelocked());
require(newDuration <= timelock[selector], ErrorsLib.TimelockNotDecreasing());
timelock[selector] = newDuration;
emit EventsLib.DecreaseTimelock(selector, newDuration);
}
function abdicate(bytes4 selector) external {
timelocked();
abdicated[selector] = true;
emit EventsLib.Abdicate(selector);
}
function setPerformanceFee(uint256 newPerformanceFee) external {
timelocked();
require(newPerformanceFee <= MAX_PERFORMANCE_FEE, ErrorsLib.FeeTooHigh());
require(performanceFeeRecipient != address(0) || newPerformanceFee == 0, ErrorsLib.FeeInvariantBroken());
accrueInterest();
// forge-lint: disable-next-item(unsafe-typecast) safe because 2**96 > MAX_PERFORMANCE_FEE.
performanceFee = uint96(newPerformanceFee);
emit EventsLib.SetPerformanceFee(newPerformanceFee);
}
function setManagementFee(uint256 newManagementFee) external {
timelocked();
require(newManagementFee <= MAX_MANAGEMENT_FEE, ErrorsLib.FeeTooHigh());
require(managementFeeRecipient != address(0) || newManagementFee == 0, ErrorsLib.FeeInvariantBroken());
accrueInterest();
// forge-lint: disable-next-item(unsafe-typecast) safe because 2**96 > MAX_MANAGEMENT_FEE.
managementFee = uint96(newManagementFee);
emit EventsLib.SetManagementFee(newManagementFee);
}
function setPerformanceFeeRecipient(address newPerformanceFeeRecipient) external {
timelocked();
require(newPerformanceFeeRecipient != address(0) || performanceFee == 0, ErrorsLib.FeeInvariantBroken());
accrueInterest();
performanceFeeRecipient = newPerformanceFeeRecipient;
emit EventsLib.SetPerformanceFeeRecipient(newPerformanceFeeRecipient);
}
function setManagementFeeRecipient(address newManagementFeeRecipient) external {
timelocked();
require(newManagementFeeRecipient != address(0) || managementFee == 0, ErrorsLib.FeeInvariantBroken());
accrueInterest();
managementFeeRecipient = newManagementFeeRecipient;
emit EventsLib.SetManagementFeeRecipient(newManagementFeeRecipient);
}
function increaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external {
timelocked();
bytes32 id = keccak256(idData);
require(newAbsoluteCap >= caps[id].absoluteCap, ErrorsLib.AbsoluteCapNotIncreasing());
caps[id].absoluteCap = newAbsoluteCap.toUint128();
emit EventsLib.IncreaseAbsoluteCap(id, idData, newAbsoluteCap);
}
function decreaseAbsoluteCap(bytes memory idData, uint256 newAbsoluteCap) external {
bytes32 id = keccak256(idData);
require(msg.sender == curator || isSentinel[msg.sender], ErrorsLib.Unauthorized());
require(newAbsoluteCap <= caps[id].absoluteCap, ErrorsLib.AbsoluteCapNotDecreasing());
// forge-lint: disable-next-item(unsafe-typecast) safe because newAbsoluteCap <= absoluteCap < 2**128.
caps[id].absoluteCap = uint128(newAbsoluteCap);
emit EventsLib.DecreaseAbsoluteCap(msg.sender, id, idData, newAbsoluteCap);
}
function increaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external {
timelocked();
bytes32 id = keccak256(idData);
require(newRelativeCap <= WAD, ErrorsLib.RelativeCapAboveOne());
require(newRelativeCap >= caps[id].relativeCap, ErrorsLib.RelativeCapNotIncreasing());
// forge-lint: disable-next-item(unsafe-typecast) safe because WAD < 2**128.
caps[id].relativeCap = uint128(newRelativeCap);
emit EventsLib.IncreaseRelativeCap(id, idData, newRelativeCap);
}
function decreaseRelativeCap(bytes memory idData, uint256 newRelativeCap) external {
bytes32 id = keccak256(idData);
require(msg.sender == curator || isSentinel[msg.sender], ErrorsLib.Unauthorized());
require(newRelativeCap <= caps[id].relativeCap, ErrorsLib.RelativeCapNotDecreasing());
// forge-lint: disable-next-item(unsafe-typecast) safe because WAD < 2**128.
caps[id].relativeCap = uint128(newRelativeCap);
emit EventsLib.DecreaseRelativeCap(msg.sender, id, idData, newRelativeCap);
}
function setForceDeallocatePenalty(address adapter, uint256 newForceDeallocatePenalty) external {
timelocked();
require(newForceDeallocatePenalty <= MAX_FORCE_DEALLOCATE_PENALTY, ErrorsLib.PenaltyTooHigh());
forceDeallocatePenalty[adapter] = newForceDeallocatePenalty;
emit EventsLib.SetForceDeallocatePenalty(adapter, newForceDeallocatePenalty);
}
/* ALLOCATOR FUNCTIONS */
function allocate(address adapter, bytes memory data, uint256 assets) external {
require(isAllocator[msg.sender], ErrorsLib.Unauthorized());
allocateInternal(adapter, data, assets);
}
function allocateInternal(address adapter, bytes memory data, uint256 assets) internal {
require(isAdapter[adapter], ErrorsLib.NotAdapter());
accrueInterest();
SafeERC20Lib.safeTransfer(asset, adapter, assets);
(bytes32[] memory ids, int256 change) = IAdapter(adapter).allocate(data, assets, msg.sig, msg.sender);
for (uint256 i; i < ids.length; i++) {
Caps storage _caps = caps[ids[i]];
_caps.allocation = (int256(_caps.allocation) + change).toUint256();
require(_caps.absoluteCap > 0, ErrorsLib.ZeroAbsoluteCap());
require(_caps.allocation <= _caps.absoluteCap, ErrorsLib.AbsoluteCapExceeded());
require(
_caps.relativeCap == WAD || _caps.allocation <= firstTotalAssets.mulDivDown(_caps.relativeCap, WAD),
ErrorsLib.RelativeCapExceeded()
);
}
emit EventsLib.Allocate(msg.sender, adapter, assets, ids, change);
}
function deallocate(address adapter, bytes memory data, uint256 assets) external {
require(isAllocator[msg.sender] || isSentinel[msg.sender], ErrorsLib.Unauthorized());
deallocateInternal(adapter, data, assets);
}
function deallocateInternal(address adapter, bytes memory data, uint256 assets)
internal
returns (bytes32[] memory)
{
require(isAdapter[adapter], ErrorsLib.NotAdapter());
(bytes32[] memory ids, int256 change) = IAdapter(adapter).deallocate(data, assets, msg.sig, msg.sender);
for (uint256 i; i < ids.length; i++) {
Caps storage _caps = caps[ids[i]];
require(_caps.allocation > 0, ErrorsLib.ZeroAllocation());
_caps.allocation = (int256(_caps.allocation) + change).toUint256();
}
SafeERC20Lib.safeTransferFrom(asset, adapter, address(this), assets);
emit EventsLib.Deallocate(msg.sender, adapter, assets, ids, change);
return ids;
}
/// @dev Whether newLiquidityAdapter is an adapter is checked in allocate/deallocate.
/// @dev newLiquidityData is indexed in the event so it cannot be read from logs.
function setLiquidityAdapterAndData(address newLiquidityAdapter, bytes memory newLiquidityData) external {
require(isAllocator[msg.sender], ErrorsLib.Unauthorized());
liquidityAdapter = newLiquidityAdapter;
liquidityData = newLiquidityData;
emit EventsLib.SetLiquidityAdapterAndData(msg.sender, newLiquidityAdapter, newLiquidityData);
}
function setMaxRate(uint256 newMaxRate) external {
require(isAllocator[msg.sender], ErrorsLib.Unauthorized());
require(newMaxRate <= MAX_MAX_RATE, ErrorsLib.MaxRateTooHigh());
accrueInterest();
// forge-lint: disable-next-item(unsafe-typecast) safe because newMaxRate <= MAX_MAX_RATE < 2**64-1.
maxRate = uint64(newMaxRate);
emit EventsLib.SetMaxRate(newMaxRate);
}
/* EXCHANGE RATE FUNCTIONS */
function accrueInterest() public {
(uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares) = accrueInterestView();
emit EventsLib.AccrueInterest(_totalAssets, newTotalAssets, performanceFeeShares, managementFeeShares);
_totalAssets = newTotalAssets.toUint128();
if (firstTotalAssets == 0) firstTotalAssets = newTotalAssets;
if (performanceFeeShares != 0) createShares(performanceFeeRecipient, performanceFeeShares);
if (managementFeeShares != 0) createShares(managementFeeRecipient, managementFeeShares);
lastUpdate = uint64(block.timestamp);
}
/// @dev Returns newTotalAssets, performanceFeeShares, managementFeeShares.
/// @dev The management fee is not bound to the interest, so it can make the share price go down.
/// @dev The management fees is taken even if the vault incurs some losses.
/// @dev Both fees are rounded down, so fee recipients could receive less than expected.
/// @dev The performance fee is taken on the "distributed interest" (which differs from the "real interest" because
/// of the max rate).
function accrueInterestView() public view returns (uint256, uint256, uint256) {
if (firstTotalAssets != 0) return (_totalAssets, 0, 0);
uint256 elapsed = block.timestamp - lastUpdate;
uint256 realAssets = IERC20(asset).balanceOf(address(this));
for (uint256 i = 0; i < adapters.length; i++) {
realAssets += IAdapter(adapters[i]).realAssets();
}
uint256 maxTotalAssets = _totalAssets + (_totalAssets * elapsed).mulDivDown(maxRate, WAD);
uint256 newTotalAssets = MathLib.min(realAssets, maxTotalAssets);
uint256 interest = newTotalAssets.zeroFloorSub(_totalAssets);
// The performance fee assets may be rounded down to 0 if interest * fee < WAD.
uint256 performanceFeeAssets = interest > 0 && performanceFee > 0 && canReceiveShares(performanceFeeRecipient)
? interest.mulDivDown(performanceFee, WAD)
: 0;
// The management fee is taken on newTotalAssets to make all approximations consistent (interacting less
// increases fees).
uint256 managementFeeAssets = elapsed > 0 && managementFee > 0 && canReceiveShares(managementFeeRecipient)
? (newTotalAssets * elapsed).mulDivDown(managementFee, WAD)
: 0;
// Interest should be accrued at least every 10 years to avoid fees exceeding total assets.
uint256 newTotalAssetsWithoutFees = newTotalAssets - performanceFeeAssets - managementFeeAssets;
uint256 performanceFeeShares =
performanceFeeAssets.mulDivDown(totalSupply + virtualShares, newTotalAssetsWithoutFees + 1);
uint256 managementFeeShares =
managementFeeAssets.mulDivDown(totalSupply + virtualShares, newTotalAssetsWithoutFees + 1);
return (newTotalAssets, performanceFeeShares, managementFeeShares);
}
/// @dev Returns previewed minted shares.
function previewDeposit(uint256 assets) public view returns (uint256) {
(uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares) = accrueInterestView();
uint256 newTotalSupply = totalSupply + performanceFeeShares + managementFeeShares;
return assets.mulDivDown(newTotalSupply + virtualShares, newTotalAssets + 1);
}
/// @dev Returns previewed deposited assets.
function previewMint(uint256 shares) public view returns (uint256) {
(uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares) = accrueInterestView();
uint256 newTotalSupply = totalSupply + performanceFeeShares + managementFeeShares;
return shares.mulDivUp(newTotalAssets + 1, newTotalSupply + virtualShares);
}
/// @dev Returns previewed redeemed shares.
function previewWithdraw(uint256 assets) public view returns (uint256) {
(uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares) = accrueInterestView();
uint256 newTotalSupply = totalSupply + performanceFeeShares + managementFeeShares;
return assets.mulDivUp(newTotalSupply + virtualShares, newTotalAssets + 1);
}
/// @dev Returns previewed withdrawn assets.
function previewRedeem(uint256 shares) public view returns (uint256) {
(uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares) = accrueInterestView();
uint256 newTotalSupply = totalSupply + performanceFeeShares + managementFeeShares;
return shares.mulDivDown(newTotalAssets + 1, newTotalSupply + virtualShares);
}
/// @dev Returns corresponding shares (rounded down).
/// @dev Takes into account performance and management fees.
function convertToShares(uint256 assets) external view returns (uint256) {
return previewDeposit(assets);
}
/// @dev Returns corresponding assets (rounded down).
/// @dev Takes into account performance and management fees.
function convertToAssets(uint256 shares) external view returns (uint256) {
return previewRedeem(shares);
}
/* MAX FUNCTIONS */
/// @dev Gross underestimation because being revert-free cannot be guaranteed when calling the gate.
function maxDeposit(address) external pure returns (uint256) {
return 0;
}
/// @dev Gross underestimation because being revert-free cannot be guaranteed when calling the gate.
function maxMint(address) external pure returns (uint256) {
return 0;
}
/// @dev Gross underestimation because being revert-free cannot be guaranteed when calling the gate.
function maxWithdraw(address) external pure returns (uint256) {
return 0;
}
/// @dev Gross underestimation because being revert-free cannot be guaranteed when calling the gate.
function maxRedeem(address) external pure returns (uint256) {
return 0;
}
/* USER MAIN FUNCTIONS */
/// @dev Returns minted shares.
function deposit(uint256 assets, address onBehalf) external returns (uint256) {
accrueInterest();
uint256 shares = previewDeposit(assets);
enter(assets, shares, onBehalf);
return shares;
}
/// @dev Returns deposited assets.
function mint(uint256 shares, address onBehalf) external returns (uint256) {
accrueInterest();
uint256 assets = previewMint(shares);
enter(assets, shares, onBehalf);
return assets;
}
/// @dev Internal function for deposit and mint.
function enter(uint256 assets, uint256 shares, address onBehalf) internal {
require(canReceiveShares(onBehalf), ErrorsLib.CannotReceiveShares());
require(canSendAssets(msg.sender), ErrorsLib.CannotSendAssets());
SafeERC20Lib.safeTransferFrom(asset, msg.sender, address(this), assets);
createShares(onBehalf, shares);
_totalAssets += assets.toUint128();
emit EventsLib.Deposit(msg.sender, onBehalf, assets, shares);
if (liquidityAdapter != address(0)) allocateInternal(liquidityAdapter, liquidityData, assets);
}
/// @dev Returns redeemed shares.
function withdraw(uint256 assets, address receiver, address onBehalf) public returns (uint256) {
accrueInterest();
uint256 shares = previewWithdraw(assets);
exit(assets, shares, receiver, onBehalf);
return shares;
}
/// @dev Returns withdrawn assets.
function redeem(uint256 shares, address receiver, address onBehalf) external returns (uint256) {
accrueInterest();
uint256 assets = previewRedeem(shares);
exit(assets, shares, receiver, onBehalf);
return assets;
}
/// @dev Internal function for withdraw and redeem.
function exit(uint256 assets, uint256 shares, address receiver, address onBehalf) internal {
require(canSendShares(onBehalf), ErrorsLib.CannotSendShares());
require(canReceiveAssets(receiver), ErrorsLib.CannotReceiveAssets());
uint256 idleAssets = IERC20(asset).balanceOf(address(this));
if (assets > idleAssets && liquidityAdapter != address(0)) {
deallocateInternal(liquidityAdapter, liquidityData, assets - idleAssets);
}
if (msg.sender != onBehalf) {
uint256 _allowance = allowance[onBehalf][msg.sender];
if (_allowance != type(uint256).max) allowance[onBehalf][msg.sender] = _allowance - shares;
}
deleteShares(onBehalf, shares);
_totalAssets -= assets.toUint128();
SafeERC20Lib.safeTransfer(asset, receiver, assets);
emit EventsLib.Withdraw(msg.sender, receiver, onBehalf, assets, shares);
}
/// @dev Returns shares withdrawn as penalty.
/// @dev When calling this function, a penalty is taken from onBehalf, in order to discourage allocation
/// manipulations.
/// @dev The penalty is taken as a withdrawal for which assets are returned to the vault. In consequence,
/// totalAssets is decreased normally along with totalSupply (the share price doesn't change except because of
/// rounding errors), but the amount of assets actually controlled by the vault is not decreased.
/// @dev If a user has A assets in the vault, and that the vault is already fully illiquid, the optimal amount to
/// force deallocate in order to exit the vault is min(liquidity_of_market, A / (1 + penalty)).
/// This ensures that either the market is empty or that it leaves no shares nor liquidity after exiting.
function forceDeallocate(address adapter, bytes memory data, uint256 assets, address onBehalf)
external
returns (uint256)
{
bytes32[] memory ids = deallocateInternal(adapter, data, assets);
uint256 penaltyAssets = assets.mulDivUp(forceDeallocatePenalty[adapter], WAD);
uint256 penaltyShares = withdraw(penaltyAssets, address(this), onBehalf);
emit EventsLib.ForceDeallocate(msg.sender, adapter, assets, onBehalf, ids, penaltyAssets);
return penaltyShares;
}
/* ERC20 FUNCTIONS */
/// @dev Returns success (always true because reverts on failure).
function transfer(address to, uint256 shares) external returns (bool) {
require(to != address(0), ErrorsLib.ZeroAddress());
require(canSendShares(msg.sender), ErrorsLib.CannotSendShares());
require(canReceiveShares(to), ErrorsLib.CannotReceiveShares());
balanceOf[msg.sender] -= shares;
balanceOf[to] += shares;
emit EventsLib.Transfer(msg.sender, to, shares);
return true;
}
/// @dev Returns success (always true because reverts on failure).
function transferFrom(address from, address to, uint256 shares) external returns (bool) {
require(from != address(0), ErrorsLib.ZeroAddress());
require(to != address(0), ErrorsLib.ZeroAddress());
require(canSendShares(from), ErrorsLib.CannotSendShares());
require(canReceiveShares(to), ErrorsLib.CannotReceiveShares());
if (msg.sender != from) {
uint256 _allowance = allowance[from][msg.sender];
if (_allowance != type(uint256).max) {
allowance[from][msg.sender] = _allowance - shares;
emit EventsLib.AllowanceUpdatedByTransferFrom(from, msg.sender, _allowance - shares);
}
}
balanceOf[from] -= shares;
balanceOf[to] += shares;
emit EventsLib.Transfer(from, to, shares);
return true;
}
/// @dev Returns success (always true because reverts on failure).
function approve(address spender, uint256 shares) external returns (bool) {
allowance[msg.sender][spender] = shares;
emit EventsLib.Approval(msg.sender, spender, shares);
return true;
}
/// @dev Signature malleability is not explicitly prevented but it is not a problem thanks to the nonce.
function permit(address _owner, address spender, uint256 shares, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
external
{
require(deadline >= block.timestamp, ErrorsLib.PermitDeadlineExpired());
uint256 nonce = nonces[_owner]++;
bytes32 hashStruct = keccak256(abi.encode(PERMIT_TYPEHASH, _owner, spender, shares, nonce, deadline));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), hashStruct));
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == _owner, ErrorsLib.InvalidSigner());
allowance[_owner][spender] = shares;
emit EventsLib.Approval(_owner, spender, shares);
emit EventsLib.Permit(_owner, spender, shares, nonce, deadline);
}
function createShares(address to, uint256 shares) internal {
require(to != address(0), ErrorsLib.ZeroAddress());
balanceOf[to] += shares;
totalSupply += shares;
emit EventsLib.Transfer(address(0), to, shares);
}
function deleteShares(address from, uint256 shares) internal {
require(from != address(0), ErrorsLib.ZeroAddress());
balanceOf[from] -= shares;
totalSupply -= shares;
emit EventsLib.Transfer(from, address(0), shares);
}
/* PERMISSIONED TOKEN FUNCTIONS */
function canReceiveShares(address account) public view returns (bool) {
return receiveSharesGate == address(0) || IReceiveSharesGate(receiveSharesGate).canReceiveShares(account);
}
function canSendShares(address account) public view returns (bool) {
return sendSharesGate == address(0) || ISendSharesGate(sendSharesGate).canSendShares(account);
}
function canReceiveAssets(address account) public view returns (bool) {
return account == address(this) || receiveAssetsGate == address(0)
|| IReceiveAssetsGate(receiveAssetsGate).canReceiveAssets(account);
}
function canSendAssets(address account) public view returns (bool) {
return sendAssetsGate == address(0) || ISendAssetsGate(sendAssetsGate).canSendAssets(account);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity 0.8.28;
import {VaultV2} from "./VaultV2.sol";
import {IVaultV2Factory} from "./interfaces/IVaultV2Factory.sol";
contract VaultV2Factory is IVaultV2Factory {
mapping(address account => bool) public isVaultV2;
mapping(address owner => mapping(address asset => mapping(bytes32 salt => address))) public vaultV2;
/// @dev Returns the address of the deployed VaultV2.
function createVaultV2(address owner, address asset, bytes32 salt) external returns (address) {
address newVaultV2 = address(new VaultV2{salt: salt}(owner, asset));
isVaultV2[newVaultV2] = true;
vaultV2[owner][asset][salt] = newVaultV2;
emit CreateVaultV2(owner, asset, salt, newVaultV2);
return newVaultV2;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity ^0.8.0;
// Exact struct definitions from Aragon OSx DAOFactory
struct DAOSettings {
address trustedForwarder;
string daoURI;
string subdomain;
bytes metadata;
}
struct PluginSettingsTag {
uint8 release;
uint16 build;
}
struct PluginSetupRef {
PluginSettingsTag versionTag;
address pluginSetupRepo;
}
struct PluginSettings {
PluginSetupRef pluginSetupRef;
bytes data;
}
// InstalledPlugin struct from DAOFactory
struct InstalledPlugin {
address plugin;
IPluginSetup.PreparedSetupData preparedSetupData;
}
struct VotingSettings {
uint8 votingMode;
uint32 supportThreshold;
uint32 minParticipation;
uint32 minApprovalRatio;
uint64 proposalDuration;
uint256 minProposerVotingPower;
}
struct TargetConfig {
address target;
uint8 operation;
}
struct InstallationParameters {
address token;
VotingSettings votingSettings;
bytes pluginMetadata;
address createProposalCaller;
address executeCaller;
TargetConfig targetConfig;
}
/// @title PermissionLib
/// @author Aragon X - 2021-2023
/// @notice A library containing objects for permission processing.
/// @custom:security-contact sirt@aragon.org
library PermissionLib {
/// @notice The types of permission operations available in the `PermissionManager`.
/// @param Grant The grant operation setting a permission without a condition.
/// @param Revoke The revoke operation removing a permission (that was granted with or without a condition).
/// @param GrantWithCondition The grant operation setting a permission with a condition.
enum Operation {
Grant,
Revoke,
GrantWithCondition
}
/// @notice A struct containing the information for a permission to be applied on a single target contract without a condition.
/// @param operation The permission operation type.
/// @param who The address (EOA or contract) receiving the permission.
/// @param permissionId The permission identifier.
struct SingleTargetPermission {
Operation operation;
address who;
bytes32 permissionId;
}
/// @notice A struct containing the information for a permission to be applied on multiple target contracts, optionally, with a condition.
/// @param operation The permission operation type.
/// @param where The address of the target contract for which `who` receives permission.
/// @param who The address (EOA or contract) receiving the permission.
/// @param condition The `PermissionCondition` that will be asked for authorization on calls connected to the specified permission identifier.
/// @param permissionId The permission identifier.
struct MultiTargetPermission {
Operation operation;
address where;
address who;
address condition;
bytes32 permissionId;
}
}
/**
* @title Aragon Interfaces
* @notice Minimal interfaces for interacting with Aragon DAOs
* @dev Used by deployment scripts to interact with deployed DAOs
*/
/**
* @notice Interface for Aragon LockManager (helper contract from LockToVote plugin)
*/
interface ILockManager {
function lock(uint256 _amount) external;
function unlock(uint256 _amount) external;
function vote(uint256 _proposalId, uint8 _voteOption) external;
function getVotingPower(address _account) external view returns (uint256);
}
/**
* @notice Interface for Aragon LockToVote plugin
*/
interface ILockToVotePlugin {
struct Action {
address to;
uint256 value;
bytes data;
}
function createProposal(
bytes calldata _metadata,
Action[] memory _actions,
uint64 _startDate,
uint64 _endDate,
bytes memory _data
) external returns (uint256 proposalId);
function execute(uint256 _proposalId) external;
function canExecute(uint256 _proposalId) external view returns (bool);
}
/**
* @notice Interface for Aragon Multisig plugin
*/
interface IMultisig {
struct Action {
address to;
uint256 value;
bytes data;
}
function createProposal(
bytes calldata _metadata,
Action[] memory _actions,
uint256 _allowFailureMap
) external returns (uint256 proposalId);
function approve(uint256 _proposalId) external;
function execute(uint256 _proposalId) external;
function canApprove(uint256 _proposalId, address _account) external view returns (bool);
function canExecute(uint256 _proposalId) external view returns (bool);
}
/**
* @notice Interface for Aragon DAO
*/
interface IDAO {
function hasPermission(address _where, address _who, bytes32 _permissionId, bytes memory _data) external view returns (bool);
function grant(address _where, address _who, bytes32 _permissionId) external;
function revoke(address _where, address _who, bytes32 _permissionId) external;
function execute(bytes32 _callId, Action[] memory _actions, uint256 _allowFailureMap) external returns (bytes[] memory, uint256);
function ROOT_PERMISSION_ID() external returns (bytes32);
struct Action {
address to;
uint256 value;
bytes data;
}
}
/// @notice DAOFactory interface
interface IDAOFactory {
function createDao(
DAOSettings calldata _daoSettings,
PluginSettings[] calldata _pluginSettings
) external returns (address createdDao, InstalledPlugin[] memory installedPlugins);
function pluginSetupProcessor() external returns (address);
}
interface IPluginSetup {
/// @notice The data associated with a prepared setup.
/// @param helpers The address array of helpers (contracts or EOAs) associated with this plugin version after the installation or update.
/// @param permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcessor` to the installing or updating DAO.
struct PreparedSetupData {
address[] helpers;
PermissionLib.MultiTargetPermission[] permissions;
}
/// @notice The payload for plugin updates and uninstallations containing the existing contracts as well as optional data to be consumed by the plugin setup.
/// @param plugin The address of the `Plugin`.
/// @param currentHelpers The address array of all current helpers (contracts or EOAs) associated with the plugin to update from.
/// @param data The bytes-encoded data containing the input parameters for the preparation of update/uninstall as specified in the corresponding ABI on the version's metadata.
struct SetupPayload {
address plugin;
address[] currentHelpers;
bytes data;
}
/// @notice Prepares the installation of a plugin.
/// @param _dao The address of the installing DAO.
/// @param _data The bytes-encoded data containing the input parameters for the installation as specified in the plugin's build metadata JSON file.
/// @return plugin The address of the `Plugin` contract being prepared for installation.
/// @return preparedSetupData The deployed plugin's relevant data which consists of helpers and permissions.
function prepareInstallation(
address _dao,
bytes calldata _data
) external returns (address plugin, PreparedSetupData memory preparedSetupData);
/// @notice Prepares the update of a plugin.
/// @param _dao The address of the updating DAO.
/// @param _fromBuild The build number of the plugin to update from.
/// @param _payload The relevant data necessary for the `prepareUpdate`. See above.
/// @return initData The initialization data to be passed to upgradeable contracts when the update is applied in the `PluginSetupProcessor`.
/// @return preparedSetupData The deployed plugin's relevant data which consists of helpers and permissions.
function prepareUpdate(
address _dao,
uint16 _fromBuild,
SetupPayload calldata _payload
) external returns (bytes memory initData, PreparedSetupData memory preparedSetupData);
/// @notice Prepares the uninstallation of a plugin.
/// @param _dao The address of the uninstalling DAO.
/// @param _payload The relevant data necessary for the `prepareUninstallation`. See above.
/// @return permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcessor` to the uninstalling DAO.
function prepareUninstallation(
address _dao,
SetupPayload calldata _payload
) external returns (PermissionLib.MultiTargetPermission[] memory permissions);
/// @notice Returns the plugin implementation address.
/// @return The address of the plugin implementation contract.
/// @dev The implementation can be instantiated via the `new` keyword, cloned via the minimal proxy pattern (see [ERC-1167](https://eips.ethereum.org/EIPS/eip-1167)), or proxied via the UUPS proxy pattern (see [ERC-1822](https://eips.ethereum.org/EIPS/eip-1822)).
function implementation() external view returns (address);
}
interface IPluginSetupProcessor {
/// @notice A struct containing information related to plugin setups that have been applied.
/// @param blockNumber The block number at which the `applyInstallation`, `applyUpdate` or `applyUninstallation` was executed.
/// @param currentAppliedSetupId The current setup id that plugin holds. Needed to confirm that `prepareUpdate` or `prepareUninstallation` happens for the plugin's current/valid dependencies.
/// @param preparedSetupIdToBlockNumber The mapping between prepared setup IDs and block numbers at which `prepareInstallation`, `prepareUpdate` or `prepareUninstallation` was executed.
struct PluginState {
uint256 blockNumber;
bytes32 currentAppliedSetupId;
mapping(bytes32 => uint256) preparedSetupIdToBlockNumber;
}
/// @notice The struct containing the parameters for the `prepareInstallation` function.
/// @param pluginSetupRef The reference to the plugin setup to be used for the installation.
/// @param data The bytes-encoded data containing the input parameters for the installation preparation as specified in the corresponding ABI on the version's metadata.
struct PrepareInstallationParams {
PluginSetupRef pluginSetupRef;
bytes data;
}
/// @notice The struct containing the parameters for the `applyInstallation` function.
/// @param pluginSetupRef The reference to the plugin setup used for the installation.
/// @param plugin The address of the plugin contract to be installed.
/// @param permissions The array of multi-targeted permission operations to be applied by the `PluginSetupProcessor` to the DAO.
/// @param helpersHash The hash of helpers that were deployed in `prepareInstallation`. This helps to derive the setup ID.
struct ApplyInstallationParams {
PluginSetupRef pluginSetupRef;
address plugin;
PermissionLib.MultiTargetPermission[] permissions;
bytes32 helpersHash;
}
/// @notice Prepares the installation of a plugin.
/// @param _dao The address of the installing DAO.
/// @param _params The struct containing the parameters for the `prepareInstallation` function.
/// @return plugin The prepared plugin contract address.
/// @return preparedSetupData The data struct containing the array of helper contracts and permissions that the setup has prepared.
function prepareInstallation(
address _dao,
PrepareInstallationParams calldata _params
) external returns (address plugin, IPluginSetup.PreparedSetupData memory preparedSetupData);
/// @notice Applies the permissions of a prepared installation to a DAO.
/// @param _dao The address of the installing DAO.
/// @param _params The struct containing the parameters for the `applyInstallation` function.
function applyInstallation(address _dao, ApplyInstallationParams calldata _params) external;
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity ^0.8.28;
import {IVaultV2} from "@vault-v2/src/interfaces/IVaultV2.sol";
/// @title Revoker
/// @notice Make a sentinel only able to call revoke on a Vault V2
/// #dev The sentinel address to add to the Vault V2 is this contract address.
contract Revoker {
address public sentinel;
IVaultV2 public vault;
constructor(IVaultV2 _vault, address _sentinel) {
sentinel = _sentinel;
vault = _vault;
}
function revoke(bytes memory data) external {
require(msg.sender == sentinel, "Only sentinel can call");
vault.revoke(data);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity ^0.8.28;
import {IVaultV2} from "@vault-v2/src/interfaces/IVaultV2.sol";
library VaultV2Lib {
/// @notice Adds an allocator to a VaultV2 instance, assume 0-day timelocks
function addAllocatorInstant(IVaultV2 vault, address allocator) internal {
bytes memory encoding = abi.encodeWithSelector(vault.setIsAllocator.selector, address(allocator), true);
vault.submit(encoding);
vault.setIsAllocator(address(allocator), true);
}
/// @notice Removes an allocator to a VaultV2 instance, assume 0-day timelocks
function removeAllocatorInstant(IVaultV2 vault, address allocator) internal {
bytes memory encoding = abi.encodeWithSelector(vault.setIsAllocator.selector, address(allocator), false);
vault.submit(encoding);
vault.setIsAllocator(address(allocator), false);
}
/// @notice Adds collateral to a VaultV2 instance, assume 0-day timelocks
function addCollateralInstant(IVaultV2 vault, address adapter, bytes memory data, uint256 absolute, uint256 relative) internal {
bytes memory encoding;
// Accept the adapter
if (!vault.isAdapter(address(adapter))) {
encoding = abi.encodeWithSelector(vault.addAdapter.selector, address(adapter));
vault.submit(encoding);
vault.addAdapter(address(adapter));
}
// Absolute limit
encoding = abi.encodeWithSelector(
vault.increaseAbsoluteCap.selector,
data,
absolute // 100,000 USDC
);
vault.submit(encoding);
vault.increaseAbsoluteCap(data, absolute);
// Relative limit
encoding = abi.encodeWithSelector(
vault.increaseRelativeCap.selector,
data,
relative // 100%
);
vault.submit(encoding);
vault.increaseRelativeCap(data, relative);
}
/// @notice Adds collateral to a VaultV2 instance, assume 0-day timelocks
function setForceDeallocatePenaltyInstant(IVaultV2 vault, address adapter, uint256 penalty) internal {
bytes memory encoding = abi.encodeWithSelector(vault.setForceDeallocatePenalty.selector, address(adapter), penalty);
vault.submit(encoding);
vault.setForceDeallocatePenalty(address(adapter), penalty);
}
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Steakhouse Financial
pragma solidity 0.8.28;
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IBox} from "./interfaces/IBox.sol";
import {IBoxFlashCallback} from "./interfaces/IBox.sol";
import {IFunding, IOracleCallback} from "./interfaces/IFunding.sol";
import {IOracle} from "./interfaces/IOracle.sol";
import {ISwapper} from "./interfaces/ISwapper.sol";
import "./libraries/Constants.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {EventsLib} from "./libraries/EventsLib.sol";
/**
* @title Box
* @notice An ERC4626 vault that holds a base asset, invest in other ERC20 tokens and can borrow/lend via funding modules.
* @dev Features role-based access control, timelocked governance, and slippage protection
* @dev Box is not inflation or donation resistant as deposits are strictly controlled via the isFeeder role.
* @dev Should deposit happen in an automated way (liquidity on a Vault V2) and from multiple feeders, it should be seeded first.
* @dev Oracles can be manipulated to give an unfair price
* @dev It is recommanded to create resiliency by using the BoxAdapterCached
* @dev and/or by using a Vault V2 as a parent vault, which can have a reported price a but lower the NAV price and a setMaxRate()
* @dev During flash operations there is no totalAssets() calculation possible to avoid NAV based attacks
* @dev There is no protection against ERC4626 inflation attacks, as deposits are controlled via the isFeeder role.
* @dev Users shouldn't be able to deposited directly or indirectly to a Box.
* @dev The Box uses forceApprove with 0 value, making it incompatible with the BNB ERC-20 token (reverts on zero-value approvals)
* @dev Token removal can be stopped by sending dust amount of tokens. Can be fixed by deallocating then removing the token atomically
* @dev The epoch-based slippage protection is relative to Box total assets, but a bad allocator can deposit all parent Vault V2
* @dev fund into one Box to temporarily inflate its total asset and extract more value than expected.
*/
contract Box is IBox, ERC20, ReentrancyGuard {
using SafeERC20 for IERC20;
using Math for uint256;
using SafeCast for uint256;
using SafeCast for int256;
// ========== IMMUTABLE STATE ==========
/// @notice Base currency token (e.g., USDC)
address public immutable override asset;
/// @notice Number of decimals for the vault shares (normalized to 18 for assets with fewer decimals)
uint8 private immutable _decimals;
/// @notice Virtual shares used for inflation attack protection
uint256 public immutable virtualShares;
/// @notice Duration of slippage tracking epochs
uint256 public immutable slippageEpochDuration;
/// @notice Duration over which shutdown slippage tolerance increases
uint256 public immutable shutdownSlippageDuration;
/// @notice Duration between shutdown and wind-down phase
uint256 public immutable shutdownWarmup;
// ========== MUTABLE STATE ==========
/// @notice Contract owner with administrative privileges
address public owner;
/// @notice Curator who manages tokens and funding modules
address public curator;
/// @notice Guardian who can trigger shutdowns and revoke timelocked actions
address public guardian;
/// @notice Timestamp when shutdown was triggered, no shutdown if type(uint256).max
uint256 public shutdownTime;
/// @notice Recipient of skimmed tokens that aren't part of the vault's strategy
address public skimRecipient;
/// @notice Tracks which addresses can execute allocation strategies
mapping(address => bool) public isAllocator;
/// @notice Tracks which addresses can deposit into the vault
mapping(address => bool) public isFeeder;
/// @notice List of whitelisted investment tokens
IERC20[] public tokens;
/// @notice Maps each token to its price oracle
mapping(IERC20 => IOracle) public oracles;
/// @notice Maximum allowed slippage per operation and per epoch (scaled by PRECISION = 1e18)
uint256 public maxSlippage;
/// @notice Accumulated slippage within current epoch (scaled by PRECISION = 1e18)
uint256 public accumulatedSlippage;
/// @notice Timestamp when the current slippage tracking epoch started
uint256 public slippageEpochStart;
/// @notice Delay duration for each function selector (in seconds)
mapping(bytes4 => uint256) public timelock;
/// @notice Timestamp when specific calldata becomes executable
mapping(bytes => uint256) public executableAt;
/// @notice List of whitelisted funding modules for borrowing/lending
IFunding[] public fundings;
/// @notice Quick lookup to check if a funding module is whitelisted
mapping(IFunding => bool) internal fundingMap;
/// @notice Depth counter for nested NAV-caching operations (flash and swaps)
uint8 private transient _cachedNavDepth;
/// @notice Cached NAV value during flash and swap operations to prevent manipulation
uint256 private transient _cachedNav;
// ========== CONSTRUCTOR ==========
/**
* @notice Allows the contract to receive native currency
* @dev Required for skimming native currency from funding modules
*/
receive() external payable {}
/**
* @notice Fallback function to receive native currency
* @dev Required for skimming native currency from funding modules
*/
fallback() external payable {}
/**
* @notice Initializes the Box vault
* @param _asset Base currency token (e.g., USDC)
* @param _owner Initial owner address
* @param _curator Initial curator address
* @param _name ERC20 token name
* @param _symbol ERC20 token symbol
* @param _maxSlippage Max allowed slippage for a swap or aggregated over `_slippageEpochDuration`
* @param _slippageEpochDuration Duration for which slippage is measured
* @param _shutdownSlippageDuration When shutdown duration for slippage allowance to widen
* @param _shutdownWarmup Duration between shutdown and wind-down phase
* @dev To mitigate donation attacks, make an initial "dead" deposit after deployment
* (e.g., deposit a small amount and transfer the shares to address(0xdead)).
*/
constructor(
address _asset,
address _owner,
address _curator,
string memory _name,
string memory _symbol,
uint256 _maxSlippage,
uint256 _slippageEpochDuration,
uint256 _shutdownSlippageDuration,
uint256 _shutdownWarmup
) ERC20(_name, _symbol) {
_requireNonZeroAddress(_asset);
_requireNonZeroAddress(_owner);
_requireNonZeroAddress(_curator);
require(_maxSlippage <= MAX_SLIPPAGE_LIMIT, ErrorsLib.SlippageTooHigh());
_requireNotEqual(_slippageEpochDuration, 0);
_requireNotEqual(_shutdownSlippageDuration, 0);
require(_shutdownWarmup <= MAX_SHUTDOWN_WARMUP, ErrorsLib.InvalidValue());
asset = _asset;
owner = _owner;
curator = _curator;
maxSlippage = _maxSlippage;
slippageEpochDuration = _slippageEpochDuration;
shutdownSlippageDuration = _shutdownSlippageDuration;
shutdownWarmup = _shutdownWarmup;
slippageEpochStart = block.timestamp;
shutdownTime = type(uint256).max; // No shutdown initially
// Set up decimals following VaultV2 pattern
uint256 assetDecimals = IERC20Metadata(asset).decimals();
uint256 decimalOffset = uint256(18) > assetDecimals ? uint256(18) - assetDecimals : 0;
_decimals = uint8(assetDecimals + decimalOffset);
virtualShares = 10 ** decimalOffset;
emit EventsLib.BoxCreated(
address(this),
asset,
owner,
curator,
_name,
_symbol,
maxSlippage,
slippageEpochDuration,
shutdownSlippageDuration,
shutdownWarmup
);
emit EventsLib.OwnershipTransferred(address(0), _owner);
emit EventsLib.CuratorUpdated(address(0), _curator);
}
// ========== ERC4626 IMPLEMENTATION ==========
/// @notice Returns the number of decimals for the vault shares
/// @dev Overrides ERC20.decimals() to support assets with different decimal values
function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) {
return _decimals;
}
/// @inheritdoc IERC4626
/// @notice Returns the total value of assets managed by the vault
/// @dev Returns cached NAV during flash and swap operations to prevent manipulation
function totalAssets() public view returns (uint256) {
return _cachedNavDepth > 0 ? _cachedNav : _nav();
}
/// @inheritdoc IERC4626
/// @notice Calculates shares received for a given asset amount
function convertToShares(uint256 assets) public view returns (uint256) {
uint256 supply = totalSupply();
return assets.mulDiv(supply + virtualShares, totalAssets() + 1, Math.Rounding.Floor);
}
/// @inheritdoc IERC4626
/// @notice Calculates assets received for redeeming shares
function convertToAssets(uint256 shares) public view returns (uint256) {
uint256 supply = totalSupply();
return shares.mulDiv(totalAssets() + 1, supply + virtualShares, Math.Rounding.Floor);
}
/// @inheritdoc IERC4626
/// @notice Maximum assets that can be deposited
function maxDeposit(address) external view returns (uint256) {
return (isShutdown()) ? 0 : type(uint256).max;
}
/// @inheritdoc IERC4626
/// @notice Simulates share minting for a deposit
function previewDeposit(uint256 assets) public view returns (uint256) {
return convertToShares(assets);
}
/// @inheritdoc IERC4626
/// @notice Deposits base asset and mints shares to receiver
/// @dev Only authorized feeders can deposit
function deposit(uint256 assets, address receiver) public nonReentrant returns (uint256 shares) {
shares = previewDeposit(assets);
_depositMint(assets, shares, receiver);
}
/// @inheritdoc IERC4626
/// @notice Maximum shares that can be minted
function maxMint(address) external view returns (uint256) {
return (isShutdown()) ? 0 : type(uint256).max;
}
/// @inheritdoc IERC4626
/// @notice Simulates assets needed to mint shares
function previewMint(uint256 shares) public view returns (uint256) {
uint256 supply = totalSupply();
return shares.mulDiv(totalAssets() + 1, supply + virtualShares, Math.Rounding.Ceil);
}
/// @inheritdoc IERC4626
/// @notice Mints exact shares by depositing necessary base asset
/// @dev Only authorized feeders can mint
function mint(uint256 shares, address receiver) external nonReentrant returns (uint256 assets) {
assets = previewMint(shares);
_depositMint(assets, shares, receiver);
}
/// @dev Internal helper for deposit and mint to reduce bytecode duplication
function _depositMint(uint256 assets, uint256 shares, address receiver) internal {
require(_cachedNavDepth == 0, ErrorsLib.ReentryNotAllowed());
_onlyFeeder();
_requireNotShutdown();
_requireNonZeroAddress(receiver);
IERC20(asset).safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
}
/// @inheritdoc IERC4626
/// @notice Maximum assets owner can withdraw
function maxWithdraw(address owner_) external view returns (uint256) {
uint256 ownerAssets = convertToAssets(balanceOf(owner_));
uint256 availableLiquidity = IERC20(asset).balanceOf(address(this));
return ownerAssets < availableLiquidity ? ownerAssets : availableLiquidity;
}
/// @inheritdoc IERC4626
/// @notice Simulates shares burned for withdrawing assets
function previewWithdraw(uint256 assets) public view returns (uint256) {
uint256 supply = totalSupply();
return assets.mulDiv(supply + virtualShares, totalAssets() + 1, Math.Rounding.Ceil);
}
/// @inheritdoc IERC4626
/// @notice Withdraws base asset by burning owner's shares
/// @dev Requires sufficient shares and vault liquidity
function withdraw(uint256 assets, address receiver, address owner_) public nonReentrant returns (uint256 shares) {
shares = previewWithdraw(assets);
_withdrawRedeem(assets, shares, receiver, owner_);
}
/// @inheritdoc IERC4626
/// @notice Maximum shares owner can redeem
function maxRedeem(address owner_) external view returns (uint256) {
uint256 ownerShares = balanceOf(owner_);
uint256 availableLiquidity = IERC20(asset).balanceOf(address(this));
uint256 liquidityShares = convertToShares(availableLiquidity);
return ownerShares < liquidityShares ? ownerShares : liquidityShares;
}
/// @inheritdoc IERC4626
/// @notice Simulates assets received for redeeming shares
function previewRedeem(uint256 shares) public view returns (uint256) {
return convertToAssets(shares);
}
/// @inheritdoc IERC4626
/// @notice Redeems shares for underlying base asset
/// @dev Burns shares and transfers base asset to receiver
function redeem(uint256 shares, address receiver, address owner_) external nonReentrant returns (uint256 assets) {
assets = previewRedeem(shares);
_withdrawRedeem(assets, shares, receiver, owner_);
}
/// @dev Internal helper for withdraw and redeem to reduce bytecode duplication
function _withdrawRedeem(uint256 assets, uint256 shares, address receiver, address owner_) internal {
require(_cachedNavDepth == 0, ErrorsLib.ReentryNotAllowed());
_requireNonZeroAddress(receiver);
if (msg.sender != owner_) {
uint256 allowed = allowance(owner_, msg.sender);
if (allowed < shares) revert ErrorsLib.InsufficientAllowance();
if (allowed != type(uint256).max) {
_approve(owner_, msg.sender, allowed - shares);
}
}
if (balanceOf(owner_) < shares) revert ErrorsLib.InsufficientShares();
if (IERC20(asset).balanceOf(address(this)) < assets) revert ErrorsLib.InsufficientLiquidity();
_burn(owner_, shares);
IERC20(asset).safeTransfer(receiver, assets);
emit Withdraw(msg.sender, receiver, owner_, assets, shares);
}
// ========== SWAP FUNCTIONS ==========
/**
* @notice Transfers accidentally sent tokens to the skim recipient
* @param token Token to skim from the contract
* @dev Cannot skim the base asset or whitelisted investment tokens
*/
function skim(IERC20 token) external nonReentrant {
require(msg.sender == skimRecipient, ErrorsLib.OnlySkimRecipient());
_requireNotEqualAddress(address(token), asset);
require(!isToken(token), ErrorsLib.CannotSkimToken());
uint256 balance;
if (address(token) != address(0)) {
// ERC-20 tokens
balance = token.balanceOf(address(this));
require(balance > 0, ErrorsLib.CannotSkimZero());
token.safeTransfer(skimRecipient, balance);
} else {
// ETH
balance = address(this).balance;
require(balance > 0, ErrorsLib.CannotSkimZero());
(bool ok, ) = skimRecipient.call{value: balance}("");
require(ok, ErrorsLib.TransferFailed());
}
emit EventsLib.Skim(token, skimRecipient, balance);
}
/**
* @notice Swaps base asset for investment tokens
* @param token Target token to acquire
* @param assetsAmount Maximum amount of base asset to spend
* @param swapper Contract that will execute the swap
* @param data Custom data for the swapper implementation
* @return expected Expected amount of target token based on oracle price
* @return received Actual amount of target token received from the allocation
* @dev Enforces slippage protection based on oracle prices
* @dev During wind-down, slippage tolerance increases over time
*/
function allocate(
IERC20 token,
uint256 assetsAmount,
ISwapper swapper,
bytes calldata data
) public nonReentrant returns (uint256 expected, uint256 received) {
_startNavCache();
bool winddown = isWinddown();
require((isAllocator[msg.sender] && !winddown) || (winddown && _debtBalance(token) > 0), ErrorsLib.OnlyAllocatorsOrWinddown());
_requireIsToken(token);
uint256 oraclePrice = oracles[token].price();
uint256 slippageTolerance = winddown ? _winddownSlippageTolerance() : maxSlippage;
if (winddown) {
// Limit allocation to debt shortfall adjusted for slippage tolerance
uint256 debtAmount = _debtBalance(token);
uint256 existingBalance = token.balanceOf(address(this));
uint256 neededTokens = debtAmount > existingBalance ? debtAmount - existingBalance : 0;
uint256 neededValue = neededTokens.mulDiv(oraclePrice, ORACLE_PRECISION, Math.Rounding.Ceil);
uint256 maxAllocation = neededValue.mulDiv(PRECISION, PRECISION - slippageTolerance, Math.Rounding.Ceil);
require(assetsAmount <= maxAllocation, ErrorsLib.InvalidAmount());
}
// Execute swap
(uint256 assetsSpent, uint256 tokensReceived) = _executeSwap(IERC20(asset), token, assetsAmount, swapper, data);
// Calculate and validate slippage
uint256 expectedTokens = assetsAmount.mulDiv(ORACLE_PRECISION, oraclePrice, Math.Rounding.Ceil);
uint256 minTokens = _calculateMinAmount(expectedTokens, slippageTolerance);
require(tokensReceived >= minTokens, ErrorsLib.AllocationTooExpensive());
int256 slippagePct = _calculateSlippagePct(expectedTokens, tokensReceived);
// Track slippage if we are not in winddown and have positive slippage
if (!winddown && tokensReceived < expectedTokens) {
uint256 slippageValue = (expectedTokens - tokensReceived).mulDiv(oraclePrice, ORACLE_PRECISION, Math.Rounding.Ceil);
_increaseSlippage(slippageValue.mulDiv(PRECISION, totalAssets(), Math.Rounding.Ceil));
}
emit EventsLib.Allocation(token, assetsSpent, expectedTokens, tokensReceived, slippagePct, swapper, data);
_endNavCache();
return (expectedTokens, tokensReceived);
}
/**
* @notice Swaps investment tokens back to base asset
* @param token Token to sell
* @param tokensAmount Maximum amount of tokens to sell
* @param swapper Contract that will execute the swap
* @param data Custom data for the swapper implementation
* @return expected Expected amount of base asset based on oracle price
* @return received Actual amount of base asset received from the deallocation
* @dev Enforces slippage protection based on oracle prices
* @dev During wind-down, anyone can deallocate tokens with no outstanding debt
*/
function deallocate(
IERC20 token,
uint256 tokensAmount,
ISwapper swapper,
bytes calldata data
) external nonReentrant returns (uint256 expected, uint256 received) {
_startNavCache();
bool winddown = isWinddown();
require((isAllocator[msg.sender] && !winddown) || (winddown && _debtBalance(token) == 0), ErrorsLib.OnlyAllocatorsOrWinddown());
_requireIsToken(token);
uint256 oraclePrice = oracles[token].price();
uint256 slippageTolerance = winddown ? _winddownSlippageTolerance() : maxSlippage;
// Execute swap
(uint256 tokensSpent, uint256 assetsReceived) = _executeSwap(token, IERC20(asset), tokensAmount, swapper, data);
// Calculate and validate slippage
uint256 expectedAssets = tokensAmount.mulDiv(oraclePrice, ORACLE_PRECISION, Math.Rounding.Ceil);
uint256 minAssets = _calculateMinAmount(expectedAssets, slippageTolerance);
require(assetsReceived >= minAssets, ErrorsLib.TokenSaleNotGeneratingEnoughAssets());
int256 slippagePct = _calculateSlippagePct(expectedAssets, assetsReceived);
// Track slippage if not in winddown and we have positive slippage
if (!winddown && assetsReceived < expectedAssets) {
// slippage is already in asset units
uint256 slippageValue = expectedAssets - assetsReceived;
_increaseSlippage(slippageValue.mulDiv(PRECISION, totalAssets(), Math.Rounding.Ceil));
}
emit EventsLib.Deallocation(token, tokensSpent, expectedAssets, assetsReceived, slippagePct, swapper, data);
_endNavCache();
return (expectedAssets, assetsReceived);
}
/**
* @notice Swaps between two investment tokens directly
* @param from Source token to sell
* @param to Target token to buy
* @param tokensAmount Maximum amount of source token to sell
* @param swapper Contract that will execute the swap
* @param data Custom data for the swapper implementation
* @return expected Expected amount of target token based on oracle prices
* @return received Actual amount of target token received from the reallocation
* @dev More gas efficient than separate deallocate + allocate
*/
function reallocate(
IERC20 from,
IERC20 to,
uint256 tokensAmount,
ISwapper swapper,
bytes calldata data
) external nonReentrant returns (uint256 expected, uint256 received) {
_startNavCache();
_onlyAllocator();
_requireNotWinddown();
_requireIsToken(from);
_requireIsToken(to);
uint256 fromOraclePrice = oracles[from].price();
uint256 toOraclePrice = oracles[to].price();
// Execute swap
(uint256 fromSpent, uint256 toReceived) = _executeSwap(from, to, tokensAmount, swapper, data);
// Calculate expected amounts and validate slippage
uint256 fromValue = tokensAmount.mulDiv(fromOraclePrice, ORACLE_PRECISION, Math.Rounding.Ceil);
uint256 expectedToTokens = fromValue.mulDiv(ORACLE_PRECISION, toOraclePrice, Math.Rounding.Ceil);
uint256 minToTokens = _calculateMinAmount(expectedToTokens, maxSlippage);
require(toReceived >= minToTokens, ErrorsLib.ReallocationSlippageTooHigh());
int256 slippagePct = _calculateSlippagePct(expectedToTokens, toReceived);
// Track slippage if we have positive slippage
// Note: No winddown check needed as reallocate cannot be called during winddown
if (toReceived < expectedToTokens) {
uint256 slippageValue = (expectedToTokens - toReceived).mulDiv(toOraclePrice, ORACLE_PRECISION, Math.Rounding.Ceil);
_increaseSlippage(slippageValue.mulDiv(PRECISION, totalAssets(), Math.Rounding.Ceil));
}
emit EventsLib.Reallocation(from, to, fromSpent, expectedToTokens, toReceived, slippagePct, swapper, data);
_endNavCache();
return (expectedToTokens, toReceived);
}
// ========== FUNDING FUNCTIONS ==========
/**
* @notice Posts collateral to a lending facility
* @param fundingModule Module managing the facility
* @param facilityData Encoded facility identifier
* @param collateralToken Token to pledge as collateral
* @param collateralAmount Amount to pledge
* @dev Transfers tokens to module and updates collateral position
*/
function pledge(
IFunding fundingModule,
bytes calldata facilityData,
IERC20 collateralToken,
uint256 collateralAmount
) external nonReentrant {
_onlyAllocatorNotWinddown();
_requireIsFunding(fundingModule);
collateralToken.safeTransfer(address(fundingModule), collateralAmount);
fundingModule.pledge(facilityData, collateralToken, collateralAmount);
emit EventsLib.Pledge(fundingModule, facilityData, collateralToken, collateralAmount);
}
/**
* @notice Withdraws collateral from a lending facility
* @param fundingModule Module managing the facility
* @param facilityData Encoded facility identifier
* @param collateralToken Token to withdraw
* @param collateralAmount Amount to withdraw (max uint256 = all)
* @dev Returns tokens to vault, must maintain required collateral ratios
*/
function depledge(
IFunding fundingModule,
bytes calldata facilityData,
IERC20 collateralToken,
uint256 collateralAmount
) external nonReentrant {
_onlyAllocatorOrWinddown();
_requireIsFunding(fundingModule);
uint256 pledgeAmount = fundingModule.collateralBalance(facilityData, collateralToken);
if (collateralAmount == type(uint256).max) {
collateralAmount = pledgeAmount;
}
fundingModule.depledge(facilityData, collateralToken, collateralAmount);
emit EventsLib.Depledge(fundingModule, facilityData, collateralToken, collateralAmount);
}
/**
* @notice Takes out a loan from a lending facility
* @param fundingModule Module managing the facility
* @param facilityData Encoded facility identifier
* @param debtToken Token to borrow
* @param borrowAmount Amount to borrow
* @dev Requires sufficient collateral, borrowed tokens sent to vault
*/
function borrow(IFunding fundingModule, bytes calldata facilityData, IERC20 debtToken, uint256 borrowAmount) external nonReentrant {
_onlyAllocatorNotWinddown();
_requireIsFunding(fundingModule);
fundingModule.borrow(facilityData, debtToken, borrowAmount);
emit EventsLib.Borrow(fundingModule, facilityData, debtToken, borrowAmount);
}
/**
* @notice Repays borrowed tokens to a lending facility
* @param fundingModule Module managing the facility
* @param facilityData Encoded facility identifier
* @param debtToken Token to repay
* @param repayAmount Amount to repay (max uint256 = full debt)
* @dev Transfers tokens from vault to module, reduces debt position
*/
function repay(IFunding fundingModule, bytes calldata facilityData, IERC20 debtToken, uint256 repayAmount) external nonReentrant {
_onlyAllocatorOrWinddown();
_requireIsFunding(fundingModule);
uint256 debtAmount = fundingModule.debtBalance(facilityData, debtToken);
if (repayAmount > debtAmount) {
repayAmount = debtAmount;
}
debtToken.safeTransfer(address(fundingModule), repayAmount);
fundingModule.repay(facilityData, debtToken, repayAmount);
emit EventsLib.Repay(fundingModule, facilityData, debtToken, repayAmount);
}
/**
* @notice Recovers non-position tokens from a funding module
* @param fundingModule Module to skim from
* @param token Token to recover
* @dev NAV must remain unchanged to prevent skimming tokenized positions
*/
function skimFunding(IFunding fundingModule, IERC20 token) external nonReentrant {
_onlyAllocatorOrWinddown();
_requireIsFunding(fundingModule);
fundingModule.skim(token);
}
/**
* @notice Provides temporary liquidity for complex operations
* @param flashToken Token to flash loan
* @param flashAmount Amount to provide temporarily
* @param data Custom data passed to the callback
* @dev Caller must implement IBoxFlashCallback; the Box returns the tokens within the same transaction
* @dev NAV is cached during flash to prevent manipulation
*/
function flash(IERC20 flashToken, uint256 flashAmount, bytes calldata data) external {
_onlyAllocatorOrWinddown();
_requireNonZeroAddress(address(flashToken));
_requireIsTokenOrAsset(flashToken);
// Prevent re-entrancy. Can't use nonReentrant modifier because of conflict with allocate/deallocate/reallocate
require(_cachedNavDepth == 0, ErrorsLib.ReentryNotAllowed());
// Cache NAV before starting flash operation for slippage calculations
_startNavCache();
// Transfer flash amount FROM caller TO this contract
flashToken.safeTransferFrom(msg.sender, address(this), flashAmount);
// Call the callback function on the caller
IBoxFlashCallback(msg.sender).onBoxFlash(flashToken, flashAmount, data);
// Repay the flash loan by transferring back TO caller
flashToken.safeTransfer(msg.sender, flashAmount);
_endNavCache();
emit EventsLib.Flash(msg.sender, flashToken, flashAmount);
}
/**
* @notice Executes multiple calls in a single transaction
* @param data Array of encoded function calls
* @dev Allows EOAs to execute multiple operations atomically
*/
function multicall(bytes[] calldata data) external {
uint256 length = data.length;
for (uint256 i = 0; i < length; i++) {
(bool success, bytes memory returnData) = address(this).delegatecall(data[i]);
if (!success) {
assembly ("memory-safe") {
revert(add(32, returnData), mload(returnData))
}
}
}
}
// ========== ADMIN FUNCTIONS ==========
/**
* @notice Sets the address that receives skimmed tokens
* @param newSkimRecipient New recipient address for skimmed tokens
* @dev Only owner can call this function
*/
function setSkimRecipient(address newSkimRecipient) external {
_onlyOwner();
_requireNonZeroAddress(newSkimRecipient);
address oldRecipient = skimRecipient;
_requireNotEqualAddress(newSkimRecipient, oldRecipient);
skimRecipient = newSkimRecipient;
emit EventsLib.SkimRecipientUpdated(oldRecipient, newSkimRecipient);
}
/**
* @notice Transfers ownership of the contract
* @param newOwner Address that will become the new owner
* @dev Immediately transfers all owner privileges
*/
function transferOwnership(address newOwner) external {
_requireNonZeroAddress(newOwner);
address oldOwner = owner;
_onlyOwner();
_requireNotEqualAddress(newOwner, oldOwner);
owner = newOwner;
emit EventsLib.OwnershipTransferred(oldOwner, newOwner);
}
/**
* @notice Sets a new curator for the vault
* @param newCurator Address that will manage tokens and funding
* @dev Only owner can update the curator
*/
function setCurator(address newCurator) external {
_onlyOwner();
_requireNonZeroAddress(newCurator);
_requireNotEqualAddress(newCurator, curator);
address oldCurator = curator;
curator = newCurator;
emit EventsLib.CuratorUpdated(oldCurator, newCurator);
}
/**
* @notice Sets a new guardian with emergency powers
* @param newGuardian Address that can trigger shutdowns and revoke actions
* @dev Requires timelock, only curator can execute
*/
function setGuardian(address newGuardian) external {
_requireNotWinddown();
timelocked();
_onlyCurator();
_requireNonZeroAddress(newGuardian);
_requireNotEqualAddress(newGuardian, guardian);
address oldGuardian = guardian;
guardian = newGuardian;
emit EventsLib.GuardianUpdated(oldGuardian, newGuardian);
}
/**
* @notice Grants or revokes allocator privileges
* @param account Address to modify permissions for
* @param newIsAllocator True to grant allocator role, false to revoke
* @dev Allocators can execute investment strategies
*/
function setIsAllocator(address account, bool newIsAllocator) external {
_onlyCurator();
_requireNonZeroAddress(account);
require(isAllocator[account] != newIsAllocator, ErrorsLib.InvalidValue());
isAllocator[account] = newIsAllocator;
emit EventsLib.AllocatorUpdated(account, newIsAllocator);
}
/**
* @notice Initiates emergency shutdown of the vault
* @dev Stops deposits and starts the wind-down process after warmup period
* @dev Guardian or curator can trigger shutdown
*/
function shutdown() external {
require(msg.sender == guardian || msg.sender == curator, ErrorsLib.OnlyGuardianOrCuratorCanShutdown());
require(!isShutdown(), ErrorsLib.AlreadyShutdown());
shutdownTime = block.timestamp;
emit EventsLib.Shutdown(msg.sender);
}
/**
* @notice Cancels shutdown and returns vault to normal operation
* @dev Only guardian can recover, must be before wind-down phase starts
*/
function recover() external {
require(msg.sender == guardian, ErrorsLib.OnlyGuardianCanRecover());
require(isShutdown(), ErrorsLib.NotShutdown());
require(!isWinddown(), ErrorsLib.CannotRecoverAfterWinddown());
shutdownTime = type(uint256).max;
emit EventsLib.Recover(msg.sender);
}
// ========== TIMELOCK GOVERNANCE ==========
/**
* @notice Submits a function call to the timelock queue
* @param data Encoded function call to be executed after delay
* @dev Delay duration depends on the function selector
* @dev WARNING: Timelock periods are 0 by default. Users and the box owner should ensure that
* sufficiently long timelock periods are set for all critical functions (especially setGuardian)
* to allow the guardian time to react if the curator is compromised.
*/
function submit(bytes calldata data) external {
_onlyCurator();
require(executableAt[data] == 0, ErrorsLib.DataAlreadyTimelocked());
require(data.length >= 4, ErrorsLib.InvalidAmount());
bytes4 selector = bytes4(data);
uint256 delay = selector == IBox.decreaseTimelock.selector ? timelock[bytes4(data[4:8])] : timelock[selector];
require(delay != TIMELOCK_DISABLED, ErrorsLib.FunctionDisabled());
executableAt[data] = block.timestamp + delay;
emit EventsLib.TimelockSubmitted(selector, data, executableAt[data], msg.sender);
}
/**
* @dev Validates and consumes a timelocked transaction
* @dev Checks if current calldata is timelocked and ready for execution
*/
function timelocked() internal {
require(executableAt[msg.data] > 0, ErrorsLib.DataNotTimelocked());
require(block.timestamp >= executableAt[msg.data], ErrorsLib.TimelockNotExpired());
executableAt[msg.data] = 0;
emit EventsLib.TimelockExecuted(bytes4(msg.data), msg.data, msg.sender);
}
/**
* @notice Cancels a pending timelocked transaction
* @param data Encoded function call to cancel
* @dev Guardian or curator can revoke pending transactions
*/
function revoke(bytes calldata data) external {
require(msg.sender == curator || msg.sender == guardian, ErrorsLib.OnlyCuratorOrGuardian());
require(executableAt[data] > 0, ErrorsLib.DataNotTimelocked());
executableAt[data] = 0;
emit EventsLib.TimelockRevoked(bytes4(data), data, msg.sender);
}
/**
* @notice Extends the timelock delay for a function
* @param selector Function signature to modify
* @param newDuration New delay in seconds (must be longer than current)
* @dev No timelock required to increase delays
*/
function increaseTimelock(bytes4 selector, uint256 newDuration) external {
_onlyCurator();
require(newDuration <= TIMELOCK_CAP, ErrorsLib.InvalidTimelock());
require(newDuration > timelock[selector], ErrorsLib.TimelockNotIncreasing());
timelock[selector] = newDuration;
emit EventsLib.TimelockIncreased(selector, newDuration, msg.sender);
}
/**
* @notice Reduces the timelock delay for a function
* @param selector Function signature to modify
* @param newDuration New delay in seconds (must be shorter than current)
* @dev Requires timelock to prevent governance attacks
*/
function decreaseTimelock(bytes4 selector, uint256 newDuration) external {
timelocked();
_onlyCurator();
uint256 currentTimelock = timelock[selector];
require(currentTimelock != TIMELOCK_DISABLED, ErrorsLib.InvalidTimelock());
require(newDuration < currentTimelock, ErrorsLib.TimelockNotDecreasing());
timelock[selector] = newDuration;
emit EventsLib.TimelockDecreased(selector, newDuration, msg.sender);
}
/**
* @notice Permanently disables a function by setting infinite timelock
* @param selector Function signature to disable
* @dev Irreversible - function becomes permanently inaccessible
* @dev Does not impact previsously queued changes
*/
function abdicateTimelock(bytes4 selector) external {
_onlyCurator();
timelock[selector] = TIMELOCK_DISABLED;
emit EventsLib.TimelockIncreased(selector, TIMELOCK_DISABLED, msg.sender);
}
// ========== TIMELOCKED FUNCTIONS ==========
/**
* @notice Grants or revokes deposit privileges
* @param account Address to modify permissions for
* @param newIsFeeder True to allow deposits, false to revoke
* @dev Requires timelock to add feeders
*/
function setIsFeeder(address account, bool newIsFeeder) external {
timelocked();
_requireNonZeroAddress(account);
require(isFeeder[account] != newIsFeeder, ErrorsLib.InvalidValue());
isFeeder[account] = newIsFeeder;
emit EventsLib.FeederUpdated(account, newIsFeeder);
}
/**
* @notice Sets the maximum tolerated slippage for swaps
* @param newMaxSlippage New limit scaled by PRECISION (e.g., 0.01e18 = 1%)
* @dev Requires timelock, applies per-swap and per-epoch
*/
function setMaxSlippage(uint256 newMaxSlippage) external {
timelocked();
require(newMaxSlippage <= MAX_SLIPPAGE_LIMIT, ErrorsLib.SlippageTooHigh());
_requireNotEqual(newMaxSlippage, maxSlippage);
uint256 oldMaxSlippage = maxSlippage;
maxSlippage = newMaxSlippage;
emit EventsLib.MaxSlippageUpdated(oldMaxSlippage, newMaxSlippage);
}
/**
* @notice Whitelists a new investment token
* @param token Token contract to add
* @param oracle Price feed for the token
* @dev Requires timelock, oracle must return prices in base asset terms
*/
function addToken(IERC20 token, IOracle oracle) external {
timelocked();
_requireNonZeroAddress(address(token));
_requireNotEqualAddress(address(token), asset);
require(address(oracle) != address(0), ErrorsLib.OracleRequired());
require(!isToken(token), ErrorsLib.TokenAlreadyWhitelisted());
require(tokens.length < MAX_TOKENS, ErrorsLib.TooManyTokens());
tokens.push(token);
oracles[token] = oracle;
emit EventsLib.TokenAdded(token, oracle);
}
/**
* @notice Removes a token from the whitelist
* @param token Token to delist
* @dev Token balance must be zero and not used in any funding module
*/
function removeToken(IERC20 token) external {
_onlyCurator();
_requireIsToken(token);
require(token.balanceOf(address(this)) == 0, ErrorsLib.TokenBalanceMustBeZero());
require(!_isTokenUsedInFunding(token), ErrorsLib.CannotRemove());
uint256 length = tokens.length;
for (uint256 i; i < length; i++) {
if (tokens[i] == token) {
tokens[i] = tokens[length - 1];
tokens.pop();
break;
}
}
delete oracles[token];
emit EventsLib.TokenRemoved(token);
}
/**
* @notice Updates the price oracle for a whitelisted token
* @param token Token to update oracle for
* @param oracle New price feed contract
* @dev Requires timelock in normal operation, guardian can update during final wind-down
*/
function changeTokenOracle(IERC20 token, IOracle oracle) external {
if (isWinddown()) {
require(block.timestamp >= shutdownTime + shutdownWarmup + shutdownSlippageDuration, ErrorsLib.NotAllowed());
_onlyGuardian();
} else {
_onlyCurator();
timelocked();
}
_requireNonZeroAddress(address(oracle));
_requireIsToken(token);
_requireNotEqualAddress(address(oracles[token]), address(oracle));
oracles[token] = oracle;
emit EventsLib.TokenOracleChanged(token, oracle);
}
/**
* @notice Adds a new funding module for borrowing/lending
* @param fundingModule Module contract to whitelist
* @dev Module must be empty with no facilities, collateral, or debt
*/
function addFunding(IFunding fundingModule) external {
timelocked();
require(!fundingMap[fundingModule], ErrorsLib.AlreadyWhitelisted());
_requireNonZeroAddress(address(fundingModule));
// Check that Box is the owner of the funding module
(bool success, bytes memory data) = address(fundingModule).staticcall(abi.encodeWithSignature("owner()"));
require(success && data.length == 32, ErrorsLib.InvalidValue());
address fundingOwner = abi.decode(data, (address));
require(fundingOwner == address(this), ErrorsLib.InvalidValue());
require(fundingModule.facilitiesLength() == 0, ErrorsLib.NotClean());
require(fundingModule.collateralTokensLength() == 0, ErrorsLib.NotClean());
require(fundingModule.debtTokensLength() == 0, ErrorsLib.NotClean());
fundingMap[fundingModule] = true;
fundings.push(fundingModule);
emit EventsLib.FundingModuleAdded(fundingModule);
}
/**
* @notice Registers a lending facility within a funding module
* @param fundingModule Module to add facility to
* @param facilityData Encoded facility parameters
* @dev Requires timelock, facility specifics depend on module implementation
*/
function addFundingFacility(IFunding fundingModule, bytes calldata facilityData) external {
timelocked();
_requireIsFunding(fundingModule);
fundingModule.addFacility(facilityData);
emit EventsLib.FundingFacilityAdded(fundingModule, facilityData);
}
/**
* @notice Enables a token as collateral in a funding module
* @param fundingModule Module to configure
* @param collateralToken Token to use as collateral
* @dev Token must be whitelisted in the vault first
*/
function addFundingCollateral(IFunding fundingModule, IERC20 collateralToken) external {
timelocked();
_requireIsFunding(fundingModule);
_requireIsTokenOrAsset(collateralToken);
fundingModule.addCollateralToken(collateralToken);
emit EventsLib.FundingCollateralAdded(fundingModule, collateralToken);
}
/**
* @notice Enables a token for borrowing in a funding module
* @param fundingModule Module to configure
* @param debtToken Token that can be borrowed
* @dev Token must be whitelisted in the vault first
*/
function addFundingDebt(IFunding fundingModule, IERC20 debtToken) external {
timelocked();
_requireIsFunding(fundingModule);
_requireIsTokenOrAsset(debtToken);
fundingModule.addDebtToken(debtToken);
emit EventsLib.FundingDebtAdded(fundingModule, debtToken);
}
/**
* @notice Removes a funding module from the vault
* @param fundingModule Module to remove
* @dev Module must be empty with no active facilities, collateral, or debt
*/
function removeFunding(IFunding fundingModule) external {
_onlyCurator();
_requireIsFunding(fundingModule);
require(fundingModule.facilitiesLength() == 0, ErrorsLib.CannotRemove());
require(fundingModule.collateralTokensLength() == 0, ErrorsLib.CannotRemove());
require(fundingModule.debtTokensLength() == 0, ErrorsLib.CannotRemove());
fundingMap[fundingModule] = false;
uint256 index = _findFundingIndex(fundingModule);
fundings[index] = fundings[fundings.length - 1];
fundings.pop();
emit EventsLib.FundingModuleRemoved(fundingModule);
}
/**
* @notice Deregisters a lending facility from a funding module
* @param fundingModule Module containing the facility
* @param facilityData Encoded facility identifier
* @dev Facility must have no outstanding positions
*/
function removeFundingFacility(IFunding fundingModule, bytes calldata facilityData) external {
_onlyCurator();
_requireIsFunding(fundingModule);
fundingModule.removeFacility(facilityData);
emit EventsLib.FundingFacilityRemoved(fundingModule, facilityData);
}
/**
* @notice Disables a token as collateral in a funding module
* @param fundingModule Module to update
* @param collateralToken Token to remove from collateral list
* @dev Token must not be actively used as collateral
*/
function removeFundingCollateral(IFunding fundingModule, IERC20 collateralToken) external {
_onlyCurator();
_requireIsFunding(fundingModule);
fundingModule.removeCollateralToken(collateralToken);
emit EventsLib.FundingCollateralRemoved(fundingModule, collateralToken);
}
/**
* @notice Disables borrowing of a token in a funding module
* @param fundingModule Module to update
* @param debtToken Token to remove from debt list
* @dev No outstanding debt must exist for this token
*/
function removeFundingDebt(IFunding fundingModule, IERC20 debtToken) external {
_onlyCurator();
_requireIsFunding(fundingModule);
fundingModule.removeDebtToken(debtToken);
emit EventsLib.FundingDebtRemoved(fundingModule, debtToken);
}
// ========== VIEW FUNCTIONS ==========
/**
* @notice Checks if a token is whitelisted for investment
* @param token Token to check
* @return True if the token has an associated oracle
*/
function isToken(IERC20 token) public view returns (bool) {
return address(oracles[token]) != address(0);
}
/**
* @notice Checks if a token is the base asset or a whitelisted token
* @param token Token to check
* @return True if it's the base asset or has an oracle
*/
function isTokenOrAsset(IERC20 token) public view returns (bool) {
return address(token) == asset || address(oracles[token]) != address(0);
}
/**
* @notice Gets the count of whitelisted tokens
* @return Number of tokens in the investment list
*/
function tokensLength() external view returns (uint256) {
return tokens.length;
}
/**
* @notice Checks if a funding module is authorized
* @param fundingModule Module to verify
* @return True if the module can be used for borrowing/lending
*/
function isFunding(IFunding fundingModule) public view returns (bool) {
return fundingMap[fundingModule];
}
/**
* @notice Gets the count of active funding modules
* @return Number of modules available for borrowing/lending
*/
function fundingsLength() external view override returns (uint256) {
return fundings.length;
}
/**
* @notice Checks if the vault is in shutdown mode
* @return True if shutdown has been triggered
*/
function isShutdown() public view returns (bool) {
return shutdownTime != type(uint256).max;
}
/**
* @notice Checks if the vault has entered wind-down phase
* @return True if past the warmup period after shutdown
*/
function isWinddown() public view returns (bool) {
return shutdownTime != type(uint256).max && block.timestamp >= shutdownTime + shutdownWarmup;
}
// ========== INTERNAL FUNCTIONS ==========
/**
* @dev Checks if msg.sender is the owner
*/
function _onlyOwner() internal view {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
}
/**
* @dev Checks if msg.sender is the curator
*/
function _onlyCurator() internal view {
require(msg.sender == curator, ErrorsLib.OnlyCurator());
}
/**
* @dev Checks that an address is not zero
*/
function _requireNonZeroAddress(address addr) internal pure {
require(addr != address(0), ErrorsLib.InvalidAddress());
}
/**
* @dev Checks if a token is whitelisted
*/
function _requireIsToken(IERC20 token) internal view {
require(isToken(token), ErrorsLib.TokenNotWhitelisted());
}
/**
* @dev Checks if a funding module is whitelisted
*/
function _requireIsFunding(IFunding fundingModule) internal view {
require(isFunding(fundingModule), ErrorsLib.NotWhitelisted());
}
/**
* @dev Checks that vault is not in winddown
*/
function _requireNotWinddown() internal view {
require(!isWinddown(), ErrorsLib.CannotDuringWinddown());
}
/**
* @dev Checks if msg.sender is allocator and not in winddown
*/
function _onlyAllocatorNotWinddown() internal view {
require(isAllocator[msg.sender] && !isWinddown(), ErrorsLib.OnlyAllocators());
}
/**
* @dev Checks if msg.sender is allocator or in winddown
*/
function _onlyAllocatorOrWinddown() internal view {
require(isAllocator[msg.sender] || isWinddown(), ErrorsLib.OnlyAllocatorsOrWinddown());
}
/**
* @dev Checks that two values are not equal
*/
function _requireNotEqual(uint256 a, uint256 b) internal pure {
require(a != b, ErrorsLib.InvalidValue());
}
/**
* @dev Checks that two addresses are not equal
*/
function _requireNotEqualAddress(address a, address b) internal pure {
require(a != b, ErrorsLib.InvalidValue());
}
/**
* @dev Checks if msg.sender is the guardian
*/
function _onlyGuardian() internal view {
require(msg.sender == guardian, ErrorsLib.OnlyGuardian());
}
/**
* @dev Checks if msg.sender is allocator
*/
function _onlyAllocator() internal view {
require(isAllocator[msg.sender], ErrorsLib.OnlyAllocators());
}
/**
* @dev Checks if msg.sender is feeder
*/
function _onlyFeeder() internal view {
require(isFeeder[msg.sender], ErrorsLib.OnlyFeeders());
}
/**
* @dev Checks that vault is not shutdown
*/
function _requireNotShutdown() internal view {
require(!isShutdown(), ErrorsLib.CannotDuringShutdown());
}
/**
* @dev Checks if token is whitelisted or is the base asset
*/
function _requireIsTokenOrAsset(IERC20 token) internal view {
require(isTokenOrAsset(token), ErrorsLib.TokenNotWhitelisted());
}
/**
* @dev Starts NAV caching for the current operation
* @dev Caches NAV on first call (depth 0 -> 1), increments depth on nested calls
* @dev Properly handles nesting when swaps are called from flash callbacks
*/
function _startNavCache() internal {
if (_cachedNavDepth == 0) {
_cachedNav = _nav();
}
_cachedNavDepth++;
}
/**
* @dev Ends NAV caching for the current operation
* @dev Decrements the depth counter
*/
function _endNavCache() internal {
_cachedNavDepth--;
}
/**
* @dev Executes a swap through a swapper contract with approval management
* @param fromToken Token to sell
* @param toToken Token to buy
* @param maxAmount Maximum amount of fromToken to sell
* @param swapper Swapper contract to execute the trade
* @param data Custom data for the swapper
* @return spent Actual amount of fromToken spent
* @return received Amount of toToken received
*/
function _executeSwap(
IERC20 fromToken,
IERC20 toToken,
uint256 maxAmount,
ISwapper swapper,
bytes calldata data
) internal returns (uint256 spent, uint256 received) {
uint256 fromBefore = fromToken.balanceOf(address(this));
uint256 toBefore = toToken.balanceOf(address(this));
fromToken.forceApprove(address(swapper), maxAmount);
swapper.sell(fromToken, toToken, maxAmount, data);
fromToken.forceApprove(address(swapper), 0);
spent = fromBefore - fromToken.balanceOf(address(this));
received = toToken.balanceOf(address(this)) - toBefore;
require(spent <= maxAmount, ErrorsLib.SwapperDidSpendTooMuch());
}
/**
* @dev Calculates slippage percentage from expected vs actual amounts
* @param expectedAmount Expected amount based on oracle price
* @param actualAmount Actual amount received/spent
* @return slippagePct Slippage as a percentage scaled by PRECISION
*/
function _calculateSlippagePct(uint256 expectedAmount, uint256 actualAmount) internal pure returns (int256 slippagePct) {
if (expectedAmount == 0) return 0;
int256 slippage = expectedAmount.toInt256() - actualAmount.toInt256();
// Round up when calculating slippage to be conservative
if (slippage >= 0) {
// Positive slippage (loss): round up
slippagePct = int256(uint256(slippage).mulDiv(PRECISION, expectedAmount, Math.Rounding.Ceil));
} else {
// Negative slippage (gain): round down (more conservative)
slippagePct = -int256(uint256(-slippage).mulDiv(PRECISION, expectedAmount, Math.Rounding.Floor));
}
}
/**
* @dev Calculates minimum acceptable amount based on slippage tolerance
* @param expectedAmount Expected amount based on oracle price
* @param tolerance Maximum allowed slippage scaled by PRECISION
* @return minAmount Minimum acceptable amount after slippage
*/
function _calculateMinAmount(uint256 expectedAmount, uint256 tolerance) internal pure returns (uint256 minAmount) {
minAmount = expectedAmount.mulDiv(PRECISION - tolerance, PRECISION, Math.Rounding.Ceil);
}
/**
* @dev Tracks slippage within current epoch and enforces limits
* @param slippagePct Slippage to add scaled by PRECISION
* @dev Resets epoch if duration has passed
*/
function _increaseSlippage(uint256 slippagePct) internal {
// Reset epoch if expired
if (block.timestamp >= slippageEpochStart + slippageEpochDuration) {
slippageEpochStart = block.timestamp;
accumulatedSlippage = slippagePct;
emit EventsLib.SlippageEpochReset(block.timestamp);
} else {
accumulatedSlippage += slippagePct;
}
require(accumulatedSlippage < maxSlippage, ErrorsLib.TooMuchAccumulatedSlippage());
emit EventsLib.SlippageAccumulated(slippagePct, accumulatedSlippage);
}
/**
* @dev Calculates total vault value across all positions
* @return nav Sum of base asset, token values, and funding positions
* @dev Negative funding NAV is floored to zero
* @dev Reverts if called during NAV-cached operations (swaps or flash) to prevent read-only reentrancy
*/
function _nav() internal view returns (uint256 nav) {
require(_cachedNavDepth == 0, ErrorsLib.NoNavDuringCache());
nav = IERC20(asset).balanceOf(address(this));
// Add value of all tokens
uint256 length = tokens.length;
for (uint256 i; i < length; i++) {
IERC20 token = tokens[i];
IOracle oracle = oracles[token];
uint256 tokenBalance = token.balanceOf(address(this));
if (tokenBalance > 0) {
nav += tokenBalance.mulDiv(oracle.price(), ORACLE_PRECISION);
}
}
// Loop over funding sources
length = fundings.length;
for (uint256 i; i < length; i++) {
IFunding funding = fundings[i];
nav += funding.nav(IOracleCallback(address(this)));
}
}
/**
* @dev Calculates dynamic slippage tolerance during wind-down
* @return Slippage limit that increases linearly over shutdown duration
* @dev Returns up to 1% slippage after full duration
*/
function _winddownSlippageTolerance() internal view returns (uint256) {
uint256 timeElapsed = block.timestamp - shutdownWarmup - shutdownTime;
return
(timeElapsed < shutdownSlippageDuration)
? timeElapsed.mulDiv(MAX_SLIPPAGE_LIMIT, shutdownSlippageDuration)
: MAX_SLIPPAGE_LIMIT;
}
/**
* @dev Locates a funding module's position in the array
* @param fundingModule Module to find
* @return Index in the fundings array
* @dev Reverts if module is not whitelisted
*/
function _findFundingIndex(IFunding fundingModule) internal view returns (uint256) {
uint256 length = fundings.length;
for (uint256 i = 0; i < length; i++) {
if (fundings[i] == fundingModule) {
return i;
}
}
revert ErrorsLib.NotWhitelisted();
}
/**
* @dev Checks if a token is used in any funding module
* @param token Token to check
* @return True if token is used as collateral or debt anywhere
*/
function _isTokenUsedInFunding(IERC20 token) internal view returns (bool) {
uint256 length = fundings.length;
for (uint256 i; i < length; i++) {
IFunding funding = fundings[i];
if (funding.isCollateralToken(token) || funding.isDebtToken(token)) {
return true;
}
}
return false;
}
/**
* @dev Sums outstanding debt for a token across all modules
* @param debtToken Token to check debt for
* @return totalDebt Combined debt balance from all facilities
*/
function _debtBalance(IERC20 debtToken) internal view returns (uint256 totalDebt) {
uint256 length = fundings.length;
for (uint256 i; i < length; i++) {
IFunding funding = fundings[i];
totalDebt += funding.debtBalance(debtToken);
}
}
}// 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, IOracleCallback} 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, IOracleCallback {
/* FUNCTIONS */
// ========== STATE FUNCTIONS ==========
function asset() external view override(IERC4626, IOracleCallback) 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 skimFunding(IFunding fundingModule, IERC20 token) external;
function allocate(
IERC20 token,
uint256 assetsAmount,
ISwapper swapper,
bytes calldata data
) external returns (uint256 expected, uint256 received);
function deallocate(
IERC20 token,
uint256 tokensAmount,
ISwapper swapper,
bytes calldata data
) external returns (uint256 expected, uint256 received);
function reallocate(
IERC20 from,
IERC20 to,
uint256 tokensAmount,
ISwapper swapper,
bytes calldata data
) external returns (uint256 expected, uint256 received);
// ========== 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;
// ========== BATCHING ==========
function multicall(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);
}// SPDX-License-Identifier: UNLICENSED
// Copyright (c) 2025 Steakhouse Financial
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Box} from "./../Box.sol";
import {IBox} from "./../interfaces/IBox.sol";
import {IBoxFactory} from "./../interfaces/IBoxFactory.sol";
contract BoxFactory is IBoxFactory {
/* STORAGE */
mapping(address => bool) public isBox;
/* FUNCTIONS */
/// @dev Returns the address of the deployed Box
function createBox(
IERC20 _asset,
address _owner,
address _curator,
string memory _name,
string memory _symbol,
uint256 _maxSlippage,
uint256 _slippageEpochDuration,
uint256 _shutdownSlippageDuration,
uint256 _shutdownWarmup,
bytes32 salt
) external returns (IBox) {
IBox _box = new Box{salt: salt}(
address(_asset),
_owner,
_curator,
_name,
_symbol,
_maxSlippage,
_slippageEpochDuration,
_shutdownSlippageDuration,
_shutdownWarmup
);
isBox[address(_box)] = true;
emit BoxCreated(
_box,
_asset,
_owner,
_curator,
_name,
_symbol,
_maxSlippage,
_slippageEpochDuration,
_shutdownSlippageDuration,
_shutdownWarmup
);
return _box;
}
}// 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.
/// @dev Uses CREATE2 with fixed salt to prevent duplicate adapters for the same (parentVault, box) pair.
function createBoxAdapter(address parentVault, IBox box) external returns (IBoxAdapter) {
BoxAdapter _boxAdapter = new BoxAdapter{salt: bytes32(0)}(parentVault, box);
boxAdapter[parentVault][box] = _boxAdapter;
isBoxAdapter[address(_boxAdapter)] = true;
emit CreateBoxAdapter(parentVault, address(box), _boxAdapter);
return _boxAdapter;
}
}// 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);
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity 0.8.28;
import {IMorpho, Id, MarketParams, Position} from "@morpho-blue/interfaces/IMorpho.sol";
import "@morpho-blue/libraries/ConstantsLib.sol";
import {MarketParamsLib} from "@morpho-blue/libraries/MarketParamsLib.sol";
import {MorphoBalancesLib} from "@morpho-blue/libraries/periphery/MorphoBalancesLib.sol";
import {MorphoLib} from "@morpho-blue/libraries/periphery/MorphoLib.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {MathLib} from "./../lib/morpho-blue/src/libraries/MathLib.sol";
import {FundingBase} from "./FundingBase.sol";
import {IOracleCallback} from "./interfaces/IFunding.sol";
import {IOracle} from "./interfaces/IOracle.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
contract FundingMorpho is FundingBase {
using SafeERC20 for IERC20;
using MarketParamsLib for MarketParams;
using MorphoBalancesLib for IMorpho;
using MorphoLib for IMorpho;
using MathLib for uint256;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.AddressSet;
IMorpho public immutable morpho;
uint256 public immutable lltvCap; // Maximum LTV/LTTV ration in 18 decimals, e.g. 80e16 for 80%
mapping(bytes32 => bytes) public facilityDataMap; // hash => facility data
constructor(address owner_, address morpho_, uint256 lltvCap_) FundingBase(owner_) {
require(morpho_ != address(0), ErrorsLib.InvalidAddress());
require(lltvCap_ <= 100e16, ErrorsLib.InvalidValue()); // Max 100%
require(lltvCap_ > 0, ErrorsLib.InvalidValue()); // Min above 0%
morpho = IMorpho(morpho_);
lltvCap = lltvCap_;
}
// ========== ADMIN ==========
/// @dev Before adding a facility, you need to add the underlying tokens as collateral/debt tokens
function addFacility(bytes calldata facilityData) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
MarketParams memory market = decodeFacilityData(facilityData);
require(isCollateralToken(IERC20(market.collateralToken)), ErrorsLib.TokenNotWhitelisted());
require(isDebtToken(IERC20(market.loanToken)), ErrorsLib.TokenNotWhitelisted());
bytes32 facilityHash = keccak256(facilityData);
require(facilitiesSet.add(facilityHash), ErrorsLib.AlreadyWhitelisted());
facilityDataMap[facilityHash] = facilityData;
}
function removeFacility(bytes calldata facilityData) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(!_isFacilityUsed(facilityData), ErrorsLib.CannotRemove());
bytes32 facilityHash = keccak256(facilityData);
require(facilitiesSet.remove(facilityHash), ErrorsLib.NotWhitelisted());
delete facilityDataMap[facilityHash];
}
function facilities(uint256 index) external view returns (bytes memory) {
bytes32 facilityHash = facilitiesSet.at(index);
return facilityDataMap[facilityHash];
}
/// @dev Before being able to remove a collateral, no facility should reference it and the balance should be 0
function removeCollateralToken(IERC20 collateralToken) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(_collateralBalance(collateralToken) == 0, ErrorsLib.CannotRemove());
uint256 length = facilitiesSet.length();
for (uint i = 0; i < length; i++) {
bytes32 facilityHash = facilitiesSet.at(i);
MarketParams memory market = decodeFacilityData(facilityDataMap[facilityHash]);
require(address(market.collateralToken) != address(collateralToken), ErrorsLib.CannotRemove());
}
require(collateralTokensSet.remove(address(collateralToken)), ErrorsLib.NotWhitelisted());
}
/// @dev Before being able to remove a debt, no facility should reference it and the balance should be 0
function removeDebtToken(IERC20 debtToken) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(_debtBalance(debtToken) == 0, ErrorsLib.CannotRemove());
uint256 length = facilitiesSet.length();
for (uint i = 0; i < length; i++) {
bytes32 facilityHash = facilitiesSet.at(i);
MarketParams memory market = decodeFacilityData(facilityDataMap[facilityHash]);
require(address(market.loanToken) != address(debtToken), ErrorsLib.CannotRemove());
}
require(debtTokensSet.remove(address(debtToken)), ErrorsLib.NotWhitelisted());
}
// ========== ACTIONS ==========
/// @dev Assume caller did transfer the collateral tokens to this contract before calling
function pledge(bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(isFacility(facilityData), ErrorsLib.NotWhitelisted());
require(isCollateralToken(collateralToken), ErrorsLib.TokenNotWhitelisted());
MarketParams memory market = decodeFacilityData(facilityData);
require(address(collateralToken) == market.collateralToken, "FundingModuleMorpho: Wrong collateral token");
collateralToken.forceApprove(address(morpho), collateralAmount);
morpho.supplyCollateral(market, collateralAmount, address(this), "");
}
function depledge(bytes calldata facilityData, IERC20 collateralToken, uint256 collateralAmount) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(isFacility(facilityData), ErrorsLib.NotWhitelisted());
require(isCollateralToken(collateralToken), ErrorsLib.TokenNotWhitelisted());
MarketParams memory market = decodeFacilityData(facilityData);
require(address(collateralToken) == market.collateralToken, "FundingModuleMorpho: Wrong collateral token");
morpho.withdrawCollateral(market, collateralAmount, address(this), owner);
require(ltv(facilityData) <= (market.lltv * lltvCap) / 100e16, ErrorsLib.ExcessiveLTV());
}
function borrow(bytes calldata facilityData, IERC20 debtToken, uint256 borrowAmount) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(isFacility(facilityData), ErrorsLib.NotWhitelisted());
require(isDebtToken(debtToken), ErrorsLib.TokenNotWhitelisted());
MarketParams memory market = decodeFacilityData(facilityData);
require(address(debtToken) == market.loanToken, "FundingModuleMorpho: Wrong debt token");
morpho.borrow(market, borrowAmount, 0, address(this), owner);
require(ltv(facilityData) <= (market.lltv * lltvCap) / 100e16, ErrorsLib.ExcessiveLTV());
}
/// @dev Assume caller did transfer the debt tokens to this contract before calling
function repay(bytes calldata facilityData, IERC20 debtToken, uint256 repayAmount) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(isFacility(facilityData), ErrorsLib.NotWhitelisted());
require(isDebtToken(debtToken), ErrorsLib.TokenNotWhitelisted());
MarketParams memory market = decodeFacilityData(facilityData);
require(address(debtToken) == market.loanToken, "FundingModuleMorpho: Wrong debt token");
uint256 debtAmount = morpho.expectedBorrowAssets(market, address(this));
debtToken.forceApprove(address(morpho), repayAmount);
// If the amount repaid is all the debt, we convert to all shares
// amount repaid would internally get translated to more shares that there is to repaid
if (repayAmount == debtAmount) {
morpho.repay(market, 0, morpho.borrowShares(market.id(), address(this)), address(this), "");
} else {
morpho.repay(market, repayAmount, 0, address(this), "");
}
}
// ========== POSITION ==========
/// @dev returns 0 if there is no collateral
function ltv(bytes calldata facilityData) public view override returns (uint256) {
MarketParams memory market = decodeFacilityData(facilityData);
Id marketId = market.id();
uint256 borrowedAssets = morpho.expectedBorrowAssets(market, address(this));
uint256 collateralAmount = morpho.collateral(marketId, address(this));
if (collateralAmount == 0) return 0;
require(market.oracle != address(0), ErrorsLib.NoOracleForToken());
uint256 collateralPrice = IOracle(market.oracle).price();
uint256 collateralValue = collateralAmount.mulDivDown(collateralPrice, ORACLE_PRICE_SCALE);
return (collateralValue == 0) ? 0 : borrowedAssets.wDivUp(collateralValue);
}
function debtBalance(bytes calldata facilityData, IERC20 debtToken) external view override returns (uint256) {
MarketParams memory market = decodeFacilityData(facilityData);
require(address(debtToken) == market.loanToken, "FundingModuleMorpho: Wrong debt token");
return morpho.expectedBorrowAssets(market, address(this));
}
function collateralBalance(bytes calldata facilityData, IERC20 collateralToken) external view override returns (uint256) {
MarketParams memory market = decodeFacilityData(facilityData);
require(address(collateralToken) == market.collateralToken, "FundingModuleMorpho: Wrong collateral token");
return morpho.collateral(market.id(), address(this));
}
/// @dev The NAV for a given lending market can be negative but there is no recourse so it can be floored to 0.
function nav(IOracleCallback oraclesProvider) public view override returns (uint256) {
uint256 nav_ = 0;
address asset = oraclesProvider.asset();
uint256 length = facilitiesSet.length();
for (uint256 i = 0; i < length; i++) {
uint256 facilityNav = 0;
bytes32 facilityHash = facilitiesSet.at(i);
MarketParams memory market = decodeFacilityData(facilityDataMap[facilityHash]);
uint256 collateralBalance_ = morpho.collateral(market.id(), address(this));
if (collateralBalance_ == 0) continue; // No debt if no collateral
if (market.collateralToken == asset) {
// RIs are considered to have a price of ORACLE_PRECISION
facilityNav += collateralBalance_;
} else {
IOracle oracle = oraclesProvider.oracles(IERC20(market.collateralToken));
facilityNav += collateralBalance_.mulDivDown(oracle.price(), ORACLE_PRICE_SCALE);
}
uint256 debtBalance_ = morpho.expectedBorrowAssets(market, address(this));
if (market.loanToken == asset) {
facilityNav = (facilityNav > debtBalance_) ? facilityNav - debtBalance_ : 0;
} else {
IOracle oracle = oraclesProvider.oracles(IERC20(market.loanToken));
uint256 value = debtBalance_.mulDivUp(oracle.price(), ORACLE_PRICE_SCALE);
facilityNav = (facilityNav > value) ? facilityNav - value : 0;
}
nav_ += facilityNav;
}
return nav_;
}
// ========== Other exposed view functions ==========
function decodeFacilityData(bytes memory facilityData) public pure returns (MarketParams memory market) {
// MarketParams has 4 addresses (32 bytes each) + 1 uint256 (32 bytes) = 160 bytes
require(facilityData.length == 160, ErrorsLib.InvalidFacilityData());
(MarketParams memory marketParams) = abi.decode(facilityData, (MarketParams));
return (marketParams);
}
function encodeFacilityData(MarketParams memory market) public pure returns (bytes memory) {
return abi.encode(market);
}
// ========== Internal functions ==========
function _debtBalance(IERC20 debtToken) internal view override returns (uint256 balance) {
uint256 length = facilitiesSet.length();
for (uint256 i = 0; i < length; i++) {
bytes32 facilityHash = facilitiesSet.at(i);
MarketParams memory market = decodeFacilityData(facilityDataMap[facilityHash]);
if (address(debtToken) == market.loanToken) {
balance += morpho.expectedBorrowAssets(market, address(this));
}
}
}
function _collateralBalance(IERC20 collateralToken) internal view override returns (uint256 balance) {
uint256 length = facilitiesSet.length();
for (uint256 i = 0; i < length; i++) {
bytes32 facilityHash = facilitiesSet.at(i);
MarketParams memory market = decodeFacilityData(facilityDataMap[facilityHash]);
if (address(collateralToken) == market.collateralToken) {
balance += morpho.collateral(market.id(), address(this));
}
}
}
function _isFacilityUsed(bytes calldata facilityData) internal view override returns (bool) {
MarketParams memory market = decodeFacilityData(facilityData);
Position memory position = morpho.position(market.id(), address(this));
return position.collateral > 0 || position.borrowShares > 0;
}
}// 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 skim(IERC20 token) external;
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);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
type Id is bytes32;
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
}
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
uint256 supplyShares;
uint128 borrowShares;
uint128 collateral;
}
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
struct Market {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
}
struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
/// @notice The EIP-712 domain separator.
/// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
/// same chain id and on forks because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice The owner of the contract.
/// @dev It has the power to change the owner.
/// @dev It has the power to set fees on markets and set the fee recipient.
/// @dev It has the power to enable but not disable IRMs and LLTVs.
function owner() external view returns (address);
/// @notice The fee recipient of all markets.
/// @dev The recipient receives the fees of a given market through a supply position on that market.
function feeRecipient() external view returns (address);
/// @notice Whether the `irm` is enabled.
function isIrmEnabled(address irm) external view returns (bool);
/// @notice Whether the `lltv` is enabled.
function isLltvEnabled(uint256 lltv) external view returns (bool);
/// @notice Whether `authorized` is authorized to modify `authorizer`'s position on all markets.
/// @dev Anyone is authorized to modify their own positions, regardless of this variable.
function isAuthorized(address authorizer, address authorized) external view returns (bool);
/// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
function nonce(address authorizer) external view returns (uint256);
/// @notice Sets `newOwner` as `owner` of the contract.
/// @dev Warning: No two-step transfer ownership.
/// @dev Warning: The owner can be set to the zero address.
function setOwner(address newOwner) external;
/// @notice Enables `irm` as a possible IRM for market creation.
/// @dev Warning: It is not possible to disable an IRM.
function enableIrm(address irm) external;
/// @notice Enables `lltv` as a possible LLTV for market creation.
/// @dev Warning: It is not possible to disable a LLTV.
function enableLltv(uint256 lltv) external;
/// @notice Sets the `newFee` for the given market `marketParams`.
/// @param newFee The new fee, scaled by WAD.
/// @dev Warning: The recipient can be the zero address.
function setFee(MarketParams memory marketParams, uint256 newFee) external;
/// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
/// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
/// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
/// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Creates the market `marketParams`.
/// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
/// Morpho behaves as expected:
/// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
/// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
/// burn functions are not supported.
/// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
/// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
/// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
/// - The IRM should not re-enter Morpho.
/// - The oracle should return a price with the correct scaling.
/// - The oracle price should not be able to change instantly such that the new price is less than the old price
/// multiplied by LLTV*LIF. In particular, if the loan asset is a vault that can receive donations, the oracle
/// should not price its shares using the AUM.
/// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
/// properties (funds could get stuck):
/// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
/// - The amount of assets supplied and borrowed should not be too high (max ~1e32), otherwise the number of shares
/// might not fit within 128 bits.
/// - The IRM should not revert on `borrowRate`.
/// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
/// `_accrueInterest` can overflow).
/// - The oracle should not revert `price`.
/// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
/// `assetsRepaid` in `liquidate` can overflow).
/// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
/// the point where `totalBorrowShares` is very large and borrowing overflows.
function createMarket(MarketParams memory marketParams) external;
/// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupply` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
/// amount of shares is given for full compatibility and precision.
/// @dev Supplying a large amount can revert for overflow.
/// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to supply assets to.
/// @param assets The amount of assets to supply.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased supply position.
/// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
/// @return assetsSupplied The amount of assets supplied.
/// @return sharesSupplied The amount of shares minted.
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
/// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
/// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
/// conversion roundings between shares and assets.
/// @param marketParams The market to withdraw assets from.
/// @param assets The amount of assets to withdraw.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the supply position.
/// @param receiver The address that will receive the withdrawn assets.
/// @return assetsWithdrawn The amount of assets withdrawn.
/// @return sharesWithdrawn The amount of shares burned.
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
/// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
/// given for full compatibility and precision.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Borrowing a large amount can revert for overflow.
/// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to borrow assets from.
/// @param assets The amount of assets to borrow.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased borrow position.
/// @param receiver The address that will receive the borrowed assets.
/// @return assetsBorrowed The amount of assets borrowed.
/// @return sharesBorrowed The amount of shares minted.
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
/// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoRepay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice Supplies `assets` of collateral on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupplyCollateral` function with the given `data`.
/// @dev Interest are not accrued since it's not required and it saves gas.
/// @dev Supplying a large amount can revert for overflow.
/// @param marketParams The market to supply collateral to.
/// @param assets The amount of collateral to supply.
/// @param onBehalf The address that will own the increased collateral position.
/// @param data Arbitrary data to pass to the `onMorphoSupplyCollateral` callback. Pass empty data if not needed.
function supplyCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes memory data)
external;
/// @notice Withdraws `assets` of collateral on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more collateral than supplied will revert for underflow.
/// @param marketParams The market to withdraw collateral from.
/// @param assets The amount of collateral to withdraw.
/// @param onBehalf The address of the owner of the collateral position.
/// @param receiver The address that will receive the collateral assets.
function withdrawCollateral(MarketParams memory marketParams, uint256 assets, address onBehalf, address receiver)
external;
/// @notice Liquidates the given `repaidShares` of debt asset or seize the given `seizedAssets` of collateral on the
/// given market `marketParams` of the given `borrower`'s position, optionally calling back the caller's
/// `onMorphoLiquidate` function with the given `data`.
/// @dev Either `seizedAssets` or `repaidShares` should be zero.
/// @dev Seizing more than the collateral balance will underflow and revert without any error message.
/// @dev Repaying more than the borrow balance will underflow and revert without any error message.
/// @dev An attacker can front-run a liquidation with a small repay making the transaction revert for underflow.
/// @param marketParams The market of the position.
/// @param borrower The owner of the position.
/// @param seizedAssets The amount of collateral to seize.
/// @param repaidShares The amount of shares to repay.
/// @param data Arbitrary data to pass to the `onMorphoLiquidate` callback. Pass empty data if not needed.
/// @return The amount of assets seized.
/// @return The amount of assets repaid.
function liquidate(
MarketParams memory marketParams,
address borrower,
uint256 seizedAssets,
uint256 repaidShares,
bytes memory data
) external returns (uint256, uint256);
/// @notice Executes a flash loan.
/// @dev Flash loans have access to the whole balance of the contract (the liquidity and deposited collateral of all
/// markets combined, plus donations).
/// @dev Warning: Not ERC-3156 compliant but compatibility is easily reached:
/// - `flashFee` is zero.
/// - `maxFlashLoan` is the token's balance of this contract.
/// - The receiver of `assets` is the caller.
/// @param token The token to flash loan.
/// @param assets The amount of assets to flash loan.
/// @param data Arbitrary data to pass to the `onMorphoFlashLoan` callback.
function flashLoan(address token, uint256 assets, bytes calldata data) external;
/// @notice Sets the authorization for `authorized` to manage `msg.sender`'s positions.
/// @param authorized The authorized address.
/// @param newIsAuthorized The new authorization status.
function setAuthorization(address authorized, bool newIsAuthorized) external;
/// @notice Sets the authorization for `authorization.authorized` to manage `authorization.authorizer`'s positions.
/// @dev Warning: Reverts if the signature has already been submitted.
/// @dev The signature is malleable, but it has no impact on the security here.
/// @dev The nonce is passed as argument to be able to revert with a different error message.
/// @param authorization The `Authorization` struct.
/// @param signature The signature.
function setAuthorizationWithSig(Authorization calldata authorization, Signature calldata signature) external;
/// @notice Accrues interest for the given market `marketParams`.
function accrueInterest(MarketParams memory marketParams) external;
/// @notice Returns the data stored on the different `slots`.
function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}
/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user)
external
view
returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
/// accrual.
function market(Id id)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee
);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id)
external
view
returns (address loanToken, address collateralToken, address oracle, address irm, uint256 lltv);
}
/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams} from "../interfaces/IMorpho.sol";
/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to convert a market to its id.
library MarketParamsLib {
/// @notice The length of the data used to compute the id of a market.
/// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;
/// @notice Returns the id of the market `marketParams`.
function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
assembly ("memory-safe") {
marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity 0.8.28;
import {IVaultV2} from "../interfaces/IVaultV2.sol";
import {IERC4626} from "../interfaces/IERC4626.sol";
import {IERC20} from "../interfaces/IERC20.sol";
import {IMorphoVaultV1Adapter} from "./interfaces/IMorphoVaultV1Adapter.sol";
import {SafeERC20Lib} from "../libraries/SafeERC20Lib.sol";
/// @dev Designed, developed and audited for Morpho Vaults V1 (V1.0 and V1.1) (also known as MetaMorpho). Integration
/// with other vaults must be carefully assessed from a security standpoint.
/// @dev This adapter must be used with Morpho Vaults V1 that are protected against inflation attacks with an initial
/// deposit. See https://docs.openzeppelin.com/contracts/5.x/erc4626#inflation-attack.
/// @dev Must not be used with a Morpho Vault V1 which has a market with an Irm that can re-enter the parent vault or
/// the adapter.
/// @dev Morpho Vaults V1.1 do not realize bad debt, so Morpho Vaults V2 supplying in them will not realize the
/// corresponding bad debt.
/// @dev Losses that correspond to rounding errors are realizable.
/// @dev Shares of the Morpho Vault V1 cannot be skimmed (unlike any other token).
/// @dev If expectedSupplyAssets reverts for a market of the morphoVaultV1, realAssets will revert and the vault will
/// not be able to accrueInterest.
/// @dev Shouldn't be used alongside another adapter that re-uses the id (abi.encode("this", address(this)).
contract MorphoVaultV1Adapter is IMorphoVaultV1Adapter {
/* IMMUTABLES */
address public immutable factory;
address public immutable parentVault;
address public immutable morphoVaultV1;
bytes32 public immutable adapterId;
/* STORAGE */
address public skimRecipient;
/* FUNCTIONS */
constructor(address _parentVault, address _morphoVaultV1) {
factory = msg.sender;
parentVault = _parentVault;
morphoVaultV1 = _morphoVaultV1;
adapterId = keccak256(abi.encode("this", address(this)));
address asset = IVaultV2(_parentVault).asset();
require(asset == IERC4626(_morphoVaultV1).asset(), AssetMismatch());
SafeERC20Lib.safeApprove(asset, _parentVault, type(uint256).max);
SafeERC20Lib.safeApprove(asset, _morphoVaultV1, 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 != morphoVaultV1, CannotSkimMorphoVaultV1Shares());
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(morphoVaultV1).deposit(assets, address(this));
uint256 oldAllocation = allocation();
uint256 newAllocation = IERC4626(morphoVaultV1).previewRedeem(IERC4626(morphoVaultV1).balanceOf(address(this)));
// forge-lint: disable-next-item(unsafe-typecast) safe because Market V1 bounds the total supply of the
// underlying token, and allocation is less than the max total assets of the vault.
return (ids(), int256(newAllocation) - int256(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(morphoVaultV1).withdraw(assets, address(this), address(this));
uint256 oldAllocation = allocation();
uint256 newAllocation = IERC4626(morphoVaultV1).previewRedeem(IERC4626(morphoVaultV1).balanceOf(address(this)));
// forge-lint: disable-next-item(unsafe-typecast) safe because Market V1 bounds the total supply of the
// underlying token, and allocation is less than the max total assets of the vault.
return (ids(), int256(newAllocation) - int256(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
? IERC4626(morphoVaultV1).previewRedeem(IERC4626(morphoVaultV1).balanceOf(address(this)))
: 0;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
interface IMorphoVaultV1AdapterFactory {
/* EVENTS */
event CreateMorphoVaultV1Adapter(
address indexed parentVault, address indexed morphoVaultV1, address indexed morphoVaultV1Adapter
);
/* FUNCTIONS */
function morphoVaultV1Adapter(address parentVault, address morphoVaultV1) external view returns (address);
function isMorphoVaultV1Adapter(address account) external view returns (bool);
function createMorphoVaultV1Adapter(address parentVault, address morphoVaultV1)
external
returns (address morphoVaultV1Adapter);
}// 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 receiver, address onBehalf) external returns (uint256 shares);
function redeem(uint256 shares, address receiver, address onBehalf) 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);
}// 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);
}// 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);
}// 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);
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
interface IAdapterRegistry {
function isInRegistry(address account) external view returns (bool);
}// 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 AutomaticallyTimelocked();
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 TimelockNotDecreasing();
error TimelockNotExpired();
error TimelockNotIncreasing();
error TransferFromReturnedFalse();
error TransferFromReverted();
error TransferReturnedFalse();
error TransferReverted();
error Unauthorized();
error ZeroAbsoluteCap();
error ZeroAddress();
error ZeroAllocation();
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity ^0.8.0;
library EventsLib {
// ERC20 events
event Approval(address indexed owner, address indexed spender, uint256 shares);
event Transfer(address indexed from, address indexed to, uint256 shares);
/// @dev Emitted when the allowance is updated by transferFrom (not when it is updated by permit, approve, withdraw,
/// redeem because their respective events allow to track the allowance).
event AllowanceUpdatedByTransferFrom(address indexed owner, address indexed spender, uint256 shares);
event Permit(address indexed owner, address indexed spender, uint256 shares, uint256 nonce, uint256 deadline);
// ERC4626 events
event Deposit(address indexed sender, address indexed onBehalf, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender, address indexed receiver, address indexed onBehalf, uint256 assets, uint256 shares
);
// Vault creation events
event Constructor(address indexed owner, address indexed asset);
// Allocation events
event Allocate(address indexed sender, address indexed adapter, uint256 assets, bytes32[] ids, int256 change);
event Deallocate(address indexed sender, address indexed adapter, uint256 assets, bytes32[] ids, int256 change);
event ForceDeallocate(
address indexed sender,
address adapter,
uint256 assets,
address indexed onBehalf,
bytes32[] ids,
uint256 penaltyAssets
);
// Fee and interest events
event AccrueInterest(
uint256 previousTotalAssets, uint256 newTotalAssets, uint256 performanceFeeShares, uint256 managementFeeShares
);
// Timelock events
event Revoke(address indexed sender, bytes4 indexed selector, bytes data);
event Submit(bytes4 indexed selector, bytes data, uint256 executableAt);
event Accept(bytes4 indexed selector, bytes data);
// Configuration events
event SetOwner(address indexed newOwner);
event SetCurator(address indexed newCurator);
event SetIsSentinel(address indexed account, bool newIsSentinel);
event SetName(string newName);
event SetSymbol(string newSymbol);
event SetIsAllocator(address indexed account, bool newIsAllocator);
event SetReceiveSharesGate(address indexed newReceiveSharesGate);
event SetSendSharesGate(address indexed newSendSharesGate);
event SetReceiveAssetsGate(address indexed newReceiveAssetsGate);
event SetSendAssetsGate(address indexed newSendAssetsGate);
event SetAdapterRegistry(address indexed newAdapterRegistry);
event AddAdapter(address indexed account);
event RemoveAdapter(address indexed account);
event DecreaseTimelock(bytes4 indexed selector, uint256 newDuration);
event IncreaseTimelock(bytes4 indexed selector, uint256 newDuration);
event Abdicate(bytes4 indexed selector);
event SetLiquidityAdapterAndData(
address indexed sender, address indexed newLiquidityAdapter, bytes indexed newLiquidityData
);
event SetPerformanceFee(uint256 newPerformanceFee);
event SetPerformanceFeeRecipient(address indexed newPerformanceFeeRecipient);
event SetManagementFee(uint256 newManagementFee);
event SetManagementFeeRecipient(address indexed newManagementFeeRecipient);
event DecreaseAbsoluteCap(address indexed sender, bytes32 indexed id, bytes idData, uint256 newAbsoluteCap);
event IncreaseAbsoluteCap(bytes32 indexed id, bytes idData, uint256 newAbsoluteCap);
event DecreaseRelativeCap(address indexed sender, bytes32 indexed id, bytes idData, uint256 newRelativeCap);
event IncreaseRelativeCap(bytes32 indexed id, bytes idData, uint256 newRelativeCap);
event SetMaxRate(uint256 newMaxRate);
event SetForceDeallocatePenalty(address indexed adapter, uint256 forceDeallocatePenalty);
}// 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());
// forge-lint: disable-next-item(unsafe-typecast) safe because x <= type(uint128).max.
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());
// forge-lint: disable-next-item(unsafe-typecast) safe because x >= 0.
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)))
}
}
}// 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());
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
interface IReceiveSharesGate {
function canReceiveShares(address account) external view returns (bool);
}
interface ISendSharesGate {
function canSendShares(address account) external view returns (bool);
}
interface IReceiveAssetsGate {
function canReceiveAssets(address account) external view returns (bool);
}
interface ISendAssetsGate {
function canSendAssets(address account) external view returns (bool);
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
interface IVaultV2Factory {
/* EVENTS */
event CreateVaultV2(address indexed owner, address indexed asset, bytes32 salt, address indexed newVaultV2);
/* FUNCTIONS */
function isVaultV2(address account) external view returns (bool);
function vaultV2(address owner, address asset, bytes32 salt) external view returns (address);
function createVaultV2(address owner, address asset, bytes32 salt) external returns (address newVaultV2);
}// 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/// @inheritdoc IERC20
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (!_safeTransfer(token, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
if (!_safeTransferFrom(token, from, to, value, true)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _safeTransfer(token, to, value, false);
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _safeTransferFrom(token, from, to, value, false);
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
if (!_safeApprove(token, spender, value, false)) {
if (!_safeApprove(token, spender, 0, true)) revert SafeERC20FailedOperation(address(token));
if (!_safeApprove(token, spender, value, true)) revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity `token.transfer(to, value)` call, relaxing the requirement on the return value: the
* return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransfer(IERC20 token, address to, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.transfer.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(to, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0, 0x44, 0, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
/**
* @dev Imitates a Solidity `token.transferFrom(from, to, value)` call, relaxing the requirement on the return
* value: the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param from The sender of the tokens
* @param to The recipient of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value,
bool bubble
) private returns (bool success) {
bytes4 selector = IERC20.transferFrom.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(from, shr(96, not(0))))
mstore(0x24, and(to, shr(96, not(0))))
mstore(0x44, value)
success := call(gas(), token, 0, 0, 0x64, 0, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
mstore(0x60, 0)
}
}
/**
* @dev Imitates a Solidity `token.approve(spender, value)` call, relaxing the requirement on the return value:
* the return value is optional (but if data is returned, it must not be false).
*
* @param token The token targeted by the call.
* @param spender The spender of the tokens
* @param value The amount of token to transfer
* @param bubble Behavior switch if the transfer call reverts: bubble the revert reason or return a false boolean.
*/
function _safeApprove(IERC20 token, address spender, uint256 value, bool bubble) private returns (bool success) {
bytes4 selector = IERC20.approve.selector;
assembly ("memory-safe") {
let fmp := mload(0x40)
mstore(0x00, selector)
mstore(0x04, and(spender, shr(96, not(0))))
mstore(0x24, value)
success := call(gas(), token, 0, 0, 0x44, 0, 0x20)
// if call success and return is true, all is good.
// otherwise (not success or return is not true), we need to perform further checks
if iszero(and(success, eq(mload(0x00), 1))) {
// if the call was a failure and bubble is enabled, bubble the error
if and(iszero(success), bubble) {
returndatacopy(fmp, 0, returndatasize())
revert(fmp, returndatasize())
}
// if the return value is not true, then the call is only successful if:
// - the token address has code
// - the returndata is empty
success := and(success, and(iszero(returndatasize()), gt(extcodesize(token), 0)))
}
mstore(0x40, fmp)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
/**
* @dev Counts the number of leading zero bits in a uint256.
*/
function clz(uint256 x) internal pure returns (uint256) {
return ternary(x == 0, 256, 255 - log2(x));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// 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;
}// SPDX-License-Identifier: MIT // Copyright (c) 2025 Steakhouse pragma solidity >=0.8.0; // Precision for percentage calculations uint256 constant PRECISION = 1 ether; // Maximum timelock duration (4 weeks) uint256 constant TIMELOCK_CAP = 4 weeks; // Timelock duration for disabled selectors uint256 constant TIMELOCK_DISABLED = type(uint256).max; // Maximum allowed slippage percentage (1%) uint256 constant MAX_SLIPPAGE_LIMIT = 0.01 ether; // Delay from start of a shutdown to possible liquidations uint256 constant MAX_SHUTDOWN_WARMUP = 4 weeks; // Precision for oracle prices uint256 constant ORACLE_PRECISION = 1e36; // Maximum number of tokens allowed in a box uint256 constant MAX_TOKENS = 20;
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
library ErrorsLib {
// General errors
error InvalidAddress();
error InvalidAmount();
error InvalidValue();
error CannotRemove();
error AlreadyWhitelisted();
error NotWhitelisted();
error NotClean();
error NotAllowed();
// Access control errors
error OnlyOwner();
error OnlyCurator();
error OnlyGuardian();
error OnlyCuratorOrGuardian();
error OnlyAllocators();
error OnlyFeeders();
error OnlySkimRecipient();
error OnlyAllocatorsOrWinddown();
error OnlyMorpho();
error OnlyBox();
error OnlyPool();
error OnlyThisContract();
error CannotDuringShutdown();
error CannotDuringWinddown();
// Withdraw/Redeem errors
error InsufficientShares();
error InsufficientAllowance();
error InsufficientLiquidity();
error DataAlreadyTimelocked();
// Token errors
error TokenNotWhitelisted();
error TokenAlreadyWhitelisted();
error OracleRequired();
error NoOracleForToken();
error TokenBalanceMustBeZero();
error TooManyTokens();
// Slippage errors
error SwapperDidSpendTooMuch();
error AllocationTooExpensive();
error TokenSaleNotGeneratingEnoughAssets();
error ReallocationSlippageTooHigh();
error TooMuchAccumulatedSlippage();
error SlippageTooHigh();
// Shutdown/Recover errors
error OnlyGuardianOrCuratorCanShutdown();
error OnlyGuardianCanRecover();
error AlreadyShutdown();
error NotShutdown();
error CannotRecoverAfterWinddown();
// Timelock errors
error TimelockNotExpired();
error DataNotTimelocked();
error InvalidTimelock();
error TimelockNotIncreasing();
error TimelockNotDecreasing();
error FunctionDisabled();
// Skim errors
error CannotSkimToken();
error CannotSkimZero();
error SkimChangedNav();
error TransferFailed();
// Funding module errors
error ExcessiveLTV();
error NoNavDuringCache();
error InvalidFacilityData();
// Flash callback errors
error ReentryNotAllowed();
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IFunding} from "./../interfaces/IFunding.sol";
import {IOracle} from "./../interfaces/IOracle.sol";
import {ISwapper} from "./../interfaces/ISwapper.sol";
library EventsLib {
// ========== FACTORIES ==========
event BoxCreated(
address indexed box,
address indexed asset,
address indexed owner,
address curator,
string name,
string symbol,
uint256 maxSlippage,
uint256 slippageEpochDuration,
uint256 shutdownSlippageDuration,
uint256 shutdownWarmup
);
// ========== ACCESS CONTROL ==========
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event CuratorUpdated(address indexed previousCurator, address indexed newCurator);
event GuardianUpdated(address indexed previousGuardian, address indexed newGuardian);
event AllocatorUpdated(address indexed account, bool isAllocator);
event FeederUpdated(address indexed account, bool isFeeder);
// ========== INVESTMENT MANAGEMENT ==========
event Allocation(
IERC20 indexed token,
uint256 assets,
uint256 expectedTokens,
uint256 actualTokens,
int256 slippagePct,
ISwapper indexed swapper,
bytes data
);
event Deallocation(
IERC20 indexed token,
uint256 tokens,
uint256 expectedAssets,
uint256 actualAssets,
int256 slippagePct,
ISwapper indexed swapper,
bytes data
);
event Reallocation(
IERC20 indexed fromToken,
IERC20 indexed toToken,
uint256 tokensFrom,
uint256 expectedTokensTo,
uint256 actualTokensTo,
int256 slippagePct,
ISwapper indexed swapper,
bytes data
);
event Pledge(IFunding indexed fundingModule, bytes facilityData, IERC20 collateralToken, uint256 collateralAmount);
event Depledge(IFunding indexed fundingModule, bytes facilityData, IERC20 collateralToken, uint256 collateralAmount);
event Borrow(IFunding indexed fundingModule, bytes facilityData, IERC20 debtToken, uint256 borrowAmount);
event Repay(IFunding indexed fundingModule, bytes facilityData, IERC20 debtToken, uint256 repayAmount);
event Flash(address indexed caller, IERC20 indexed token, uint256 amount);
// ========== MISC ==========
event SlippageAccumulated(uint256 amount, uint256 total);
event SlippageEpochReset(uint256 newEpochStart);
event MaxSlippageUpdated(uint256 previousMaxSlippage, uint256 newMaxSlippage);
event Skim(IERC20 indexed token, address indexed recipient, uint256 amount);
// ========== CONFIGURATION ==========
event TokenAdded(IERC20 indexed token, IOracle indexed oracle);
event TokenRemoved(IERC20 indexed token);
event TokenOracleChanged(IERC20 indexed token, IOracle indexed oracle);
event FundingModuleAdded(IFunding indexed fundingModule);
event FundingFacilityAdded(IFunding indexed fundingModule, bytes facilityData);
event FundingCollateralAdded(IFunding indexed fundingModule, IERC20 collateralToken);
event FundingDebtAdded(IFunding indexed fundingModule, IERC20 debtToken);
event FundingModuleRemoved(IFunding indexed fundingModule);
event FundingFacilityRemoved(IFunding indexed fundingModule, bytes facilityData);
event FundingCollateralRemoved(IFunding indexed fundingModule, IERC20 collateralToken);
event FundingDebtRemoved(IFunding indexed fundingModule, IERC20 debtToken);
event SkimRecipientUpdated(address indexed previousRecipient, address indexed newRecipient);
event Shutdown(address indexed guardian);
event Recover(address indexed guardian);
// ========== TIMELOCK ==========
event TimelockSubmitted(bytes4 indexed selector, bytes data, uint256 executableAt, address who);
event TimelockRevoked(bytes4 indexed selector, bytes data, address who);
event TimelockIncreased(bytes4 indexed selector, uint256 newDuration, address who);
event TimelockDecreased(bytes4 indexed selector, uint256 newDuration, address who);
event TimelockExecuted(bytes4 indexed selector, bytes data, address who);
}// SPDX-License-Identifier: MIT
// Copyright (c) 2025 Steakhouse Financial
pragma solidity >=0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IBox} from "./IBox.sol";
interface IBoxFactory {
/* EVENTS */
event BoxCreated(
IBox indexed box,
IERC20 indexed currency,
address indexed owner,
address curator,
string name,
string symbol,
uint256 maxSlippage,
uint256 slippageEpochDuration,
uint256 shutdownSlippageDuration,
uint256 shutdownWarmup
);
/* FUNCTIONS */
function createBox(
IERC20 _currency,
address _owner,
address _curator,
string memory _name,
string memory _symbol,
uint256 _maxSlippage,
uint256 _slippageEpochDuration,
uint256 _shutdownSlippageDuration,
uint256 _shutdownWarmup,
bytes32 salt
) external returns (IBox box);
}// 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 {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 {
/* 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 bounded by Vault V2 which requires totalAssets to stay below ~10^35
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 bounded by Vault V2 which requires totalAssets to stay below ~10^35
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));
}
}// 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);
}// 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);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @dev The maximum fee a market can have (25%).
uint256 constant MAX_FEE = 0.25e18;
/// @dev Oracle price scale.
uint256 constant ORACLE_PRICE_SCALE = 1e36;
/// @dev Liquidation cursor.
uint256 constant LIQUIDATION_CURSOR = 0.3e18;
/// @dev Max liquidation incentive factor.
uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;
/// @dev The EIP-712 typeHash for EIP712Domain.
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
/// @dev The EIP-712 typeHash for Authorization.
bytes32 constant AUTHORIZATION_TYPEHASH =
keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";
import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";
/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
using MathLib for uint256;
using MathLib for uint128;
using UtilsLib for uint256;
using MorphoLib for IMorpho;
using SharesMathLib for uint256;
using MarketParamsLib for MarketParams;
/// @notice Returns the expected market balances of a market after having accrued interest.
/// @return The expected total supply assets.
/// @return The expected total supply shares.
/// @return The expected total borrow assets.
/// @return The expected total borrow shares.
function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256, uint256, uint256, uint256)
{
Id id = marketParams.id();
Market memory market = morpho.market(id);
uint256 elapsed = block.timestamp - market.lastUpdate;
// Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
market.totalBorrowAssets += interest.toUint128();
market.totalSupplyAssets += interest.toUint128();
if (market.fee != 0) {
uint256 feeAmount = interest.wMulDown(market.fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already updated.
uint256 feeShares =
feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
market.totalSupplyShares += feeShares.toUint128();
}
}
return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
}
/// @notice Returns the expected total supply assets of a market after having accrued interest.
function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyAssets)
{
(totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total borrow assets of a market after having accrued interest.
function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalBorrowAssets)
{
(,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total supply shares of a market after having accrued interest.
function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyShares)
{
(, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
/// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user)
internal
view
returns (uint256)
{
Id id = marketParams.id();
uint256 supplyShares = morpho.supplyShares(id, user);
(uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
}
/// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
/// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
/// assets.
function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user)
internal
view
returns (uint256)
{
Id id = marketParams.id();
uint256 borrowShares = morpho.borrowShares(id, user);
(,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams);
return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";
/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
return uint256(morpho.extSloads(slot)[0]);
}
function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function fee(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function _array(bytes32 x) private pure returns (bytes32[] memory) {
bytes32[] memory res = new bytes32[](1);
res[0] = x;
return res;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
import {Math} from "../math/Math.sol";
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Set can be cleared (all elements removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* The following types are supported:
*
* - `bytes32` (`Bytes32Set`) since v3.3.0
* - `address` (`AddressSet`) since v3.3.0
* - `uint256` (`UintSet`) since v3.3.0
* - `string` (`StringSet`) since v5.4.0
* - `bytes` (`BytesSet`) since v5.4.0
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: This function has an unbounded cost that scales with set size. Developers should keep in mind that
* using it may render the function uncallable if the set grows to the point where clearing it consumes too much
* gas to fit in a block.
*/
function _clear(Set storage set) private {
uint256 len = _length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set, uint256 start, uint256 end) private view returns (bytes32[] memory) {
unchecked {
end = Math.min(end, _length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes32[] memory result = new bytes32[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set, uint256 start, uint256 end) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set, uint256 start, uint256 end) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set, uint256 start, uint256 end) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner, start, end);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
struct StringSet {
// Storage of set values
string[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(string value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(StringSet storage set, string memory value) internal returns (bool) {
if (!contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(StringSet storage set, string memory value) internal returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
string memory lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(StringSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(StringSet storage set, string memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(StringSet storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(StringSet storage set, uint256 index) internal view returns (string memory) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(StringSet storage set) internal view returns (string[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(StringSet storage set, uint256 start, uint256 end) internal view returns (string[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
string[] memory result = new string[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
struct BytesSet {
// Storage of set values
bytes[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(BytesSet storage set, bytes memory value) internal returns (bool) {
if (!contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(BytesSet storage set, bytes memory value) internal returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes memory lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(BytesSet storage set) internal {
uint256 len = length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(BytesSet storage set, bytes memory value) internal view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(BytesSet storage set) internal view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(BytesSet storage set, uint256 index) internal view returns (bytes memory) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(BytesSet storage set) internal view returns (bytes[] memory) {
return set._values;
}
/**
* @dev Return a slice of the set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(BytesSet storage set, uint256 start, uint256 end) internal view returns (bytes[] memory) {
unchecked {
end = Math.min(end, length(set));
start = Math.min(start, end);
uint256 len = end - start;
bytes[] memory result = new bytes[](len);
for (uint256 i = 0; i < len; ++i) {
result[i] = Arrays.unsafeAccess(set._values, start + i).value;
}
return result;
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
uint256 constant WAD = 1e18;
/// @title MathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
/// @dev Returns (`x` * `y`) / `WAD` rounded down.
function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded down.
function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded up.
function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
/// @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 the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
/// continuous compound interest rate.
function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 firstTerm = x * n;
uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);
return firstTerm + secondTerm + thirdTerm;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Steakhouse Financial
pragma solidity 0.8.28;
import "@morpho-blue/libraries/ConstantsLib.sol";
import {MathLib} from "@morpho-blue/libraries/MathLib.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {IFunding, IOracleCallback} from "./interfaces/IFunding.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
/**
* @title FundingBase
* @notice Abstract base contract for funding modules (Aave, Morpho, etc.)
* @dev Contains common functionality shared across all funding module implementations
*/
abstract contract FundingBase is IFunding {
using SafeERC20 for IERC20;
using MathLib for uint256;
using EnumerableSet for EnumerableSet.Bytes32Set;
using EnumerableSet for EnumerableSet.AddressSet;
// ========== STORAGE ==========
address public immutable owner;
EnumerableSet.Bytes32Set internal facilitiesSet;
EnumerableSet.AddressSet internal collateralTokensSet;
EnumerableSet.AddressSet internal debtTokensSet;
// ========== INITIALIZATION ==========
/**
* @notice Allows the contract to receive native currency
* @dev Required for skimming native currency back to the Box
*/
receive() external payable {}
/**
* @notice Fallback function to receive native currency
* @dev Required for skimming native currency back to the Box
*/
fallback() external payable {}
constructor(address _owner) {
require(_owner != address(0), ErrorsLib.InvalidAddress());
owner = _owner;
}
// ========== ABSTRACT FUNCTIONS ==========
/// @notice Calculate the net asset value of the funding module
/// @dev Must be implemented by each module with protocol-specific logic
function nav(IOracleCallback oraclesProvider) public view virtual returns (uint256);
/// @notice Check if a facility is currently in use (has positions)
/// @dev Must be implemented by each module with protocol-specific logic
function _isFacilityUsed(bytes calldata facilityData) internal view virtual returns (bool);
/// @notice Get the total collateral balance for a specific token
/// @dev Must be implemented by each module with protocol-specific logic
function _collateralBalance(IERC20 collateralToken) internal view virtual returns (uint256);
/// @notice Get the total debt balance for a specific token
/// @dev Must be implemented by each module with protocol-specific logic
function _debtBalance(IERC20 debtToken) internal view virtual returns (uint256);
// ========== COMMON ADMIN FUNCTIONS ==========
function isFacility(bytes calldata facilityData) public view override returns (bool) {
bytes32 facilityHash = keccak256(facilityData);
return facilitiesSet.contains(facilityHash);
}
function facilitiesLength() external view returns (uint256) {
return facilitiesSet.length();
}
function addCollateralToken(IERC20 collateralToken) external virtual override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(collateralTokensSet.add(address(collateralToken)), ErrorsLib.AlreadyWhitelisted());
}
function isCollateralToken(IERC20 collateralToken) public view override returns (bool) {
return collateralTokensSet.contains(address(collateralToken));
}
function collateralTokensLength() external view returns (uint256) {
return collateralTokensSet.length();
}
function collateralTokens(uint256 index) external view returns (IERC20) {
return IERC20(collateralTokensSet.at(index));
}
function addDebtToken(IERC20 debtToken) external virtual override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
require(debtTokensSet.add(address(debtToken)), ErrorsLib.AlreadyWhitelisted());
}
function isDebtToken(IERC20 debtToken) public view override returns (bool) {
return debtTokensSet.contains(address(debtToken));
}
function debtTokensLength() external view returns (uint256) {
return debtTokensSet.length();
}
function debtTokens(uint256 index) external view returns (IERC20) {
return IERC20(debtTokensSet.at(index));
}
// ========== COMMON ACTIONS ==========
function skim(IERC20 token) external override {
require(msg.sender == owner, ErrorsLib.OnlyOwner());
uint256 navBefore = nav(IOracleCallback(owner));
uint256 balance;
if (address(token) != address(0)) {
// ERC-20 tokens
balance = token.balanceOf(address(this));
require(balance > 0, ErrorsLib.InvalidAmount());
token.safeTransfer(owner, balance);
} else {
// ETH
balance = address(this).balance;
require(balance > 0, ErrorsLib.InvalidAmount());
payable(owner).transfer(balance);
}
uint256 navAfter = nav(IOracleCallback(owner));
require(navBefore == navAfter, ErrorsLib.SkimChangedNav());
}
/**
* @notice Executes multiple calls in a single transaction
* @param data Array of encoded function calls
* @dev Allows EOAs to execute multiple operations atomically
*/
function multicall(bytes[] calldata data) external {
uint256 length = data.length;
for (uint256 i = 0; i < length; i++) {
(bool success, bytes memory returnData) = address(this).delegatecall(data[i]);
if (!success) {
assembly ("memory-safe") {
revert(add(32, returnData), mload(returnData))
}
}
}
}
// ========== COMMON VIEW FUNCTIONS ==========
function debtBalance(IERC20 debtToken) external view override returns (uint256) {
return _debtBalance(debtToken);
}
function collateralBalance(IERC20 collateralToken) external view override returns (uint256) {
return _collateralBalance(collateralToken);
}
// ========== ENUMERABLE SET GETTERS ==========
/// @notice Get all facility hashes as an array
function facilitiesArray() external view returns (bytes32[] memory) {
uint256 length = facilitiesSet.length();
bytes32[] memory allFacilities = new bytes32[](length);
for (uint256 i = 0; i < length; i++) {
allFacilities[i] = facilitiesSet.at(i);
}
return allFacilities;
}
/// @notice Get all collateral token addresses as an array
function collateralTokensArray() external view returns (address[] memory) {
uint256 length = collateralTokensSet.length();
address[] memory allTokens = new address[](length);
for (uint256 i = 0; i < length; i++) {
allTokens[i] = collateralTokensSet.at(i);
}
return allTokens;
}
/// @notice Get all debt token addresses as an array
function debtTokensArray() external view returns (address[] memory) {
uint256 length = debtTokensSet.length();
address[] memory allTokens = new address[](length);
for (uint256 i = 0; i < length; i++) {
allTokens[i] = debtTokensSet.at(i);
}
return allTokens;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (c) 2025 Morpho Association
pragma solidity >=0.5.0;
import {IAdapter} from "../../interfaces/IAdapter.sol";
interface IMorphoVaultV1Adapter is IAdapter {
/* EVENTS */
event SetSkimRecipient(address indexed newSkimRecipient);
event Skim(address indexed token, uint256 assets);
/* ERRORS */
error AssetMismatch();
error CannotSkimMorphoVaultV1Shares();
error InvalidData();
error NotAuthorized();
/* FUNCTIONS */
function factory() external view returns (address);
function parentVault() external view returns (address);
function morphoVaultV1() external view returns (address);
function adapterId() external view returns (bytes32);
function skimRecipient() external view returns (address);
function allocation() external view returns (uint256);
function ids() external view returns (bytes32[] memory);
function setSkimRecipient(address newSkimRecipient) external;
function skim(address token) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC1363.sol)
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {MarketParams, Market} from "./IMorpho.sol";
/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
/// storage.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
/// @dev Returns true if there is exactly one zero among `x` and `y`.
function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
/// @dev Returns the min of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `x` safely cast to uint128.
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
return uint128(x);
}
/// @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))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {MathLib} from "./MathLib.sol";
/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
using MathLib for uint256;
/// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
/// high precision computations.
/// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
/// stays low enough not to inflate these assets to a significant value.
/// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
uint256 internal constant VIRTUAL_SHARES = 1e6;
/// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
/// empty.
uint256 internal constant VIRTUAL_ASSETS = 1;
/// @dev Calculates the value of `assets` quoted in shares, rounding down.
function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding down.
function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
/// @dev Calculates the value of `assets` quoted in shares, rounding up.
function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding up.
function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id} from "../../interfaces/IMorpho.sol";
/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
/* SLOTS */
uint256 internal constant OWNER_SLOT = 0;
uint256 internal constant FEE_RECIPIENT_SLOT = 1;
uint256 internal constant POSITION_SLOT = 2;
uint256 internal constant MARKET_SLOT = 3;
uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
uint256 internal constant IS_AUTHORIZED_SLOT = 6;
uint256 internal constant NONCE_SLOT = 7;
uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8;
/* SLOT OFFSETS */
uint256 internal constant LOAN_TOKEN_OFFSET = 0;
uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
uint256 internal constant ORACLE_OFFSET = 2;
uint256 internal constant IRM_OFFSET = 3;
uint256 internal constant LLTV_OFFSET = 4;
uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;
uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;
/* GETTERS */
function ownerSlot() internal pure returns (bytes32) {
return bytes32(OWNER_SLOT);
}
function feeRecipientSlot() internal pure returns (bytes32) {
return bytes32(FEE_RECIPIENT_SLOT);
}
function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
return bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
);
}
function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
return bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
+ BORROW_SHARES_AND_COLLATERAL_OFFSET
);
}
function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
}
function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
}
function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
}
function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
}
function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
}
function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT))));
}
function nonceSlot(address authorizer) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizer, NONCE_SLOT));
}
function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
}
function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
}
function idToOracleSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
}
function idToIrmSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
}
function idToLltvSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Arrays.sol)
// This file was procedurally generated from scripts/generate/templates/Arrays.js.
pragma solidity ^0.8.20;
import {Comparators} from "./Comparators.sol";
import {SlotDerivation} from "./SlotDerivation.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Math} from "./math/Math.sol";
/**
* @dev Collection of functions related to array types.
*/
library Arrays {
using SlotDerivation for bytes32;
using StorageSlot for bytes32;
/**
* @dev Sort an array of uint256 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
uint256[] memory array,
function(uint256, uint256) pure returns (bool) comp
) internal pure returns (uint256[] memory) {
_quickSort(_begin(array), _end(array), comp);
return array;
}
/**
* @dev Variant of {sort} that sorts an array of uint256 in increasing order.
*/
function sort(uint256[] memory array) internal pure returns (uint256[] memory) {
sort(array, Comparators.lt);
return array;
}
/**
* @dev Sort an array of address (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
address[] memory array,
function(address, address) pure returns (bool) comp
) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of address in increasing order.
*/
function sort(address[] memory array) internal pure returns (address[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Sort an array of bytes32 (in memory) following the provided comparator function.
*
* This function does the sorting "in place", meaning that it overrides the input. The object is returned for
* convenience, but that returned value can be discarded safely if the caller has a memory pointer to the array.
*
* NOTE: this function's cost is `O(n · log(n))` in average and `O(n²)` in the worst case, with n the length of the
* array. Using it in view functions that are executed through `eth_call` is safe, but one should be very careful
* when executing this as part of a transaction. If the array being sorted is too large, the sort operation may
* consume more gas than is available in a block, leading to potential DoS.
*
* IMPORTANT: Consider memory side-effects when using custom comparator functions that access memory in an unsafe way.
*/
function sort(
bytes32[] memory array,
function(bytes32, bytes32) pure returns (bool) comp
) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), _castToUint256Comp(comp));
return array;
}
/**
* @dev Variant of {sort} that sorts an array of bytes32 in increasing order.
*/
function sort(bytes32[] memory array) internal pure returns (bytes32[] memory) {
sort(_castToUint256Array(array), Comparators.lt);
return array;
}
/**
* @dev Performs a quick sort of a segment of memory. The segment sorted starts at `begin` (inclusive), and stops
* at end (exclusive). Sorting follows the `comp` comparator.
*
* Invariant: `begin <= end`. This is the case when initially called by {sort} and is preserved in subcalls.
*
* IMPORTANT: Memory locations between `begin` and `end` are not validated/zeroed. This function should
* be used only if the limits are within a memory array.
*/
function _quickSort(uint256 begin, uint256 end, function(uint256, uint256) pure returns (bool) comp) private pure {
unchecked {
if (end - begin < 0x40) return;
// Use first element as pivot
uint256 pivot = _mload(begin);
// Position where the pivot should be at the end of the loop
uint256 pos = begin;
for (uint256 it = begin + 0x20; it < end; it += 0x20) {
if (comp(_mload(it), pivot)) {
// If the value stored at the iterator's position comes before the pivot, we increment the
// position of the pivot and move the value there.
pos += 0x20;
_swap(pos, it);
}
}
_swap(begin, pos); // Swap pivot into place
_quickSort(begin, pos, comp); // Sort the left side of the pivot
_quickSort(pos + 0x20, end, comp); // Sort the right side of the pivot
}
}
/**
* @dev Pointer to the memory location of the first element of `array`.
*/
function _begin(uint256[] memory array) private pure returns (uint256 ptr) {
assembly ("memory-safe") {
ptr := add(array, 0x20)
}
}
/**
* @dev Pointer to the memory location of the first memory word (32bytes) after `array`. This is the memory word
* that comes just after the last element of the array.
*/
function _end(uint256[] memory array) private pure returns (uint256 ptr) {
unchecked {
return _begin(array) + array.length * 0x20;
}
}
/**
* @dev Load memory word (as a uint256) at location `ptr`.
*/
function _mload(uint256 ptr) private pure returns (uint256 value) {
assembly {
value := mload(ptr)
}
}
/**
* @dev Swaps the elements memory location `ptr1` and `ptr2`.
*/
function _swap(uint256 ptr1, uint256 ptr2) private pure {
assembly {
let value1 := mload(ptr1)
let value2 := mload(ptr2)
mstore(ptr1, value2)
mstore(ptr2, value1)
}
}
/// @dev Helper: low level cast address memory array to uint256 memory array
function _castToUint256Array(address[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 memory array to uint256 memory array
function _castToUint256Array(bytes32[] memory input) private pure returns (uint256[] memory output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast address comp function to uint256 comp function
function _castToUint256Comp(
function(address, address) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/// @dev Helper: low level cast bytes32 comp function to uint256 comp function
function _castToUint256Comp(
function(bytes32, bytes32) pure returns (bool) input
) private pure returns (function(uint256, uint256) pure returns (bool) output) {
assembly {
output := input
}
}
/**
* @dev Searches a sorted `array` and returns the first index that contains
* a value greater or equal to `element`. If no such index exists (i.e. all
* values in the array are strictly less than `element`), the array length is
* returned. Time complexity O(log n).
*
* NOTE: The `array` is expected to be sorted in ascending order, and to
* contain no repeated elements.
*
* IMPORTANT: Deprecated. This implementation behaves as {lowerBound} but lacks
* support for repeated elements in the array. The {lowerBound} function should
* be used instead.
*/
function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
low = mid + 1;
}
}
// At this point `low` is the exclusive upper bound. We will return the inclusive upper bound.
if (low > 0 && unsafeAccess(array, low - 1).value == element) {
return low - 1;
} else {
return low;
}
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value greater or equal than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/lower_bound[lower_bound].
*/
function lowerBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Searches an `array` sorted in ascending order and returns the first
* index that contains a value strictly greater than `element`. If no such index
* exists (i.e. all values in the array are strictly less than `element`), the array
* length is returned. Time complexity O(log n).
*
* See C++'s https://en.cppreference.com/w/cpp/algorithm/upper_bound[upper_bound].
*/
function upperBound(uint256[] storage array, uint256 element) internal view returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeAccess(array, mid).value > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Same as {lowerBound}, but with an array in memory.
*/
function lowerBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) < element) {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
} else {
high = mid;
}
}
return low;
}
/**
* @dev Same as {upperBound}, but with an array in memory.
*/
function upperBoundMemory(uint256[] memory array, uint256 element) internal pure returns (uint256) {
uint256 low = 0;
uint256 high = array.length;
if (high == 0) {
return 0;
}
while (low < high) {
uint256 mid = Math.average(low, high);
// Note that mid will always be strictly less than high (i.e. it will be a valid array index)
// because Math.average rounds towards zero (it does integer division with truncation).
if (unsafeMemoryAccess(array, mid) > element) {
high = mid;
} else {
// this cannot overflow because mid < high
unchecked {
low = mid + 1;
}
}
}
return low;
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(address[] storage arr, uint256 pos) internal pure returns (StorageSlot.AddressSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getAddressSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes32[] storage arr, uint256 pos) internal pure returns (StorageSlot.Bytes32Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytes32Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(uint256[] storage arr, uint256 pos) internal pure returns (StorageSlot.Uint256Slot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getUint256Slot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(bytes[] storage arr, uint256 pos) internal pure returns (StorageSlot.BytesSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getBytesSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeAccess(string[] storage arr, uint256 pos) internal pure returns (StorageSlot.StringSlot storage) {
bytes32 slot;
assembly ("memory-safe") {
slot := arr.slot
}
return slot.deriveArray().offset(pos).getStringSlot();
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(address[] memory arr, uint256 pos) internal pure returns (address res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes32[] memory arr, uint256 pos) internal pure returns (bytes32 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(uint256[] memory arr, uint256 pos) internal pure returns (uint256 res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(bytes[] memory arr, uint256 pos) internal pure returns (bytes memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Access an array in an "unsafe" way. Skips solidity "index-out-of-range" check.
*
* WARNING: Only use if you are certain `pos` is lower than the array length.
*/
function unsafeMemoryAccess(string[] memory arr, uint256 pos) internal pure returns (string memory res) {
assembly {
res := mload(add(add(arr, 0x20), mul(pos, 0x20)))
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(address[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes32[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(uint256[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(bytes[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
/**
* @dev Helper to set the length of a dynamic array. Directly writing to `.length` is forbidden.
*
* WARNING: this does not clear elements if length is reduced, of initialize elements if length is increased.
*/
function unsafeSetLength(string[] storage array, uint256 len) internal {
assembly ("memory-safe") {
sstore(array.slot, len)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC20.sol)
pragma solidity >=0.4.16;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/IERC165.sol)
pragma solidity >=0.4.16;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
/// @notice Thrown when the caller is not the owner.
string internal constant NOT_OWNER = "not owner";
/// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";
/// @notice Thrown when the fee to set exceeds the maximum fee.
string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";
/// @notice Thrown when the value is already set.
string internal constant ALREADY_SET = "already set";
/// @notice Thrown when the IRM is not enabled at market creation.
string internal constant IRM_NOT_ENABLED = "IRM not enabled";
/// @notice Thrown when the LLTV is not enabled at market creation.
string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";
/// @notice Thrown when the market is already created.
string internal constant MARKET_ALREADY_CREATED = "market already created";
/// @notice Thrown when a token to transfer doesn't have code.
string internal constant NO_CODE = "no code";
/// @notice Thrown when the market is not created.
string internal constant MARKET_NOT_CREATED = "market not created";
/// @notice Thrown when not exactly one of the input amount is zero.
string internal constant INCONSISTENT_INPUT = "inconsistent input";
/// @notice Thrown when zero assets is passed as input.
string internal constant ZERO_ASSETS = "zero assets";
/// @notice Thrown when a zero address is passed as input.
string internal constant ZERO_ADDRESS = "zero address";
/// @notice Thrown when the caller is not authorized to conduct an action.
string internal constant UNAUTHORIZED = "unauthorized";
/// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral";
/// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";
/// @notice Thrown when the position to liquidate is healthy.
string internal constant HEALTHY_POSITION = "position is healthy";
/// @notice Thrown when the authorization signature is invalid.
string internal constant INVALID_SIGNATURE = "invalid signature";
/// @notice Thrown when the authorization signature is expired.
string internal constant SIGNATURE_EXPIRED = "signature expired";
/// @notice Thrown when the nonce is invalid.
string internal constant INVALID_NONCE = "invalid nonce";
/// @notice Thrown when a token transfer reverted.
string internal constant TRANSFER_REVERTED = "transfer reverted";
/// @notice Thrown when a token transfer returned false.
string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";
/// @notice Thrown when a token transferFrom reverted.
string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";
/// @notice Thrown when a token transferFrom returned false
string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";
/// @notice Thrown when the maximum uint128 is exceeded.
string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Comparators.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides a set of functions to compare values.
*
* _Available since v5.1._
*/
library Comparators {
function lt(uint256 a, uint256 b) internal pure returns (bool) {
return a < b;
}
function gt(uint256 a, uint256 b) internal pure returns (bool) {
return a > b;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/SlotDerivation.sol)
// This file was procedurally generated from scripts/generate/templates/SlotDerivation.js.
pragma solidity ^0.8.20;
/**
* @dev Library for computing storage (and transient storage) locations from namespaces and deriving slots
* corresponding to standard patterns. The derivation method for array and mapping matches the storage layout used by
* the solidity language / compiler.
*
* See https://docs.soliditylang.org/en/v0.8.20/internals/layout_in_storage.html#mappings-and-dynamic-arrays[Solidity docs for mappings and dynamic arrays.].
*
* Example usage:
* ```solidity
* contract Example {
* // Add the library methods
* using StorageSlot for bytes32;
* using SlotDerivation for bytes32;
*
* // Declare a namespace
* string private constant _NAMESPACE = "<namespace>"; // eg. OpenZeppelin.Slot
*
* function setValueInNamespace(uint256 key, address newValue) internal {
* _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value = newValue;
* }
*
* function getValueInNamespace(uint256 key) internal view returns (address) {
* return _NAMESPACE.erc7201Slot().deriveMapping(key).getAddressSlot().value;
* }
* }
* ```
*
* TIP: Consider using this library along with {StorageSlot}.
*
* NOTE: This library provides a way to manipulate storage locations in a non-standard way. Tooling for checking
* upgrade safety will ignore the slots accessed through this library.
*
* _Available since v5.1._
*/
library SlotDerivation {
/**
* @dev Derive an ERC-7201 slot from a string (namespace).
*/
function erc7201Slot(string memory namespace) internal pure returns (bytes32 slot) {
assembly ("memory-safe") {
mstore(0x00, sub(keccak256(add(namespace, 0x20), mload(namespace)), 1))
slot := and(keccak256(0x00, 0x20), not(0xff))
}
}
/**
* @dev Add an offset to a slot to get the n-th element of a structure or an array.
*/
function offset(bytes32 slot, uint256 pos) internal pure returns (bytes32 result) {
unchecked {
return bytes32(uint256(slot) + pos);
}
}
/**
* @dev Derive the location of the first element in an array from the slot where the length is stored.
*/
function deriveArray(bytes32 slot) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, slot)
result := keccak256(0x00, 0x20)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, address key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, and(key, shr(96, not(0))))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bool key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, iszero(iszero(key)))
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes32 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, uint256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, int256 key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
mstore(0x00, key)
mstore(0x20, slot)
result := keccak256(0x00, 0x40)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, string memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
/**
* @dev Derive the location of a mapping element from the key.
*/
function deriveMapping(bytes32 slot, bytes memory key) internal pure returns (bytes32 result) {
assembly ("memory-safe") {
let length := mload(key)
let begin := add(key, 0x20)
let end := add(begin, length)
let cache := mload(end)
mstore(end, slot)
result := keccak256(begin, add(length, 0x20))
mstore(end, cache)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}{
"remappings": [
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@vault-v2/=lib/vault-v2/",
"@morpho-blue/=lib/morpho-blue/src/",
"@aragon/osx/=lib/osx/packages/contracts/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/",
"osx/=lib/osx/",
"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
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"box","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"string","name":"symbol","type":"string"}],"name":"BoxCreated","type":"event"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"address","name":"feeder","type":"address"}],"name":"addFeeder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"contract IFunding","name":"fundingModule","type":"address"}],"name":"addFunding","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"contract IFunding","name":"fundingModule","type":"address"},{"internalType":"contract IERC20","name":"collateral","type":"address"}],"name":"addFundingCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"contract IFunding","name":"fundingModule","type":"address"},{"internalType":"contract IERC20","name":"debt","type":"address"}],"name":"addFundingDebt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"address","name":"morpho","type":"address"},{"internalType":"uint256","name":"maxLltvRatio","type":"uint256"}],"name":"addFundingMorpho","outputs":[{"internalType":"contract FundingMorpho","name":"fundingMorpho","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"contract FundingMorpho","name":"funding","type":"address"},{"internalType":"address","name":"morpho","type":"address"},{"internalType":"contract IERC20","name":"loan","type":"address"},{"internalType":"contract IERC20","name":"collateral","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"name":"addMorphoMarket","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"contract IOracle","name":"oracle","type":"address"}],"name":"addToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allocators","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"boxCreator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"boxFactory","outputs":[{"internalType":"contract BoxFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"maxSlippage","type":"uint256"},{"internalType":"uint256","name":"slippageEpochDuration","type":"uint256"},{"internalType":"uint256","name":"shutdownSlippageDuration","type":"uint256"},{"internalType":"uint256","name":"shutdownWarmup","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"address","name":"creator","type":"address"}],"name":"createBox","outputs":[{"internalType":"contract IBox","name":"box","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"maxSlippage","type":"uint256"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"createStandardBox","outputs":[{"internalType":"contract IBox","name":"box","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"curator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IBox","name":"box","type":"address"},{"internalType":"address","name":"guardian","type":"address"}],"name":"finalize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vaultAllocator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6080806040523461030457612105461480156102f9575b156100c757505f80546001600160a01b0319908116730a0e559bc3b0950a7e448f0d4894db195b9cf8dd1790915560018054821673827e86072b06674a077f592a531dce4590adecdb17905560028054909116731be9b8c406120b2c2b8990d0522cd226a3d60e36179055610089610308565b61009161038a565b600480546001600160a01b03191673846365f9a09aeb7005127c6060876c82f7f70c0b1790555b60405161524290816103e78239f35b4660010361017057505f80546001600160a01b0319908116730a0e559bc3b0950a7e448f0d4894db195b9cf8dd1790915560018054821673827e86072b06674a077f592a531dce4590adecdb1790556002805490911673aad84c80b013c34d70e54fb343d0c2f309f635e717905561013d610308565b61014561038a565b600480546001600160a01b03191673cf23d316e7c415a70836ec9e68568c3cd82ebfc41790556100b8565b4661a4b10361021a57505f80546001600160a01b0319908116730a0e559bc3b0950a7e448f0d4894db195b9cf8dd1790915560018054821673827e86072b06674a077f592a531dce4590adecdb1790556002805490911673257021f139dcb62a303571666d3718f25f4bf4621790556101e7610308565b6101ef61038a565b600480546001600160a01b0319167372576c537e25aecb3026e5c8ef4b90436e22a3331790556100b8565b466089036102c357505f80546001600160a01b0319908116730a0e559bc3b0950a7e448f0d4894db195b9cf8dd1790915560018054821673827e86072b06674a077f592a531dce4590adecdb1790556002805490911673f4fe5381e526e4e4fee0572881f4d0c5382f9a75179055610290610308565b61029861038a565b600480546001600160a01b03191673a0a261b900d4f2179d6a5317b5cea00fa28392301790556100b8565b62461bcd60e51b81526020600482015260116024820152702ab739bab83837b93a32b21031b430b4b760791b6044820152606490fd5b50617a694614610016565b5f80fd5b600354680100000000000000008110156103765760018101806003558110156103625760035f525f5160206156295f395f51905f520180546001600160a01b03191671aeb716a0df7a9a1aad119b772644bc089da8179055565b634e487b7160e01b5f52603260045260245ffd5b634e487b7160e01b5f52604160045260245ffd5b600354680100000000000000008110156103765760018101806003558110156103625760035f525f5160206156295f395f51905f520180546001600160a01b03191673feed46c11f57b7126a773eec6ae9ca7ae1c03c9a17905556fe60806040526004361015610011575f80fd5b5f5f3560e01c806321bf63d614611e9f578063241c57a214611e5b5780634ab117bc14611d735780634aca692e14611b8857806371004d9814611a15578063782574271461191657806380271fd7146118d557806384b9ee43146117e75780638da5cb5b146117c0578063aba32fd114611432578063bcdf569b14610988578063c6e85b3b14610860578063de64f0ab14610837578063e66f53b71461080e578063f0f81f4e146100f55763f31fcfb7146100ca575f80fd5b346100f257806003193601126100f2576004546040516001600160a01b039091168152602090f35b80fd5b50346100f2576101203660031901126100f257610110611fa0565b6024356001600160401b0381116106995761012f903690600401612079565b906044356001600160401b0381116106bd5761014f903690600401612079565b916101586120bf565b5060048054604051636bee28ed60e11b81526001600160a01b0394851692810183905230602482018190526044820152610140606482015291936020918391168188816101be6101ac61014483018a6120f0565b8281036003190160848401528b6120f0565b60643560a483015260843560c483015260a43560e483015260c43561010483015260e43561012483015203925af19081156108035785916107bd575b506001600160a01b03908116808652600560205260408620549093911661077f5782855260056020526040852080546001600160a01b03191633179055845b6003548110156102c6578561024d82611fe2565b905460039190911b1c6001600160a01b0316853b156102c257604051906358c9542560e11b82526004820152600160248201528181604481838a5af180156102b75761029e575b5050600101610239565b816102a89161203d565b6102b357855f610294565b8580fd5b6040513d84823e3d90fd5b5080fd5b506002549193909185906001600160a01b0316843b156102c257604051906358c9542560e11b8252600482015260016024820152818160448183895af180156102b75790829161076a575b50506002546001600160a01b0316843b156102c25760405190632b30997b60e01b82526004820152818160248183895af180156102b757908291610755575b50506040516338d52e0f60e01b8152602081600481885afa9081156102b757829161071b575b5060405163b3d7f6b960e01b8152633b9aca0060048201526001600160a01b039190911690602081602481895afa9081156107105783916106de575b506040516323b872dd60e01b8152602081806103d385303360048501612114565b038187875af1801561069d576106c1575b506040516386be439760e01b6020820152306024820152600160448201526044815261041160648261203d565b863b156106bd578361043f916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b0381838b5af1801561069d579084916106a8575b5050853b15610699576040516386be439760e01b8152306004820152600160248201528381604481838b5af1801561069d57908493929161067e575b50906044602092604051948593849263095ea7b360e01b84528b600485015260248401525af180156102b757610651575b506040516394bf804d60e01b8152633b9aca00600482015261dead602482015260208160448185895af180156102b75761061e575b506040516386be439760e01b60208201523060248201526044808201839052815261052160648261203d565b843b156102c2578161054f916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183895af180156102b757908291610609575b5050833b156100f2576040516386be439760e01b815230600482015260248101829052818160448183895af180156102b7576105f4575b50507f9313587846a45e3f0342e7e56db607fa3145faab53bf0d0ee138be73b70b38546105dc6020956105e986946040519384936040855260408501906120f0565b908382038a8501526120f0565b0390a3604051908152f35b6105ff82809261203d565b6100f2578061059a565b816106139161203d565b6100f2578086610563565b6020813d602011610649575b816106376020938361203d565b8101031261064557516104f5565b5f80fd5b3d915061062a565b6106729060203d602011610677575b61066a818361203d565b8101906121a3565b6104c0565b503d610660565b8361068c919492939461203d565b610699579082918861048f565b8280fd5b6040513d86823e3d90fd5b816106b29161203d565b610699578288610453565b8380fd5b6106d99060203d6020116106775761066a818361203d565b6103e4565b90506020813d602011610708575b816106f96020938361203d565b810103126106455751876103b2565b3d91506106ec565b6040513d85823e3d90fd5b90506020813d60201161074d575b816107366020938361203d565b810103126102c25761074790612136565b86610376565b3d9150610729565b8161075f9161203d565b6100f2578086610350565b816107749161203d565b6100f2578086610311565b60405162461bcd60e51b8152602060048201526016602482015275109bde08185b1c9958591e481c9959da5cdd195c995960521b6044820152606490fd5b90506020813d6020116107fb575b816107d86020938361203d565b810103126107f757516001600160a01b03811681036107f7575f6101fa565b8480fd5b3d91506107cb565b6040513d87823e3d90fd5b50346100f257806003193601126100f2576001546040516001600160a01b039091168152602090f35b50346100f257806003193601126100f2576002546040516001600160a01b039091168152602090f35b50346100f25760603660031901126100f2578061087b611fa0565b610883611fb6565b604435916001600160a01b038316830361096e576108a08161214a565b604051632a3b5eb960e11b60208201526001600160a01b039190911692906108de816108d08486602484016120d6565b03601f19810183528261203d565b833b156107f7578461090c916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af1908115610803578591610973575b5050823b1561096e5761094c92849283604051809681958294632a3b5eb960e11b8452600484016120d6565b03925af180156102b75761095d5750f35b816109679161203d565b6100f25780f35b505050fd5b8161097d9161203d565b61096e57835f610920565b50346100f25760403660031901126100f257806109a3611fa0565b6109ab611fb6565b906109b58161214a565b604051634506d62560e11b60208201526001600160a01b039283166024808301829052825291909216916109ea60448261203d565b823b1561096e5783610a18916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d57849161141d575b5050813b156112485760405190634506d62560e11b82526004820152828160248183865af1908115610710578391611408575b5050604051634796629160e01b6020820152639de732d360e01b602482015262093a806044808301919091528152610a9860648261203d565b813b156112485782610ac6916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916113f3575b5050803b1561126157604051634796629160e01b8152639de732d360e01b600482015262093a806024820152828160448183865af19081156107105783916113de575b5050604051634796629160e01b6020820152634506d62560e11b602482015262093a806044808301919091528152610b5660648261203d565b813b156112485782610b84916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916113c9575b5050803b1561126157604051634796629160e01b8152634506d62560e11b600482015262093a806024820152828160448183865af19081156107105783916113b4575b5050604051634796629160e01b6020820152635c1a1a4f60e01b602482015262093a806044808301919091528152610c1460648261203d565b813b156112485782610c42916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af190811561071057839161139f575b5050803b1561126157604051634796629160e01b8152635c1a1a4f60e01b600482015262093a806024820152828160448183865af190811561071057839161138a575b5050604051634796629160e01b6020820152635b4099e360e01b602482015262093a806044808301919091528152610cd260648261203d565b813b156112485782610d00916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611375575b5050803b1561126157604051634796629160e01b8152635b4099e360e01b600482015262093a806024820152828160448183865af1908115610710578391611360575b5050604051634796629160e01b6020820152632a3b5eb960e11b60248201526203f4806044808301919091528152610d9060648261203d565b813b156112485782610dbe916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af190811561071057839161134b575b5050803b1561126157604051634796629160e01b8152632a3b5eb960e11b60048201526203f4806024820152828160448183865af1908115610710578391611336575b5050604051634796629160e01b60208201526357984beb60e11b60248201526203f4806044808301919091528152610e4e60648261203d565b813b156112485782610e7c916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611321575b5050803b1561126157604051634796629160e01b81526357984beb60e11b60048201526203f4806024820152828160448183865af190811561071057839161130c575b5050604051634796629160e01b60208201526308ccb85360e01b60248201526203f4806044808301919091528152610f0c60648261203d565b813b156112485782610f3a916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112f7575b5050803b1561126157604051634796629160e01b81526308ccb85360e01b60048201526203f4806024820152828160448183865af19081156107105783916112e2575b5050604051634796629160e01b6020820152630b5af0ad60e41b60248201526203f4806044808301919091528152610fca60648261203d565b813b156112485782610ff8916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112cd575b5050803b1561126157604051634796629160e01b8152630b5af0ad60e41b60048201526203f4806024820152828160448183865af19081156107105783916112b8575b5050604051634796629160e01b60208201526343f68a4960e01b602482015262015180604480830191909152815261108860648261203d565b813b1561124857826110b6916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112a3575b5050803b1561126157604051634796629160e01b81526343f68a4960e01b6004820152620151806024820152828160448183865af190811561071057839161128e575b5050604051634796629160e01b60208201526386be439760e01b6024820152610e10604480830191909152815261114560648261203d565b813b156112485782611173916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611279575b5050803b1561126157604051634796629160e01b81526386be439760e01b6004820152610e106024820152828160448183865af1908115610710578391611264575b50506001546001600160a01b0316813b15611248576040519063e90956cf60e01b82526004820152828160248183865af190811561071057839161124c575b505081546001600160a01b0316813b1561124857829160248392604051948593849263f2fde38b60e01b845260048401525af180156102b75761095d5750f35b5050fd5b816112569161203d565b61126157815f611208565b50fd5b8161126e9161203d565b61126157815f6111c9565b816112839161203d565b61126157815f611187565b816112989161203d565b61126157815f61110d565b816112ad9161203d565b61126157815f6110ca565b816112c29161203d565b61126157815f61104f565b816112d79161203d565b61126157815f61100c565b816112ec9161203d565b61126157815f610f91565b816113019161203d565b61126157815f610f4e565b816113169161203d565b61126157815f610ed3565b8161132b9161203d565b61126157815f610e90565b816113409161203d565b61126157815f610e15565b816113559161203d565b61126157815f610dd2565b8161136a9161203d565b61126157815f610d57565b8161137f9161203d565b61126157815f610d14565b816113949161203d565b61126157815f610c99565b816113a99161203d565b61126157815f610c56565b816113be9161203d565b61126157815f610bdb565b816113d39161203d565b61126157815f610b98565b816113e89161203d565b61126157815f610b1d565b816113fd9161203d565b61126157815f610ada565b816114129161203d565b61126157815f610a5f565b816114279161203d565b61124857825f610a2c565b50346100f2576101003660031901126100f25761144d611fa0565b6024356001600160a01b03811691908290036106995761146b611fcc565b916064356001600160a01b03811692908390036107f7576084356001600160a01b03811694908590036102b35760a4356001600160a01b038116908190036117bc5760c4356001600160a01b03811691908290036117b8576114cc8461214a565b604051956114d98761200e565b8652602086019687526040860190815260608601918252608086019260e435845260a0808820602460405180948193632c3c915760e01b83526004830152600180861b03165afa9081156117ad578991611724575b505186516001600160a01b039182169116036116df576040516384c1b85360e01b815295516001600160a01b0390811660048801529651871660248701525186166044860152519094166064840152925160848301528392838360a481855afa92831561069d578493611664575b5060018060a01b0316916040516357984beb60e11b6020820152826024820152604060448201526115d4816108d060648201856120f0565b833b156107f75784611602916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af190811561080357859161164f575b5050823b1561096e5761094c928492836040518096819582946357984beb60e11b845260048401526040602484015260448301906120f0565b816116599161203d565b61096e57835f611616565b9092503d8085833e611676818361203d565b8101906020818303126107f7578051906001600160401b0382116102b3570181601f820112156107f7578051906116ac8261205e565b926116ba604051948561203d565b828452602083830101116102b35781869260208093018386015e83010152915f61159c565b60405162461bcd60e51b815260206004820152601b60248201527f4d61726b6574206e6f74206c6973746564206f6e204d6f7270686f00000000006044820152606490fd5b905060a0813d60a0116117a5575b8161173f60a0938361203d565b810103126117a1576080604051916117568361200e565b61175f81612136565b835261176d60208201612136565b602084015261177e60408201612136565b604084015261178f60608201612136565b6060840152015160808201525f61152e565b8880fd5b3d9150611732565b6040513d8b823e3d90fd5b8780fd5b8680fd5b50346100f257806003193601126100f257546040516001600160a01b039091168152602090f35b50346100f25760403660031901126100f25780611802611fa0565b61180a611fb6565b906118148161214a565b604051639de732d360e01b60208201526001600160a01b0392831660248083018290528252919092169161184960448261203d565b823b1561096e5783611877916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d5784916118c0575b5050813b15611248578291602483926040519485938492639de732d360e01b845260048401525af180156102b75761095d5750f35b816118ca9161203d565b61124857825f61188b565b50346100f25760203660031901126100f2576020906001600160a01b036118fa611fa0565b16815260058252604060018060a01b0391205416604051908152f35b50346100f25760403660031901126100f25780611931611fa0565b611939611fb6565b906119438161214a565b6040516386be439760e01b60208201526001600160a01b039283166024820181905260016044808401919091528252919092169161198260648261203d565b823b1561096e57836119b0916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d578491611a00575b5050813b156112485782916044839260405194859384926386be439760e01b84526004840152600160248401525af180156102b75761095d5750f35b81611a0a9161203d565b61124857825f6119c4565b50346100f25760603660031901126100f257611a2f611fa0565b611a37611fb6565b90611a418161214a565b6040516001600160a01b03919091169161305180830191906001600160401b03831184841017611b74578392869492611a83926121bc85398660443592612114565b039082f0918215611b6757604051639de732d360e01b60208201526001600160a01b039093166024808501829052845292611abf60448261203d565b813b156106995782611aed916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611b52575b5050803b156102c2578190602460405180948193639de732d360e01b83528760048401525af1801561071057611b3d575b602082604051908152f35b611b4883809261203d565b6102c25781611b32565b81611b5c9161203d565b6102c257815f611b01565b50604051903d90823e3d90fd5b634e487b7160e01b86526041600452602486fd5b50346100f25760a03660031901126100f257611ba2611fa0565b6024356001600160401b03811161069957611bc1903690600401612079565b906044356001600160401b0381116106bd57611be1903690600401612079565b60048054604051636bee28ed60e11b81526001600160a01b0394851692810183905230602482018190526044820152610140606482015292949193919291602091839116818881611c396101ac61014483018a6120f0565b60643560a483015262093a8060c4830152620d2f0060e483015262093a8061010483015260843561012483015203925af1908115610803578591611d31575b506001600160a01b03908116808652600560205260408620549093911661077f5782855260056020526040852080546001600160a01b03191633179055845b6003548110156102c65785611ccb82611fe2565b905460039190911b1c6001600160a01b0316853b156102c257604051906358c9542560e11b82526004820152600160248201528181604481838a5af180156102b757611d1c575b5050600101611cb7565b81611d269161203d565b6102b357855f611d12565b90506020813d602011611d6b575b81611d4c6020938361203d565b810103126107f757516001600160a01b03811681036107f7575f611c78565b3d9150611d3f565b50346100f25760603660031901126100f25780611d8e611fa0565b611d96611fb6565b611d9e611fcc565b91611da88161214a565b604051630b5af0ad60e41b60208201526001600160a01b03919091169290611dd8816108d08486602484016120d6565b833b156107f75784611e06916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af1908115610803578591611e46575b5050823b1561096e5761094c92849283604051809681958294630b5af0ad60e41b8452600484016120d6565b81611e509161203d565b61096e57835f611e1a565b50346100f25760203660031901126100f257600435906003548210156100f2576020611e8683611fe2565b905460405160039290921b1c6001600160a01b03168152f35b503461064557606036600319011261064557611eb9611fa0565b90611ec2611fb6565b611eca611fcc565b92611ed48161214a565b6040516308ccb85360e01b6020820152936001600160a01b03919091169190611f1385611f058385602484016120d6565b03601f19810187528661203d565b823b15610645575f611f41956040518097819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af18015611f9557611f7f575b839450823b1561096e5761094c928492836040518096819582946308ccb85360e01b8452600484016120d6565b9250925f611f8c9161203d565b5f918390611f52565b6040513d5f823e3d90fd5b600435906001600160a01b038216820361064557565b602435906001600160a01b038216820361064557565b604435906001600160a01b038216820361064557565b600354811015611ffa5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b60a081019081106001600160401b0382111761202957604052565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761202957604052565b6001600160401b03811161202957601f01601f191660200190565b81601f82011215610645578035906120908261205e565b9261209e604051948561203d565b8284526020838301011161064557815f926020809301838601378301015290565b61010435906001600160a01b038216820361064557565b6001600160a01b0391821681529116602082015260400190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b6001600160a01b03918216815291166020820152604081019190915260600190565b51906001600160a01b038216820361064557565b6001600160a01b039081165f9081526005602052604090205416330361216c57565b60405162461bcd60e51b815260206004820152600f60248201526e2737ba103137bc1031b932b0ba37b960891b6044820152606490fd5b9081602091031261064557518015158103610645579056fe60e03461017f57601f61305138819003918201601f19168301916001600160401b038311848410176101835780849260609460405283398101031261017f5761004781610197565b90604061005660208301610197565b910151916001600160a01b03811615610170576080526001600160a01b0316801561017057670de0b6b3a764000082116101615781156101615760a05260c052604051612ea590816101ac823960805181818161035a015281816104560152818161062c015281816109d701528181610a8701528181610b3601528181610db901528181610f230152818161101b0152818161137e0152818161158501526115f8015260a0518181816102f101528181610bdf01528181610e7e015281816110a70152818161123e015281816113e001528181611683015281816117eb01528181611d8401528181612134015281816123c801526129c0015260c051818181610ace0152610c710152f35b632a9ffab760e21b5f5260045ffd5b63e6c4247b60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361017f5756fe60806040526004361015610010575b005b5f5f3560e01c8063075300a1146117b057806310d967591461177757806311005b071461175457806313c71131146115e4578063172c48c7146115c55780632991889914611569578063351210871461134b57806338034739146113105780633cb82c72146112bb57806346d360aa1461129d5780634766162c146112625780635bc01cb8146111fc5780635bf2adde14611006578063627fa7f814610fdb578063681b317914610f7a5780636b4f41af14610f075780637210ffe114610da557806384c1b85314610cf4578063894db62b14610b22578063896bcd7b14610af15780638d0fd74114610ab65780638da5cb5b14610a7157806394f9059f146109ba578063a1bf284014610996578063a2619ae714610937578063ac9650d81461083a578063b43f82a01461081c578063b4d8144c146105f6578063bc25cf771461043a578063c4d68a1a1461033d578063ca169c3814610320578063d8fbc833146102db578063e158c62414610284578063ea90eb2c14610221578063f48f3b40146101e95763faa0de75146101a357005b346101e65760203660031901126101e657600435906001600160401b0382116101e65760206101de6101d8366004860161180f565b9061211b565b604051908152f35b80fd5b50346101e65760203660031901126101e6576020610208600435612a9b565b905460405160039290921b1c6001600160a01b03168152f35b50346101e657806003193601126101e65760045461023e816120b4565b915b81811061025957604051806102558582611ab6565b0390f35b80610265600192612a9b565b838060a01b0391549060031b1c1661027d8286612107565b5201610240565b50346101e657806003193601126101e65780546102a0816120b4565b915b8181106102b757604051806102558582611af8565b806102c3600192612a6d565b90549060031b1c6102d48286612107565b52016102a2565b50346101e657806003193601126101e6576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101e657806003193601126101e65760209054604051908152f35b50346101e65760203660031901126101e65761035761183c565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361042b5761039182612361565b61041c578054916001600160a01b0316815b8381106103cb57506103b490612d39565b156103bc5780f35b630b094f2760e31b8152600490fd5b6103d481612a6d565b90549060031b1c835260066020528160018060a01b036103fe6103f960408720611a16565b611be2565b51161461040d576001016103a3565b631acba50760e21b8352600483fd5b631acba50760e21b8152600490fd5b635fc483c560e01b8152600490fd5b50346101e65760203660031901126101e65761045461183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316338190036105e75761048f81611d4e565b916001600160a01b0381169081156105ae576040516370a0823160e01b815230600482015290602082602481865afa9182156105a357869261056b575b50811561055c576040519163a9059cbb60e01b87528460045260245260208660448180855af190600187511482161561053d575b50906040521561052b575061051490611d4e565b0361051c5780f35b63eeb3730b60e01b8152600490fd5b635274afe760e01b8452600452602483fd5b6001821516610553573b15153d1516165f610500565b823d88823e3d90fd5b63162908e360e11b8652600486fd5b9091506020813d60201161059b575b816105876020938361192d565b810103126105975751905f6104cc565b5f80fd5b3d915061057a565b6040513d88823e3d90fd5b50504780156105d85783808080938582f1156105cd5761051490611d4e565b6040513d84823e3d90fd5b63162908e360e11b8452600484fd5b635fc483c560e01b8352600483fd5b50346101e65760203660031901126101e6576004356001600160401b0381116108185761062790369060040161180f565b9091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610809576106686103f9368386611969565b6020818101516001600160a01b03165f90815260039091526040902054156107fa57516001600160a01b03165f90815260056020526040902054156107eb576106b2368285611969565b602081519101206106c281612b83565b156107dc578252600660205260408220906001600160401b0381116107c8576106eb82546119de565b601f811161078d575b5082601f821160011461072d57839482939492610722575b50508160011b915f199060031b1c191617905580f35b013590505f8061070c565b601f198216948385526020852091855b87811061077557508360019596971061075c575b505050811b01905580f35b01355f19600384901b60f8161c191690555f8080610751565b9092602060018192868601358155019401910161073d565b6107b89083855260208520601f840160051c810191602085106107be575b601f0160051c0190611b91565b5f6106f4565b90915081906107ab565b634e487b7160e01b83526041600452602483fd5b63b73e95e160e01b8352600483fd5b6307c241ad60e51b8252600482fd5b6307c241ad60e51b8352600483fd5b635fc483c560e01b8252600482fd5b5080fd5b50346101e657806003193601126101e6576020600254604051908152f35b50346101e65760203660031901126101e6576004356001600160401b0381116108185736602382011215610818578181600401356001600160401b038111610818573660248260051b85010111610818579160421981360301915b83811015610933578460248260051b84010135848112156108185783016024810135906001600160401b03821161092f5760440190803603821361092f57829181604051928392833781018381520390305af43d15610927573d906108f98261194e565b91610907604051938461192d565b82523d87602084013e5b1561091f5750600101610895565b805190602001fd5b606090610911565b8280fd5b8480f35b50346101e657806003193601126101e657600254610954816120b4565b915b81811061096b57604051806102558582611ab6565b80610977600192612a83565b838060a01b0391549060031b1c1661098f8286612107565b5201610956565b50346101e65760203660031901126101e65760206101de6109b561183c565b612953565b50346101e65760203660031901126101e6576109d461183c565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361042b57610a0e82612953565b61041c578054916001600160a01b0316815b838110610a3157506103b490612c8e565b610a3a81612a6d565b90549060031b1c835260066020528160018060a01b036020610a616103f960408820611a16565b0151161461040d57600101610a20565b50346101e657806003193601126101e6576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101e657806003193601126101e65760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101e65760203660031901126101e657600435906001600160a01b03821682036101e65760206101de83611d4e565b50346101e657610b31366118b8565b9092907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169033829003610ce557610b8d610b75368686611969565b602081519101205f52600160205260405f2054151590565b15610cd6576001600160a01b0385165f9081526003602052604090205415610cc757908591610bdd610bc36103f9368888611969565b602081015190976001600160a01b03908116911614611b31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610cc3576101048492836040519586948593638720316d60e01b8552610c358d600487019061199f565b60a48501523060c485015260e48401525af180156105cd57610cae575b50506080610c6c670de0b6b3a764000093610c969361211b565b9301517f000000000000000000000000000000000000000000000000000000000000000090611ba7565b0410610c9f5780f35b635d386c8960e11b8152600490fd5b81610cb89161192d565b610cc357835f610c52565b8380fd5b6307c241ad60e51b8652600486fd5b630b094f2760e31b8652600486fd5b635fc483c560e01b8652600486fd5b50346101e65760a03660031901126101e657604051610d12816118fe565b610d1a61183c565b81526024356001600160a01b038116810361092f5760208201526044356001600160a01b038116810361092f5760408201526064356001600160a01b038116810361092f57610255925060608201526084356080820152610d8260405191602083019061199f565b60a08152610d9160c08261192d565b604051918291602083526020830190611894565b50346101e657610db4366118b8565b9092907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003610ce557610df7610b75368686611969565b15610cd6576001600160a01b0385165f9081526005602052604090205415610cc757610e41610e2a6103f9368787611969565b805190966001600160a01b03908116911614611c87565b6040516350d8cd4b60e01b815291610e5c600484018761199f565b60a483015260c482018690523060e483015261010482015260408161012481887f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015610efc57670de0b6b3a764000093610c9693608093610c6c93610ece575b5061211b565b610eef9060403d604011610ef5575b610ee7818361192d565b810190611ce1565b50610ec8565b503d610edd565b6040513d87823e3d90fd5b50346101e65760203660031901126101e657610f2161183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361080957610f63906001600160a01b0316612b33565b15610f6b5780f35b63b73e95e160e01b8152600490fd5b50346101e65760203660031901126101e657600435906001600160401b0382116101e6576020610fd1610fba610fb3366004870161180f565b3691611969565b8281519101205f52600160205260405f2054151590565b6040519015158152f35b50346101e65760203660031901126101e657610d916040610255926004358152600660205220611a16565b50346101e657611015366118b8565b909290917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036111ed57611057610b75368385611969565b156111de576001600160a01b0384165f90815260056020526040902054156111cf576103f98592611089923691611969565b80519092906110a5906001600160a01b03868116911614611c87565b7f0000000000000000000000000000000000000000000000000000000000000000906110d230858461253e565b6001600160a01b038316956110ea90839088906123f3565b810361117557506040926111279261110b61014493309060a08520906128f5565b95855196879586946320b76e8160e01b8652600486019061199f565b8260a485015260c48401523060e4840152610120610104840152816101248401525af180156105cd57611158575080f35b6111709060403d604011610ef557610ee7818361192d565b505080f35b9361014491509261119e92604094855196879586946320b76e8160e01b8652600486019061199f565b60a48401528160c48401523060e4840152610120610104840152816101248401525af180156105cd57611158575080f35b6307c241ad60e51b8552600485fd5b630b094f2760e31b8552600485fd5b635fc483c560e01b8552600485fd5b50346101e65760206101de6103f961123a61122361121936611852565b9491903691611969565b805190926001600160a01b03908116911614611c87565b30907f000000000000000000000000000000000000000000000000000000000000000061253e565b50346101e65760203660031901126101e6576020610fd161128161183c565b6001600160a01b03165f90815260036020526040902054151590565b50346101e657806003193601126101e6576020600454604051908152f35b50346101e65760203660031901126101e657600435906001600160401b0382116101e657366023830112156101e65760a06113016103f936600486013560248701611969565b61130e604051809261199f565bf35b50346101e65760203660031901126101e6576020610fd161132f61183c565b6001600160a01b03165f90815260056020526040902054151590565b50346101e65760203660031901126101e6576004356001600160401b0381116108185761137c90369060040161180f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036105e75760a06113bc6103f9368486611969565b206040516349e2903160e11b815260048101919091523060248201526060816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561155e5784916114d6575b5060408101516001600160801b031615801591906114bf575b5061040d5790611442913691611969565b6020815191012061145281612bcf565b156114b05781526006602052806040812061146d81546119de565b80611476575050f35b601f811160011461148657505580f35b818352602083206114a291601f0160051c810190600101611b91565b808252816020812091555580f35b630b094f2760e31b8252600482fd5b602001516001600160801b0316151590505f611431565b90506060813d606011611556575b816114f16060938361192d565b81010312610cc35760405190606082018281106001600160401b03821117611542576115379160409182528051845261152c6020820161252a565b60208501520161252a565b60408201525f611418565b634e487b7160e01b86526041600452602486fd5b3d91506114e4565b6040513d86823e3d90fd5b50346101e65760203660031901126101e65761158361183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361080957610f63906001600160a01b0316612ac8565b50346101e65760203660031901126101e6576020610208600435612a83565b5034610597576115f3366118b8565b9290917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361174557611634610b75368385611969565b15611736576001600160a01b0383165f908152600360205260409020541561172757611665916103f9913691611969565b6020810151611681906001600160a01b03848116911614611b31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316916116b990849084906123f3565b813b15610597576101246116e6915f8094604051968795869463238d657960e01b8652600486019061199f565b60a48401523060c484015261010060e4840152816101048401525af1801561171c57611710575080f35b61000e91505f9061192d565b6040513d5f823e3d90fd5b6307c241ad60e51b5f5260045ffd5b630b094f2760e31b5f5260045ffd5b635fc483c560e01b5f5260045ffd5b346105975760203660031901126105975760206101de61177261183c565b612361565b3461059757602036600319011261059757611793600435612a6d565b90549060031b1c5f526006602052610255610d9160405f20611a16565b346105975760206101de60a06103f96117e66117ce61121936611852565b8581015190925f196001861b01908116911614611b31565b2030907f00000000000000000000000000000000000000000000000000000000000000006122e2565b9181601f84011215610597578235916001600160401b038311610597576020838186019501011161059757565b600435906001600160a01b038216820361059757565b604060031982011261059757600435906001600160401b0382116105975761187c9160040161180f565b90916024356001600160a01b03811681036105975790565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b606060031982011261059757600435906001600160401b038211610597576118e29160040161180f565b90916024356001600160a01b0381168103610597579060443590565b60a081019081106001600160401b0382111761191957604052565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761191957604052565b6001600160401b03811161191957601f01601f191660200190565b9291926119758261194e565b91611983604051938461192d565b829481845281830111610597578281602093845f960137010152565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015190911690830152608090810151910152565b90600182811c92168015611a0c575b60208310146119f857565b634e487b7160e01b5f52602260045260245ffd5b91607f16916119ed565b9060405191825f825492611a29846119de565b8084529360018116908115611a945750600114611a50575b50611a4e9250038361192d565b565b90505f9291925260205f20905f915b818310611a78575050906020611a4e928201015f611a41565b6020919350806001915483858901015201910190918492611a5f565b905060209250611a4e94915060ff191682840152151560051b8201015f611a41565b60206040818301928281528451809452019201905f5b818110611ad95750505090565b82516001600160a01b0316845260209384019390920191600101611acc565b60206040818301928281528451809452019201905f5b818110611b1b5750505090565b8251845260209384019390920191600101611b0e565b15611b3857565b60405162461bcd60e51b815260206004820152602b60248201527f46756e64696e674d6f64756c654d6f7270686f3a2057726f6e6720636f6c6c6160448201526a3a32b930b6103a37b5b2b760a91b6064820152608490fd5b818110611b9c575050565b5f8155600101611b91565b81810292918115918404141715611bba57565b634e487b7160e01b5f52601160045260245ffd5b51906001600160a01b038216820361059757565b5f6080604051611bf1816118fe565b828152826020820152826040820152826060820152015260a0815103611c785760a0818051810103126105975760a060405191611c2d836118fe565b611c3960208201611bce565b8352611c4760408201611bce565b6020840152611c5860608201611bce565b6040840152611c6960808201611bce565b60608401520151608082015290565b632eb79b5360e21b5f5260045ffd5b15611c8e57565b60405162461bcd60e51b815260206004820152602560248201527f46756e64696e674d6f64756c654d6f7270686f3a2057726f6e672064656274206044820152643a37b5b2b760d91b6064820152608490fd5b9190826040910312610597576020825192015190565b8115611d01570490565b634e487b7160e01b5f52601260045260245ffd5b9081602091031261059757516001600160a01b03811681036105975790565b91908201809211611bba57565b91908203918211611bba57565b6040516338d52e0f60e01b81525f916001600160a01b0316602082600481845afa91821561171c575f92612061575b505f545f927f0000000000000000000000000000000000000000000000000000000000000000925b828510611db457505050505090565b90919293945f90611dc487612a6d565b90549060031b1c5f526006602052611de16103f960405f20611a16565b611def3060a08320896122e2565b80156120555760208201516001600160a01b038781169591169190858303611f7a57611e1b9250611d34565b925b611e2830838a61253e565b91516001600160a01b0316908103611e6e5750600192611e5a929180821115611e6657611e5491611d41565b90611d34565b955b0193929190611da5565b50505f611e54565b906040519163addd509960e01b83526004830152602082602481885afa91821561171c576004926020915f91611f4d575b5060405163501ad8ff60e11b815293849182906001600160a01b03165afa90811561171c575f91611f18575b611ed59250611ba7565b906ec097ce7bc90715b34b9f0fffffffff8201809211611bba576001926a0c097ce7bc90715b34b9f160241b611e5a93048082115f14611e6657611e5491611d41565b90506020823d8211611f45575b81611f326020938361192d565b8101031261059757611ed5915190611ecb565b3d9150611f25565b611f6d9150823d8111611f73575b611f65818361192d565b810190611d15565b5f611e9f565b503d611f5b565b50906040519063addd509960e01b82526004820152602081602481895afa90811561171c576004916020915f91612038575b5060405163501ad8ff60e11b815292839182906001600160a01b03165afa90811561171c575f91611ff9575b50611ff2906a0c097ce7bc90715b34b9f160241b92611ba7565b0492611e1d565b90506020813d8211612030575b816120136020938361192d565b8101031261059757516a0c097ce7bc90715b34b9f160241b611fd8565b3d9150612006565b61204f9150823d8111611f7357611f65818361192d565b5f611fac565b50509560019150611e5c565b9091506020813d602011612095575b8161207d6020938361192d565b810103126105975761208e90611bce565b905f611d7d565b3d9150612070565b6001600160401b0381116119195760051b60200190565b906120be8261209d565b6120cb604051918261192d565b82815280926120dc601f199161209d565b0190602036910137565b8051156120f35760200190565b634e487b7160e01b5f52603260045260245ffd5b80518210156120f35760209160051b010190565b6103f99061212a923691611969565b60a08120906121667f000000000000000000000000000000000000000000000000000000000000000061215e30848361253e565b9330916122e2565b9081156122625760400180516001600160a01b031615612253575160405163501ad8ff60e11b815290602090829060049082906001600160a01b03165afa90811561171c575f91612213575b506121cc906a0c097ce7bc90715b34b9f160241b92611ba7565b04806121d85750505f90565b670de0b6b3a764000091828102928184041490151715611bba575f19810191818311611bba576122109261220b91611d34565b611cf7565b90565b90506020813d60201161224b575b8161222e6020938361192d565b8101031261059757516a0c097ce7bc90715b34b9f160241b6121b2565b3d9150612221565b630bd226f160e31b5f5260045ffd5b5050505f90565b602081830312610597578051906001600160401b03821161059757019080601f8301121561059757815161229c8161209d565b926122aa604051948561192d565b81845260208085019260051b82010192831161059757602001905b8282106122d25750505090565b81518152602091820191016122c5565b6122f86122f3612314945f946129eb565b612a46565b906040518080958194637784c68560e01b835260048301611af8565b03916001600160a01b03165afa801561171c57612338915f9161233f575b506120e6565b5160801c90565b61235b91503d805f833e612353818361192d565b810190612269565b5f612332565b905f915f54905f5b82811061237557505050565b61237e81612a6d565b90549060031b1c5f52600660205261239b6103f960405f20611a16565b80516001600160a01b038481169116146123b9575b50600101612369565b60019195611e546123ec9230907f000000000000000000000000000000000000000000000000000000000000000061253e565b94906123b0565b916040519163095ea7b360e01b5f5260018060a01b031691826004528160245260205f60448180885af19060015f511482161561251b575b6040521561243857505050565b60405163095ea7b360e01b5f52826004525f60245260205f60448180885af19060015f5114821615612503575b604052156124e5576040519163095ea7b360e01b5f5260045260245260205f60448180865af19060015f51148216156124c4575b604052156124a45750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b9060018115166124dc57823b15153d15161690612499565b503d5f823e3d90fd5b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b9060018115166124dc57843b15153d15161690612465565b90843b15153d1516169061242b565b51906001600160801b038216820361059757565b9161254d9060a08320846128f5565b9060c060a08220602460405180968193632e3071cd60e11b8352600483015260018060a01b03165afa92831561171c575f93612844575b5060808301805161259e906001600160801b031642611d41565b8015158061282e575b80612818575b61260b575b50505060408201516060909201516001600160801b03908116921660018101908110611bba57620f4240830191828411611bba57620f423f916125f491611ba7565b920191818311611bba576122109261220b91611d34565b6060830151604051638c00bf6b60e01b8152936001600160a01b03909116919061263990600486019061199f565b85516001600160801b0390811660a486015260208088018051831660c4880152604089018051841660e489015260608a015184166101048901529551831661012488015260a08901805190931661014488015295919382906101649082905afa90811561171c575f916127de575b50612708906127026126cc670de0b6b3a76400009460018060801b0388511693611ba7565b6126fd671bc16d674ec800006126e28380611ba7565b046729a2241af62c00006126f68483611ba7565b0492611d34565b611d34565b90611ba7565b04916001600160801b0361272e61271e85612dfd565b83516001600160801b0316612de4565b1690526001600160801b0361275561274584612dfd565b87516001600160801b0316612de4565b168552516001600160801b03169081156125b257670de0b6b3a76400009161277c91611ba7565b046127908160018060801b03865116611d41565b60018060801b0383511690620f42408201809211611bba5760018101809111611bba576127ce61271e9161220b6127d39460018060801b0396611ba7565b612dfd565b1690525f80806125b2565b90506020813d602011612810575b816127f96020938361192d565b810103126105975751670de0b6b3a76400006126a7565b3d91506127ec565b5060608301516001600160a01b031615156125ad565b5060408501516001600160801b031615156125a7565b90925060c0813d60c0116128ed575b8161286060c0938361192d565b81010312610597576040519060c082018281106001600160401b03821117611919576128e19160a0916040526128958161252a565b84526128a36020820161252a565b60208501526128b46040820161252a565b60408501526128c56060820161252a565b60608501526128d66080820161252a565b60808501520161252a565b60a0820152915f612584565b3d9150612853565b6122f86122f3612906945f946129eb565b03916001600160a01b03165afa90811561171c575f91612939575b506001600160801b0390612934906120e6565b511690565b61294d91503d805f833e612353818361192d565b5f612921565b905f915f54905f5b82811061296757505050565b61297081612a6d565b90549060031b1c5f52600660205261298d6103f960405f20611a16565b60208101516001600160a01b038481169116146129ae575b5060010161295b565b60019195611e5460a06129e4932030907f00000000000000000000000000000000000000000000000000000000000000006122e2565b94906129a5565b604051602081019182526002604082015260408152612a0b60608261192d565b51902060405190602082019260018060a01b03168352604082015260408152612a3560608261192d565b51902060018101809111611bba5790565b60405190612a5560408361192d565b6001825260203681840137612a69826120e6565b5290565b5f548110156120f3575f805260205f2001905f90565b6002548110156120f35760025f5260205f2001905f90565b6004548110156120f35760045f5260205f2001905f90565b80548210156120f3575f5260205f2001905f90565b805f52600360205260405f2054155f14612b2e57600254600160401b81101561191957612b17612b018260018594016002556002612ab3565b819391549060031b91821b915f19901b19161790565b9055600254905f52600360205260405f2055600190565b505f90565b805f52600560205260405f2054155f14612b2e57600454600160401b81101561191957612b6c612b018260018594016004556004612ab3565b9055600454905f52600560205260405f2055600190565b805f52600160205260405f2054155f14612b2e575f54600160401b81101561191957612bb9612b018260018594015f555f612ab3565b90555f54905f52600160205260405f2055600190565b5f818152600160205260409020548015612c88575f198101818111611bba575f545f19810191908211611bba57818103612c52575b5050505f548015612c3e575f1901612c1c815f612ab3565b8154905f199060031b1b191690555f555f5260016020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b612c72612c62612b01935f612ab3565b90549060031b1c9283925f612ab3565b90555f52600160205260405f20555f8080612c04565b50505f90565b5f818152600360205260409020548015612c88575f198101818111611bba576002545f19810191908211611bba57818103612d01575b5050506002548015612c3e575f1901612cde816002612ab3565b8154905f199060031b1b191690556002555f5260036020525f6040812055600190565b612d23612d12612b01936002612ab3565b90549060031b1c9283926002612ab3565b90555f52600360205260405f20555f8080612cc4565b5f818152600560205260409020548015612c88575f198101818111611bba576004545f19810191908211611bba57818103612dac575b5050506004548015612c3e575f1901612d89816004612ab3565b8154905f199060031b1b191690556004555f5260056020525f6040812055600190565b612dce612dbd612b01936004612ab3565b90549060031b1c9283926004612ab3565b90555f52600560205260405f20555f8080612d6f565b6001600160801b039182169082160191908211611bba57565b604051612e0b60408261192d565b60148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201526001600160801b038211612e4757506001600160801b031690565b60405162461bcd60e51b815260206004820152908190612e6b906024830190611894565b0390fdfea2646970667358221220c753b8f190b70487d835ac3dee9447b309017863a17dc353ba7e5445107cac4664736f6c634300081c0033a26469706673582212205995eb4c93036d1c26d86543f6398941f1fd6cc69d91eab02d28eadf0b3d7f2264736f6c634300081c0033c2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b
Deployed Bytecode
0x60806040526004361015610011575f80fd5b5f5f3560e01c806321bf63d614611e9f578063241c57a214611e5b5780634ab117bc14611d735780634aca692e14611b8857806371004d9814611a15578063782574271461191657806380271fd7146118d557806384b9ee43146117e75780638da5cb5b146117c0578063aba32fd114611432578063bcdf569b14610988578063c6e85b3b14610860578063de64f0ab14610837578063e66f53b71461080e578063f0f81f4e146100f55763f31fcfb7146100ca575f80fd5b346100f257806003193601126100f2576004546040516001600160a01b039091168152602090f35b80fd5b50346100f2576101203660031901126100f257610110611fa0565b6024356001600160401b0381116106995761012f903690600401612079565b906044356001600160401b0381116106bd5761014f903690600401612079565b916101586120bf565b5060048054604051636bee28ed60e11b81526001600160a01b0394851692810183905230602482018190526044820152610140606482015291936020918391168188816101be6101ac61014483018a6120f0565b8281036003190160848401528b6120f0565b60643560a483015260843560c483015260a43560e483015260c43561010483015260e43561012483015203925af19081156108035785916107bd575b506001600160a01b03908116808652600560205260408620549093911661077f5782855260056020526040852080546001600160a01b03191633179055845b6003548110156102c6578561024d82611fe2565b905460039190911b1c6001600160a01b0316853b156102c257604051906358c9542560e11b82526004820152600160248201528181604481838a5af180156102b75761029e575b5050600101610239565b816102a89161203d565b6102b357855f610294565b8580fd5b6040513d84823e3d90fd5b5080fd5b506002549193909185906001600160a01b0316843b156102c257604051906358c9542560e11b8252600482015260016024820152818160448183895af180156102b75790829161076a575b50506002546001600160a01b0316843b156102c25760405190632b30997b60e01b82526004820152818160248183895af180156102b757908291610755575b50506040516338d52e0f60e01b8152602081600481885afa9081156102b757829161071b575b5060405163b3d7f6b960e01b8152633b9aca0060048201526001600160a01b039190911690602081602481895afa9081156107105783916106de575b506040516323b872dd60e01b8152602081806103d385303360048501612114565b038187875af1801561069d576106c1575b506040516386be439760e01b6020820152306024820152600160448201526044815261041160648261203d565b863b156106bd578361043f916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b0381838b5af1801561069d579084916106a8575b5050853b15610699576040516386be439760e01b8152306004820152600160248201528381604481838b5af1801561069d57908493929161067e575b50906044602092604051948593849263095ea7b360e01b84528b600485015260248401525af180156102b757610651575b506040516394bf804d60e01b8152633b9aca00600482015261dead602482015260208160448185895af180156102b75761061e575b506040516386be439760e01b60208201523060248201526044808201839052815261052160648261203d565b843b156102c2578161054f916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183895af180156102b757908291610609575b5050833b156100f2576040516386be439760e01b815230600482015260248101829052818160448183895af180156102b7576105f4575b50507f9313587846a45e3f0342e7e56db607fa3145faab53bf0d0ee138be73b70b38546105dc6020956105e986946040519384936040855260408501906120f0565b908382038a8501526120f0565b0390a3604051908152f35b6105ff82809261203d565b6100f2578061059a565b816106139161203d565b6100f2578086610563565b6020813d602011610649575b816106376020938361203d565b8101031261064557516104f5565b5f80fd5b3d915061062a565b6106729060203d602011610677575b61066a818361203d565b8101906121a3565b6104c0565b503d610660565b8361068c919492939461203d565b610699579082918861048f565b8280fd5b6040513d86823e3d90fd5b816106b29161203d565b610699578288610453565b8380fd5b6106d99060203d6020116106775761066a818361203d565b6103e4565b90506020813d602011610708575b816106f96020938361203d565b810103126106455751876103b2565b3d91506106ec565b6040513d85823e3d90fd5b90506020813d60201161074d575b816107366020938361203d565b810103126102c25761074790612136565b86610376565b3d9150610729565b8161075f9161203d565b6100f2578086610350565b816107749161203d565b6100f2578086610311565b60405162461bcd60e51b8152602060048201526016602482015275109bde08185b1c9958591e481c9959da5cdd195c995960521b6044820152606490fd5b90506020813d6020116107fb575b816107d86020938361203d565b810103126107f757516001600160a01b03811681036107f7575f6101fa565b8480fd5b3d91506107cb565b6040513d87823e3d90fd5b50346100f257806003193601126100f2576001546040516001600160a01b039091168152602090f35b50346100f257806003193601126100f2576002546040516001600160a01b039091168152602090f35b50346100f25760603660031901126100f2578061087b611fa0565b610883611fb6565b604435916001600160a01b038316830361096e576108a08161214a565b604051632a3b5eb960e11b60208201526001600160a01b039190911692906108de816108d08486602484016120d6565b03601f19810183528261203d565b833b156107f7578461090c916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af1908115610803578591610973575b5050823b1561096e5761094c92849283604051809681958294632a3b5eb960e11b8452600484016120d6565b03925af180156102b75761095d5750f35b816109679161203d565b6100f25780f35b505050fd5b8161097d9161203d565b61096e57835f610920565b50346100f25760403660031901126100f257806109a3611fa0565b6109ab611fb6565b906109b58161214a565b604051634506d62560e11b60208201526001600160a01b039283166024808301829052825291909216916109ea60448261203d565b823b1561096e5783610a18916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d57849161141d575b5050813b156112485760405190634506d62560e11b82526004820152828160248183865af1908115610710578391611408575b5050604051634796629160e01b6020820152639de732d360e01b602482015262093a806044808301919091528152610a9860648261203d565b813b156112485782610ac6916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916113f3575b5050803b1561126157604051634796629160e01b8152639de732d360e01b600482015262093a806024820152828160448183865af19081156107105783916113de575b5050604051634796629160e01b6020820152634506d62560e11b602482015262093a806044808301919091528152610b5660648261203d565b813b156112485782610b84916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916113c9575b5050803b1561126157604051634796629160e01b8152634506d62560e11b600482015262093a806024820152828160448183865af19081156107105783916113b4575b5050604051634796629160e01b6020820152635c1a1a4f60e01b602482015262093a806044808301919091528152610c1460648261203d565b813b156112485782610c42916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af190811561071057839161139f575b5050803b1561126157604051634796629160e01b8152635c1a1a4f60e01b600482015262093a806024820152828160448183865af190811561071057839161138a575b5050604051634796629160e01b6020820152635b4099e360e01b602482015262093a806044808301919091528152610cd260648261203d565b813b156112485782610d00916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611375575b5050803b1561126157604051634796629160e01b8152635b4099e360e01b600482015262093a806024820152828160448183865af1908115610710578391611360575b5050604051634796629160e01b6020820152632a3b5eb960e11b60248201526203f4806044808301919091528152610d9060648261203d565b813b156112485782610dbe916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af190811561071057839161134b575b5050803b1561126157604051634796629160e01b8152632a3b5eb960e11b60048201526203f4806024820152828160448183865af1908115610710578391611336575b5050604051634796629160e01b60208201526357984beb60e11b60248201526203f4806044808301919091528152610e4e60648261203d565b813b156112485782610e7c916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611321575b5050803b1561126157604051634796629160e01b81526357984beb60e11b60048201526203f4806024820152828160448183865af190811561071057839161130c575b5050604051634796629160e01b60208201526308ccb85360e01b60248201526203f4806044808301919091528152610f0c60648261203d565b813b156112485782610f3a916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112f7575b5050803b1561126157604051634796629160e01b81526308ccb85360e01b60048201526203f4806024820152828160448183865af19081156107105783916112e2575b5050604051634796629160e01b6020820152630b5af0ad60e41b60248201526203f4806044808301919091528152610fca60648261203d565b813b156112485782610ff8916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112cd575b5050803b1561126157604051634796629160e01b8152630b5af0ad60e41b60048201526203f4806024820152828160448183865af19081156107105783916112b8575b5050604051634796629160e01b60208201526343f68a4960e01b602482015262015180604480830191909152815261108860648261203d565b813b1561124857826110b6916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af19081156107105783916112a3575b5050803b1561126157604051634796629160e01b81526343f68a4960e01b6004820152620151806024820152828160448183865af190811561071057839161128e575b5050604051634796629160e01b60208201526386be439760e01b6024820152610e10604480830191909152815261114560648261203d565b813b156112485782611173916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611279575b5050803b1561126157604051634796629160e01b81526386be439760e01b6004820152610e106024820152828160448183865af1908115610710578391611264575b50506001546001600160a01b0316813b15611248576040519063e90956cf60e01b82526004820152828160248183865af190811561071057839161124c575b505081546001600160a01b0316813b1561124857829160248392604051948593849263f2fde38b60e01b845260048401525af180156102b75761095d5750f35b5050fd5b816112569161203d565b61126157815f611208565b50fd5b8161126e9161203d565b61126157815f6111c9565b816112839161203d565b61126157815f611187565b816112989161203d565b61126157815f61110d565b816112ad9161203d565b61126157815f6110ca565b816112c29161203d565b61126157815f61104f565b816112d79161203d565b61126157815f61100c565b816112ec9161203d565b61126157815f610f91565b816113019161203d565b61126157815f610f4e565b816113169161203d565b61126157815f610ed3565b8161132b9161203d565b61126157815f610e90565b816113409161203d565b61126157815f610e15565b816113559161203d565b61126157815f610dd2565b8161136a9161203d565b61126157815f610d57565b8161137f9161203d565b61126157815f610d14565b816113949161203d565b61126157815f610c99565b816113a99161203d565b61126157815f610c56565b816113be9161203d565b61126157815f610bdb565b816113d39161203d565b61126157815f610b98565b816113e89161203d565b61126157815f610b1d565b816113fd9161203d565b61126157815f610ada565b816114129161203d565b61126157815f610a5f565b816114279161203d565b61124857825f610a2c565b50346100f2576101003660031901126100f25761144d611fa0565b6024356001600160a01b03811691908290036106995761146b611fcc565b916064356001600160a01b03811692908390036107f7576084356001600160a01b03811694908590036102b35760a4356001600160a01b038116908190036117bc5760c4356001600160a01b03811691908290036117b8576114cc8461214a565b604051956114d98761200e565b8652602086019687526040860190815260608601918252608086019260e435845260a0808820602460405180948193632c3c915760e01b83526004830152600180861b03165afa9081156117ad578991611724575b505186516001600160a01b039182169116036116df576040516384c1b85360e01b815295516001600160a01b0390811660048801529651871660248701525186166044860152519094166064840152925160848301528392838360a481855afa92831561069d578493611664575b5060018060a01b0316916040516357984beb60e11b6020820152826024820152604060448201526115d4816108d060648201856120f0565b833b156107f75784611602916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af190811561080357859161164f575b5050823b1561096e5761094c928492836040518096819582946357984beb60e11b845260048401526040602484015260448301906120f0565b816116599161203d565b61096e57835f611616565b9092503d8085833e611676818361203d565b8101906020818303126107f7578051906001600160401b0382116102b3570181601f820112156107f7578051906116ac8261205e565b926116ba604051948561203d565b828452602083830101116102b35781869260208093018386015e83010152915f61159c565b60405162461bcd60e51b815260206004820152601b60248201527f4d61726b6574206e6f74206c6973746564206f6e204d6f7270686f00000000006044820152606490fd5b905060a0813d60a0116117a5575b8161173f60a0938361203d565b810103126117a1576080604051916117568361200e565b61175f81612136565b835261176d60208201612136565b602084015261177e60408201612136565b604084015261178f60608201612136565b6060840152015160808201525f61152e565b8880fd5b3d9150611732565b6040513d8b823e3d90fd5b8780fd5b8680fd5b50346100f257806003193601126100f257546040516001600160a01b039091168152602090f35b50346100f25760403660031901126100f25780611802611fa0565b61180a611fb6565b906118148161214a565b604051639de732d360e01b60208201526001600160a01b0392831660248083018290528252919092169161184960448261203d565b823b1561096e5783611877916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d5784916118c0575b5050813b15611248578291602483926040519485938492639de732d360e01b845260048401525af180156102b75761095d5750f35b816118ca9161203d565b61124857825f61188b565b50346100f25760203660031901126100f2576020906001600160a01b036118fa611fa0565b16815260058252604060018060a01b0391205416604051908152f35b50346100f25760403660031901126100f25780611931611fa0565b611939611fb6565b906119438161214a565b6040516386be439760e01b60208201526001600160a01b039283166024820181905260016044808401919091528252919092169161198260648261203d565b823b1561096e57836119b0916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af190811561069d578491611a00575b5050813b156112485782916044839260405194859384926386be439760e01b84526004840152600160248401525af180156102b75761095d5750f35b81611a0a9161203d565b61124857825f6119c4565b50346100f25760603660031901126100f257611a2f611fa0565b611a37611fb6565b90611a418161214a565b6040516001600160a01b03919091169161305180830191906001600160401b03831184841017611b74578392869492611a83926121bc85398660443592612114565b039082f0918215611b6757604051639de732d360e01b60208201526001600160a01b039093166024808501829052845292611abf60448261203d565b813b156106995782611aed916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183865af1908115610710578391611b52575b5050803b156102c2578190602460405180948193639de732d360e01b83528760048401525af1801561071057611b3d575b602082604051908152f35b611b4883809261203d565b6102c25781611b32565b81611b5c9161203d565b6102c257815f611b01565b50604051903d90823e3d90fd5b634e487b7160e01b86526041600452602486fd5b50346100f25760a03660031901126100f257611ba2611fa0565b6024356001600160401b03811161069957611bc1903690600401612079565b906044356001600160401b0381116106bd57611be1903690600401612079565b60048054604051636bee28ed60e11b81526001600160a01b0394851692810183905230602482018190526044820152610140606482015292949193919291602091839116818881611c396101ac61014483018a6120f0565b60643560a483015262093a8060c4830152620d2f0060e483015262093a8061010483015260843561012483015203925af1908115610803578591611d31575b506001600160a01b03908116808652600560205260408620549093911661077f5782855260056020526040852080546001600160a01b03191633179055845b6003548110156102c65785611ccb82611fe2565b905460039190911b1c6001600160a01b0316853b156102c257604051906358c9542560e11b82526004820152600160248201528181604481838a5af180156102b757611d1c575b5050600101611cb7565b81611d269161203d565b6102b357855f611d12565b90506020813d602011611d6b575b81611d4c6020938361203d565b810103126107f757516001600160a01b03811681036107f7575f611c78565b3d9150611d3f565b50346100f25760603660031901126100f25780611d8e611fa0565b611d96611fb6565b611d9e611fcc565b91611da88161214a565b604051630b5af0ad60e41b60208201526001600160a01b03919091169290611dd8816108d08486602484016120d6565b833b156107f75784611e06916040518093819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183885af1908115610803578591611e46575b5050823b1561096e5761094c92849283604051809681958294630b5af0ad60e41b8452600484016120d6565b81611e509161203d565b61096e57835f611e1a565b50346100f25760203660031901126100f257600435906003548210156100f2576020611e8683611fe2565b905460405160039290921b1c6001600160a01b03168152f35b503461064557606036600319011261064557611eb9611fa0565b90611ec2611fb6565b611eca611fcc565b92611ed48161214a565b6040516308ccb85360e01b6020820152936001600160a01b03919091169190611f1385611f058385602484016120d6565b03601f19810187528661203d565b823b15610645575f611f41956040518097819263ef7fa71b60e01b83526020600484015260248301906120f0565b038183875af18015611f9557611f7f575b839450823b1561096e5761094c928492836040518096819582946308ccb85360e01b8452600484016120d6565b9250925f611f8c9161203d565b5f918390611f52565b6040513d5f823e3d90fd5b600435906001600160a01b038216820361064557565b602435906001600160a01b038216820361064557565b604435906001600160a01b038216820361064557565b600354811015611ffa5760035f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b60a081019081106001600160401b0382111761202957604052565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761202957604052565b6001600160401b03811161202957601f01601f191660200190565b81601f82011215610645578035906120908261205e565b9261209e604051948561203d565b8284526020838301011161064557815f926020809301838601378301015290565b61010435906001600160a01b038216820361064557565b6001600160a01b0391821681529116602082015260400190565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b6001600160a01b03918216815291166020820152604081019190915260600190565b51906001600160a01b038216820361064557565b6001600160a01b039081165f9081526005602052604090205416330361216c57565b60405162461bcd60e51b815260206004820152600f60248201526e2737ba103137bc1031b932b0ba37b960891b6044820152606490fd5b9081602091031261064557518015158103610645579056fe60e03461017f57601f61305138819003918201601f19168301916001600160401b038311848410176101835780849260609460405283398101031261017f5761004781610197565b90604061005660208301610197565b910151916001600160a01b03811615610170576080526001600160a01b0316801561017057670de0b6b3a764000082116101615781156101615760a05260c052604051612ea590816101ac823960805181818161035a015281816104560152818161062c015281816109d701528181610a8701528181610b3601528181610db901528181610f230152818161101b0152818161137e0152818161158501526115f8015260a0518181816102f101528181610bdf01528181610e7e015281816110a70152818161123e015281816113e001528181611683015281816117eb01528181611d8401528181612134015281816123c801526129c0015260c051818181610ace0152610c710152f35b632a9ffab760e21b5f5260045ffd5b63e6c4247b60e01b5f5260045ffd5b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361017f5756fe60806040526004361015610010575b005b5f5f3560e01c8063075300a1146117b057806310d967591461177757806311005b071461175457806313c71131146115e4578063172c48c7146115c55780632991889914611569578063351210871461134b57806338034739146113105780633cb82c72146112bb57806346d360aa1461129d5780634766162c146112625780635bc01cb8146111fc5780635bf2adde14611006578063627fa7f814610fdb578063681b317914610f7a5780636b4f41af14610f075780637210ffe114610da557806384c1b85314610cf4578063894db62b14610b22578063896bcd7b14610af15780638d0fd74114610ab65780638da5cb5b14610a7157806394f9059f146109ba578063a1bf284014610996578063a2619ae714610937578063ac9650d81461083a578063b43f82a01461081c578063b4d8144c146105f6578063bc25cf771461043a578063c4d68a1a1461033d578063ca169c3814610320578063d8fbc833146102db578063e158c62414610284578063ea90eb2c14610221578063f48f3b40146101e95763faa0de75146101a357005b346101e65760203660031901126101e657600435906001600160401b0382116101e65760206101de6101d8366004860161180f565b9061211b565b604051908152f35b80fd5b50346101e65760203660031901126101e6576020610208600435612a9b565b905460405160039290921b1c6001600160a01b03168152f35b50346101e657806003193601126101e65760045461023e816120b4565b915b81811061025957604051806102558582611ab6565b0390f35b80610265600192612a9b565b838060a01b0391549060031b1c1661027d8286612107565b5201610240565b50346101e657806003193601126101e65780546102a0816120b4565b915b8181106102b757604051806102558582611af8565b806102c3600192612a6d565b90549060031b1c6102d48286612107565b52016102a2565b50346101e657806003193601126101e6576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101e657806003193601126101e65760209054604051908152f35b50346101e65760203660031901126101e65761035761183c565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361042b5761039182612361565b61041c578054916001600160a01b0316815b8381106103cb57506103b490612d39565b156103bc5780f35b630b094f2760e31b8152600490fd5b6103d481612a6d565b90549060031b1c835260066020528160018060a01b036103fe6103f960408720611a16565b611be2565b51161461040d576001016103a3565b631acba50760e21b8352600483fd5b631acba50760e21b8152600490fd5b635fc483c560e01b8152600490fd5b50346101e65760203660031901126101e65761045461183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316338190036105e75761048f81611d4e565b916001600160a01b0381169081156105ae576040516370a0823160e01b815230600482015290602082602481865afa9182156105a357869261056b575b50811561055c576040519163a9059cbb60e01b87528460045260245260208660448180855af190600187511482161561053d575b50906040521561052b575061051490611d4e565b0361051c5780f35b63eeb3730b60e01b8152600490fd5b635274afe760e01b8452600452602483fd5b6001821516610553573b15153d1516165f610500565b823d88823e3d90fd5b63162908e360e11b8652600486fd5b9091506020813d60201161059b575b816105876020938361192d565b810103126105975751905f6104cc565b5f80fd5b3d915061057a565b6040513d88823e3d90fd5b50504780156105d85783808080938582f1156105cd5761051490611d4e565b6040513d84823e3d90fd5b63162908e360e11b8452600484fd5b635fc483c560e01b8352600483fd5b50346101e65760203660031901126101e6576004356001600160401b0381116108185761062790369060040161180f565b9091907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610809576106686103f9368386611969565b6020818101516001600160a01b03165f90815260039091526040902054156107fa57516001600160a01b03165f90815260056020526040902054156107eb576106b2368285611969565b602081519101206106c281612b83565b156107dc578252600660205260408220906001600160401b0381116107c8576106eb82546119de565b601f811161078d575b5082601f821160011461072d57839482939492610722575b50508160011b915f199060031b1c191617905580f35b013590505f8061070c565b601f198216948385526020852091855b87811061077557508360019596971061075c575b505050811b01905580f35b01355f19600384901b60f8161c191690555f8080610751565b9092602060018192868601358155019401910161073d565b6107b89083855260208520601f840160051c810191602085106107be575b601f0160051c0190611b91565b5f6106f4565b90915081906107ab565b634e487b7160e01b83526041600452602483fd5b63b73e95e160e01b8352600483fd5b6307c241ad60e51b8252600482fd5b6307c241ad60e51b8352600483fd5b635fc483c560e01b8252600482fd5b5080fd5b50346101e657806003193601126101e6576020600254604051908152f35b50346101e65760203660031901126101e6576004356001600160401b0381116108185736602382011215610818578181600401356001600160401b038111610818573660248260051b85010111610818579160421981360301915b83811015610933578460248260051b84010135848112156108185783016024810135906001600160401b03821161092f5760440190803603821361092f57829181604051928392833781018381520390305af43d15610927573d906108f98261194e565b91610907604051938461192d565b82523d87602084013e5b1561091f5750600101610895565b805190602001fd5b606090610911565b8280fd5b8480f35b50346101e657806003193601126101e657600254610954816120b4565b915b81811061096b57604051806102558582611ab6565b80610977600192612a83565b838060a01b0391549060031b1c1661098f8286612107565b5201610956565b50346101e65760203660031901126101e65760206101de6109b561183c565b612953565b50346101e65760203660031901126101e6576109d461183c565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361042b57610a0e82612953565b61041c578054916001600160a01b0316815b838110610a3157506103b490612c8e565b610a3a81612a6d565b90549060031b1c835260066020528160018060a01b036020610a616103f960408820611a16565b0151161461040d57600101610a20565b50346101e657806003193601126101e6576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346101e657806003193601126101e65760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b50346101e65760203660031901126101e657600435906001600160a01b03821682036101e65760206101de83611d4e565b50346101e657610b31366118b8565b9092907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169033829003610ce557610b8d610b75368686611969565b602081519101205f52600160205260405f2054151590565b15610cd6576001600160a01b0385165f9081526003602052604090205415610cc757908591610bdd610bc36103f9368888611969565b602081015190976001600160a01b03908116911614611b31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610cc3576101048492836040519586948593638720316d60e01b8552610c358d600487019061199f565b60a48501523060c485015260e48401525af180156105cd57610cae575b50506080610c6c670de0b6b3a764000093610c969361211b565b9301517f000000000000000000000000000000000000000000000000000000000000000090611ba7565b0410610c9f5780f35b635d386c8960e11b8152600490fd5b81610cb89161192d565b610cc357835f610c52565b8380fd5b6307c241ad60e51b8652600486fd5b630b094f2760e31b8652600486fd5b635fc483c560e01b8652600486fd5b50346101e65760a03660031901126101e657604051610d12816118fe565b610d1a61183c565b81526024356001600160a01b038116810361092f5760208201526044356001600160a01b038116810361092f5760408201526064356001600160a01b038116810361092f57610255925060608201526084356080820152610d8260405191602083019061199f565b60a08152610d9160c08261192d565b604051918291602083526020830190611894565b50346101e657610db4366118b8565b9092907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633819003610ce557610df7610b75368686611969565b15610cd6576001600160a01b0385165f9081526005602052604090205415610cc757610e41610e2a6103f9368787611969565b805190966001600160a01b03908116911614611c87565b6040516350d8cd4b60e01b815291610e5c600484018761199f565b60a483015260c482018690523060e483015261010482015260408161012481887f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af18015610efc57670de0b6b3a764000093610c9693608093610c6c93610ece575b5061211b565b610eef9060403d604011610ef5575b610ee7818361192d565b810190611ce1565b50610ec8565b503d610edd565b6040513d87823e3d90fd5b50346101e65760203660031901126101e657610f2161183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361080957610f63906001600160a01b0316612b33565b15610f6b5780f35b63b73e95e160e01b8152600490fd5b50346101e65760203660031901126101e657600435906001600160401b0382116101e6576020610fd1610fba610fb3366004870161180f565b3691611969565b8281519101205f52600160205260405f2054151590565b6040519015158152f35b50346101e65760203660031901126101e657610d916040610255926004358152600660205220611a16565b50346101e657611015366118b8565b909290917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036111ed57611057610b75368385611969565b156111de576001600160a01b0384165f90815260056020526040902054156111cf576103f98592611089923691611969565b80519092906110a5906001600160a01b03868116911614611c87565b7f0000000000000000000000000000000000000000000000000000000000000000906110d230858461253e565b6001600160a01b038316956110ea90839088906123f3565b810361117557506040926111279261110b61014493309060a08520906128f5565b95855196879586946320b76e8160e01b8652600486019061199f565b8260a485015260c48401523060e4840152610120610104840152816101248401525af180156105cd57611158575080f35b6111709060403d604011610ef557610ee7818361192d565b505080f35b9361014491509261119e92604094855196879586946320b76e8160e01b8652600486019061199f565b60a48401528160c48401523060e4840152610120610104840152816101248401525af180156105cd57611158575080f35b6307c241ad60e51b8552600485fd5b630b094f2760e31b8552600485fd5b635fc483c560e01b8552600485fd5b50346101e65760206101de6103f961123a61122361121936611852565b9491903691611969565b805190926001600160a01b03908116911614611c87565b30907f000000000000000000000000000000000000000000000000000000000000000061253e565b50346101e65760203660031901126101e6576020610fd161128161183c565b6001600160a01b03165f90815260036020526040902054151590565b50346101e657806003193601126101e6576020600454604051908152f35b50346101e65760203660031901126101e657600435906001600160401b0382116101e657366023830112156101e65760a06113016103f936600486013560248701611969565b61130e604051809261199f565bf35b50346101e65760203660031901126101e6576020610fd161132f61183c565b6001600160a01b03165f90815260056020526040902054151590565b50346101e65760203660031901126101e6576004356001600160401b0381116108185761137c90369060040161180f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036105e75760a06113bc6103f9368486611969565b206040516349e2903160e11b815260048101919091523060248201526060816044817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561155e5784916114d6575b5060408101516001600160801b031615801591906114bf575b5061040d5790611442913691611969565b6020815191012061145281612bcf565b156114b05781526006602052806040812061146d81546119de565b80611476575050f35b601f811160011461148657505580f35b818352602083206114a291601f0160051c810190600101611b91565b808252816020812091555580f35b630b094f2760e31b8252600482fd5b602001516001600160801b0316151590505f611431565b90506060813d606011611556575b816114f16060938361192d565b81010312610cc35760405190606082018281106001600160401b03821117611542576115379160409182528051845261152c6020820161252a565b60208501520161252a565b60408201525f611418565b634e487b7160e01b86526041600452602486fd5b3d91506114e4565b6040513d86823e3d90fd5b50346101e65760203660031901126101e65761158361183c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361080957610f63906001600160a01b0316612ac8565b50346101e65760203660031901126101e6576020610208600435612a83565b5034610597576115f3366118b8565b9290917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361174557611634610b75368385611969565b15611736576001600160a01b0383165f908152600360205260409020541561172757611665916103f9913691611969565b6020810151611681906001600160a01b03848116911614611b31565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316916116b990849084906123f3565b813b15610597576101246116e6915f8094604051968795869463238d657960e01b8652600486019061199f565b60a48401523060c484015261010060e4840152816101048401525af1801561171c57611710575080f35b61000e91505f9061192d565b6040513d5f823e3d90fd5b6307c241ad60e51b5f5260045ffd5b630b094f2760e31b5f5260045ffd5b635fc483c560e01b5f5260045ffd5b346105975760203660031901126105975760206101de61177261183c565b612361565b3461059757602036600319011261059757611793600435612a6d565b90549060031b1c5f526006602052610255610d9160405f20611a16565b346105975760206101de60a06103f96117e66117ce61121936611852565b8581015190925f196001861b01908116911614611b31565b2030907f00000000000000000000000000000000000000000000000000000000000000006122e2565b9181601f84011215610597578235916001600160401b038311610597576020838186019501011161059757565b600435906001600160a01b038216820361059757565b604060031982011261059757600435906001600160401b0382116105975761187c9160040161180f565b90916024356001600160a01b03811681036105975790565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b606060031982011261059757600435906001600160401b038211610597576118e29160040161180f565b90916024356001600160a01b0381168103610597579060443590565b60a081019081106001600160401b0382111761191957604052565b634e487b7160e01b5f52604160045260245ffd5b90601f801991011681019081106001600160401b0382111761191957604052565b6001600160401b03811161191957601f01601f191660200190565b9291926119758261194e565b91611983604051938461192d565b829481845281830111610597578281602093845f960137010152565b80516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015190911690830152608090810151910152565b90600182811c92168015611a0c575b60208310146119f857565b634e487b7160e01b5f52602260045260245ffd5b91607f16916119ed565b9060405191825f825492611a29846119de565b8084529360018116908115611a945750600114611a50575b50611a4e9250038361192d565b565b90505f9291925260205f20905f915b818310611a78575050906020611a4e928201015f611a41565b6020919350806001915483858901015201910190918492611a5f565b905060209250611a4e94915060ff191682840152151560051b8201015f611a41565b60206040818301928281528451809452019201905f5b818110611ad95750505090565b82516001600160a01b0316845260209384019390920191600101611acc565b60206040818301928281528451809452019201905f5b818110611b1b5750505090565b8251845260209384019390920191600101611b0e565b15611b3857565b60405162461bcd60e51b815260206004820152602b60248201527f46756e64696e674d6f64756c654d6f7270686f3a2057726f6e6720636f6c6c6160448201526a3a32b930b6103a37b5b2b760a91b6064820152608490fd5b818110611b9c575050565b5f8155600101611b91565b81810292918115918404141715611bba57565b634e487b7160e01b5f52601160045260245ffd5b51906001600160a01b038216820361059757565b5f6080604051611bf1816118fe565b828152826020820152826040820152826060820152015260a0815103611c785760a0818051810103126105975760a060405191611c2d836118fe565b611c3960208201611bce565b8352611c4760408201611bce565b6020840152611c5860608201611bce565b6040840152611c6960808201611bce565b60608401520151608082015290565b632eb79b5360e21b5f5260045ffd5b15611c8e57565b60405162461bcd60e51b815260206004820152602560248201527f46756e64696e674d6f64756c654d6f7270686f3a2057726f6e672064656274206044820152643a37b5b2b760d91b6064820152608490fd5b9190826040910312610597576020825192015190565b8115611d01570490565b634e487b7160e01b5f52601260045260245ffd5b9081602091031261059757516001600160a01b03811681036105975790565b91908201809211611bba57565b91908203918211611bba57565b6040516338d52e0f60e01b81525f916001600160a01b0316602082600481845afa91821561171c575f92612061575b505f545f927f0000000000000000000000000000000000000000000000000000000000000000925b828510611db457505050505090565b90919293945f90611dc487612a6d565b90549060031b1c5f526006602052611de16103f960405f20611a16565b611def3060a08320896122e2565b80156120555760208201516001600160a01b038781169591169190858303611f7a57611e1b9250611d34565b925b611e2830838a61253e565b91516001600160a01b0316908103611e6e5750600192611e5a929180821115611e6657611e5491611d41565b90611d34565b955b0193929190611da5565b50505f611e54565b906040519163addd509960e01b83526004830152602082602481885afa91821561171c576004926020915f91611f4d575b5060405163501ad8ff60e11b815293849182906001600160a01b03165afa90811561171c575f91611f18575b611ed59250611ba7565b906ec097ce7bc90715b34b9f0fffffffff8201809211611bba576001926a0c097ce7bc90715b34b9f160241b611e5a93048082115f14611e6657611e5491611d41565b90506020823d8211611f45575b81611f326020938361192d565b8101031261059757611ed5915190611ecb565b3d9150611f25565b611f6d9150823d8111611f73575b611f65818361192d565b810190611d15565b5f611e9f565b503d611f5b565b50906040519063addd509960e01b82526004820152602081602481895afa90811561171c576004916020915f91612038575b5060405163501ad8ff60e11b815292839182906001600160a01b03165afa90811561171c575f91611ff9575b50611ff2906a0c097ce7bc90715b34b9f160241b92611ba7565b0492611e1d565b90506020813d8211612030575b816120136020938361192d565b8101031261059757516a0c097ce7bc90715b34b9f160241b611fd8565b3d9150612006565b61204f9150823d8111611f7357611f65818361192d565b5f611fac565b50509560019150611e5c565b9091506020813d602011612095575b8161207d6020938361192d565b810103126105975761208e90611bce565b905f611d7d565b3d9150612070565b6001600160401b0381116119195760051b60200190565b906120be8261209d565b6120cb604051918261192d565b82815280926120dc601f199161209d565b0190602036910137565b8051156120f35760200190565b634e487b7160e01b5f52603260045260245ffd5b80518210156120f35760209160051b010190565b6103f99061212a923691611969565b60a08120906121667f000000000000000000000000000000000000000000000000000000000000000061215e30848361253e565b9330916122e2565b9081156122625760400180516001600160a01b031615612253575160405163501ad8ff60e11b815290602090829060049082906001600160a01b03165afa90811561171c575f91612213575b506121cc906a0c097ce7bc90715b34b9f160241b92611ba7565b04806121d85750505f90565b670de0b6b3a764000091828102928184041490151715611bba575f19810191818311611bba576122109261220b91611d34565b611cf7565b90565b90506020813d60201161224b575b8161222e6020938361192d565b8101031261059757516a0c097ce7bc90715b34b9f160241b6121b2565b3d9150612221565b630bd226f160e31b5f5260045ffd5b5050505f90565b602081830312610597578051906001600160401b03821161059757019080601f8301121561059757815161229c8161209d565b926122aa604051948561192d565b81845260208085019260051b82010192831161059757602001905b8282106122d25750505090565b81518152602091820191016122c5565b6122f86122f3612314945f946129eb565b612a46565b906040518080958194637784c68560e01b835260048301611af8565b03916001600160a01b03165afa801561171c57612338915f9161233f575b506120e6565b5160801c90565b61235b91503d805f833e612353818361192d565b810190612269565b5f612332565b905f915f54905f5b82811061237557505050565b61237e81612a6d565b90549060031b1c5f52600660205261239b6103f960405f20611a16565b80516001600160a01b038481169116146123b9575b50600101612369565b60019195611e546123ec9230907f000000000000000000000000000000000000000000000000000000000000000061253e565b94906123b0565b916040519163095ea7b360e01b5f5260018060a01b031691826004528160245260205f60448180885af19060015f511482161561251b575b6040521561243857505050565b60405163095ea7b360e01b5f52826004525f60245260205f60448180885af19060015f5114821615612503575b604052156124e5576040519163095ea7b360e01b5f5260045260245260205f60448180865af19060015f51148216156124c4575b604052156124a45750565b635274afe760e01b5f9081526001600160a01b0391909116600452602490fd5b9060018115166124dc57823b15153d15161690612499565b503d5f823e3d90fd5b635274afe760e01b5f9081526001600160a01b038416600452602490fd5b9060018115166124dc57843b15153d15161690612465565b90843b15153d1516169061242b565b51906001600160801b038216820361059757565b9161254d9060a08320846128f5565b9060c060a08220602460405180968193632e3071cd60e11b8352600483015260018060a01b03165afa92831561171c575f93612844575b5060808301805161259e906001600160801b031642611d41565b8015158061282e575b80612818575b61260b575b50505060408201516060909201516001600160801b03908116921660018101908110611bba57620f4240830191828411611bba57620f423f916125f491611ba7565b920191818311611bba576122109261220b91611d34565b6060830151604051638c00bf6b60e01b8152936001600160a01b03909116919061263990600486019061199f565b85516001600160801b0390811660a486015260208088018051831660c4880152604089018051841660e489015260608a015184166101048901529551831661012488015260a08901805190931661014488015295919382906101649082905afa90811561171c575f916127de575b50612708906127026126cc670de0b6b3a76400009460018060801b0388511693611ba7565b6126fd671bc16d674ec800006126e28380611ba7565b046729a2241af62c00006126f68483611ba7565b0492611d34565b611d34565b90611ba7565b04916001600160801b0361272e61271e85612dfd565b83516001600160801b0316612de4565b1690526001600160801b0361275561274584612dfd565b87516001600160801b0316612de4565b168552516001600160801b03169081156125b257670de0b6b3a76400009161277c91611ba7565b046127908160018060801b03865116611d41565b60018060801b0383511690620f42408201809211611bba5760018101809111611bba576127ce61271e9161220b6127d39460018060801b0396611ba7565b612dfd565b1690525f80806125b2565b90506020813d602011612810575b816127f96020938361192d565b810103126105975751670de0b6b3a76400006126a7565b3d91506127ec565b5060608301516001600160a01b031615156125ad565b5060408501516001600160801b031615156125a7565b90925060c0813d60c0116128ed575b8161286060c0938361192d565b81010312610597576040519060c082018281106001600160401b03821117611919576128e19160a0916040526128958161252a565b84526128a36020820161252a565b60208501526128b46040820161252a565b60408501526128c56060820161252a565b60608501526128d66080820161252a565b60808501520161252a565b60a0820152915f612584565b3d9150612853565b6122f86122f3612906945f946129eb565b03916001600160a01b03165afa90811561171c575f91612939575b506001600160801b0390612934906120e6565b511690565b61294d91503d805f833e612353818361192d565b5f612921565b905f915f54905f5b82811061296757505050565b61297081612a6d565b90549060031b1c5f52600660205261298d6103f960405f20611a16565b60208101516001600160a01b038481169116146129ae575b5060010161295b565b60019195611e5460a06129e4932030907f00000000000000000000000000000000000000000000000000000000000000006122e2565b94906129a5565b604051602081019182526002604082015260408152612a0b60608261192d565b51902060405190602082019260018060a01b03168352604082015260408152612a3560608261192d565b51902060018101809111611bba5790565b60405190612a5560408361192d565b6001825260203681840137612a69826120e6565b5290565b5f548110156120f3575f805260205f2001905f90565b6002548110156120f35760025f5260205f2001905f90565b6004548110156120f35760045f5260205f2001905f90565b80548210156120f3575f5260205f2001905f90565b805f52600360205260405f2054155f14612b2e57600254600160401b81101561191957612b17612b018260018594016002556002612ab3565b819391549060031b91821b915f19901b19161790565b9055600254905f52600360205260405f2055600190565b505f90565b805f52600560205260405f2054155f14612b2e57600454600160401b81101561191957612b6c612b018260018594016004556004612ab3565b9055600454905f52600560205260405f2055600190565b805f52600160205260405f2054155f14612b2e575f54600160401b81101561191957612bb9612b018260018594015f555f612ab3565b90555f54905f52600160205260405f2055600190565b5f818152600160205260409020548015612c88575f198101818111611bba575f545f19810191908211611bba57818103612c52575b5050505f548015612c3e575f1901612c1c815f612ab3565b8154905f199060031b1b191690555f555f5260016020525f6040812055600190565b634e487b7160e01b5f52603160045260245ffd5b612c72612c62612b01935f612ab3565b90549060031b1c9283925f612ab3565b90555f52600160205260405f20555f8080612c04565b50505f90565b5f818152600360205260409020548015612c88575f198101818111611bba576002545f19810191908211611bba57818103612d01575b5050506002548015612c3e575f1901612cde816002612ab3565b8154905f199060031b1b191690556002555f5260036020525f6040812055600190565b612d23612d12612b01936002612ab3565b90549060031b1c9283926002612ab3565b90555f52600360205260405f20555f8080612cc4565b5f818152600560205260409020548015612c88575f198101818111611bba576004545f19810191908211611bba57818103612dac575b5050506004548015612c3e575f1901612d89816004612ab3565b8154905f199060031b1b191690556004555f5260056020525f6040812055600190565b612dce612dbd612b01936004612ab3565b90549060031b1c9283926004612ab3565b90555f52600560205260405f20555f8080612d6f565b6001600160801b039182169082160191908211611bba57565b604051612e0b60408261192d565b60148152731b585e081d5a5b9d0c4c8e08195e18d95959195960621b60208201526001600160801b038211612e4757506001600160801b031690565b60405162461bcd60e51b815260206004820152908190612e6b906024830190611894565b0390fdfea2646970667358221220c753b8f190b70487d835ac3dee9447b309017863a17dc353ba7e5445107cac4664736f6c634300081c0033a26469706673582212205995eb4c93036d1c26d86543f6398941f1fd6cc69d91eab02d28eadf0b3d7f2264736f6c634300081c0033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.