Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 827 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Set Flow Caps | 21713922 | 7 hrs ago | IN | 0 ETH | 0.0002947 | ||||
Set Flow Caps | 21713919 | 7 hrs ago | IN | 0 ETH | 0.00027272 | ||||
Set Flow Caps | 21706759 | 31 hrs ago | IN | 0 ETH | 0.0001343 | ||||
Set Flow Caps | 21706758 | 31 hrs ago | IN | 0 ETH | 0.00012185 | ||||
Set Flow Caps | 21706757 | 31 hrs ago | IN | 0 ETH | 0.00012793 | ||||
Set Flow Caps | 21706756 | 31 hrs ago | IN | 0 ETH | 0.00012936 | ||||
Set Flow Caps | 21706755 | 31 hrs ago | IN | 0 ETH | 0.00012939 | ||||
Set Flow Caps | 21706751 | 31 hrs ago | IN | 0 ETH | 0.00012507 | ||||
Set Flow Caps | 21699584 | 2 days ago | IN | 0 ETH | 0.00024023 | ||||
Set Flow Caps | 21699583 | 2 days ago | IN | 0 ETH | 0.00025674 | ||||
Set Flow Caps | 21692421 | 3 days ago | IN | 0 ETH | 0.00025566 | ||||
Set Flow Caps | 21692420 | 3 days ago | IN | 0 ETH | 0.0002364 | ||||
Set Flow Caps | 21688340 | 3 days ago | IN | 0 ETH | 0.00135971 | ||||
Set Flow Caps | 21688337 | 3 days ago | IN | 0 ETH | 0.00142361 | ||||
Set Flow Caps | 21688337 | 3 days ago | IN | 0 ETH | 0.00142656 | ||||
Set Flow Caps | 21688336 | 3 days ago | IN | 0 ETH | 0.00130354 | ||||
Set Flow Caps | 21688329 | 3 days ago | IN | 0 ETH | 0.00169972 | ||||
Set Flow Caps | 21685254 | 4 days ago | IN | 0 ETH | 0.00022648 | ||||
Set Flow Caps | 21685253 | 4 days ago | IN | 0 ETH | 0.00023255 | ||||
Set Flow Caps | 21685252 | 4 days ago | IN | 0 ETH | 0.00023349 | ||||
Set Flow Caps | 21685251 | 4 days ago | IN | 0 ETH | 0.00022321 | ||||
Set Flow Caps | 21680597 | 4 days ago | IN | 0 ETH | 0.00073091 | ||||
Set Flow Caps | 21680596 | 4 days ago | IN | 0 ETH | 0.00073548 | ||||
Set Flow Caps | 21680595 | 4 days ago | IN | 0 ETH | 0.00077127 | ||||
Set Flow Caps | 21680594 | 4 days ago | IN | 0 ETH | 0.00074932 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
21057034 | 92 days ago | 0.023 ETH | ||||
20958543 | 105 days ago | 0.0005 ETH | ||||
20957724 | 105 days ago | 0.0005 ETH | ||||
20956600 | 106 days ago | 0.0005 ETH | ||||
20954476 | 106 days ago | 0.0005 ETH | ||||
20951933 | 106 days ago | 0.0005 ETH | ||||
20951897 | 106 days ago | 0.0005 ETH | ||||
20951844 | 106 days ago | 0.0005 ETH | ||||
20951825 | 106 days ago | 0.0005 ETH | ||||
20951802 | 106 days ago | 0.0005 ETH | ||||
20874135 | 117 days ago | 0.0005 ETH | ||||
20858405 | 119 days ago | 0.0005 ETH | ||||
20820553 | 125 days ago | 0.0005 ETH | ||||
20819797 | 125 days ago | 0.0005 ETH | ||||
20817126 | 125 days ago | 0.0005 ETH | ||||
20816361 | 125 days ago | 0.0005 ETH | ||||
20815521 | 125 days ago | 0.0005 ETH | ||||
20813912 | 125 days ago | 0.0005 ETH | ||||
20809265 | 126 days ago | 0.0005 ETH | ||||
20808145 | 126 days ago | 0.0005 ETH | ||||
20806068 | 127 days ago | 0.0005 ETH | ||||
20803689 | 127 days ago | 0.0005 ETH | ||||
20802724 | 127 days ago | 0.0005 ETH | ||||
20801491 | 127 days ago | 0.0005 ETH | ||||
20764048 | 132 days ago | 0.0005 ETH |
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
PublicAllocator
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 999999 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.24; import { FlowCaps, FlowCapsConfig, Withdrawal, MAX_SETTABLE_FLOW_CAP, IPublicAllocatorStaticTyping, IPublicAllocatorBase } from "./interfaces/IPublicAllocator.sol"; import { Id, IMorpho, IMetaMorpho, MarketAllocation, MarketParams } from "../lib/metamorpho/src/interfaces/IMetaMorpho.sol"; import {Market} from "../lib/metamorpho/lib/morpho-blue/src/interfaces/IMorpho.sol"; import {ErrorsLib} from "./libraries/ErrorsLib.sol"; import {EventsLib} from "./libraries/EventsLib.sol"; import {UtilsLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/UtilsLib.sol"; import {MarketParamsLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/MarketParamsLib.sol"; import {MorphoBalancesLib} from "../lib/metamorpho/lib/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol"; /// @title PublicAllocator /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Publicly callable allocator for MetaMorpho vaults. contract PublicAllocator is IPublicAllocatorStaticTyping { using MorphoBalancesLib for IMorpho; using MarketParamsLib for MarketParams; using UtilsLib for uint256; /* CONSTANTS */ /// @inheritdoc IPublicAllocatorBase IMorpho public immutable MORPHO; /* STORAGE */ /// @inheritdoc IPublicAllocatorBase mapping(address => address) public admin; /// @inheritdoc IPublicAllocatorBase mapping(address => uint256) public fee; /// @inheritdoc IPublicAllocatorBase mapping(address => uint256) public accruedFee; /// @inheritdoc IPublicAllocatorStaticTyping mapping(address => mapping(Id => FlowCaps)) public flowCaps; /* MODIFIER */ /// @dev Reverts if the caller is not the admin nor the owner of this vault. modifier onlyAdminOrVaultOwner(address vault) { if (msg.sender != admin[vault] && msg.sender != IMetaMorpho(vault).owner()) { revert ErrorsLib.NotAdminNorVaultOwner(); } _; } /* CONSTRUCTOR */ /// @dev Initializes the contract. constructor(address morpho) { MORPHO = IMorpho(morpho); } /* ADMIN OR VAULT OWNER ONLY */ /// @inheritdoc IPublicAllocatorBase function setAdmin(address vault, address newAdmin) external onlyAdminOrVaultOwner(vault) { if (admin[vault] == newAdmin) revert ErrorsLib.AlreadySet(); admin[vault] = newAdmin; emit EventsLib.SetAdmin(msg.sender, vault, newAdmin); } /// @inheritdoc IPublicAllocatorBase function setFee(address vault, uint256 newFee) external onlyAdminOrVaultOwner(vault) { if (fee[vault] == newFee) revert ErrorsLib.AlreadySet(); fee[vault] = newFee; emit EventsLib.SetFee(msg.sender, vault, newFee); } /// @inheritdoc IPublicAllocatorBase function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external onlyAdminOrVaultOwner(vault) { for (uint256 i = 0; i < config.length; i++) { Id id = config[i].id; if (!IMetaMorpho(vault).config(id).enabled && (config[i].caps.maxIn > 0 || config[i].caps.maxOut > 0)) { revert ErrorsLib.MarketNotEnabled(id); } if (config[i].caps.maxIn > MAX_SETTABLE_FLOW_CAP || config[i].caps.maxOut > MAX_SETTABLE_FLOW_CAP) { revert ErrorsLib.MaxSettableFlowCapExceeded(); } flowCaps[vault][id] = config[i].caps; } emit EventsLib.SetFlowCaps(msg.sender, vault, config); } /// @inheritdoc IPublicAllocatorBase function transferFee(address vault, address payable feeRecipient) external onlyAdminOrVaultOwner(vault) { uint256 claimed = accruedFee[vault]; accruedFee[vault] = 0; feeRecipient.transfer(claimed); emit EventsLib.TransferFee(msg.sender, vault, claimed, feeRecipient); } /* PUBLIC */ /// @inheritdoc IPublicAllocatorBase function reallocateTo(address vault, Withdrawal[] calldata withdrawals, MarketParams calldata supplyMarketParams) external payable { if (msg.value != fee[vault]) revert ErrorsLib.IncorrectFee(); if (msg.value > 0) accruedFee[vault] += msg.value; if (withdrawals.length == 0) revert ErrorsLib.EmptyWithdrawals(); Id supplyMarketId = supplyMarketParams.id(); if (!IMetaMorpho(vault).config(supplyMarketId).enabled) revert ErrorsLib.MarketNotEnabled(supplyMarketId); MarketAllocation[] memory allocations = new MarketAllocation[](withdrawals.length + 1); uint128 totalWithdrawn; Id id; Id prevId; for (uint256 i = 0; i < withdrawals.length; i++) { prevId = id; id = withdrawals[i].marketParams.id(); if (!IMetaMorpho(vault).config(id).enabled) revert ErrorsLib.MarketNotEnabled(id); uint128 withdrawnAssets = withdrawals[i].amount; if (withdrawnAssets == 0) revert ErrorsLib.WithdrawZero(id); if (Id.unwrap(id) <= Id.unwrap(prevId)) revert ErrorsLib.InconsistentWithdrawals(); if (Id.unwrap(id) == Id.unwrap(supplyMarketId)) revert ErrorsLib.DepositMarketInWithdrawals(); MORPHO.accrueInterest(withdrawals[i].marketParams); uint256 assets = MORPHO.expectedSupplyAssets(withdrawals[i].marketParams, address(vault)); if (flowCaps[vault][id].maxOut < withdrawnAssets) revert ErrorsLib.MaxOutflowExceeded(id); if (assets < withdrawnAssets) revert ErrorsLib.NotEnoughSupply(id); flowCaps[vault][id].maxIn += withdrawnAssets; flowCaps[vault][id].maxOut -= withdrawnAssets; allocations[i].marketParams = withdrawals[i].marketParams; allocations[i].assets = assets - withdrawnAssets; totalWithdrawn += withdrawnAssets; emit EventsLib.PublicWithdrawal(msg.sender, vault, id, withdrawnAssets); } if (flowCaps[vault][supplyMarketId].maxIn < totalWithdrawn) revert ErrorsLib.MaxInflowExceeded(supplyMarketId); flowCaps[vault][supplyMarketId].maxIn -= totalWithdrawn; flowCaps[vault][supplyMarketId].maxOut += totalWithdrawn; allocations[withdrawals.length].marketParams = supplyMarketParams; allocations[withdrawals.length].assets = type(uint256).max; IMetaMorpho(vault).reallocate(allocations); emit EventsLib.PublicReallocateTo(msg.sender, vault, supplyMarketId, totalWithdrawn); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import { IMetaMorpho, IMorpho, MarketAllocation, Id, MarketParams } from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol"; /// @dev Max settable flow cap, such that caps can always be stored on 128 bits. /// @dev The actual max possible flow cap is type(uint128).max-1. /// @dev Equals to 170141183460469231731687303715884105727; uint128 constant MAX_SETTABLE_FLOW_CAP = type(uint128).max / 2; struct FlowCaps { /// @notice The maximum allowed inflow in a market. uint128 maxIn; /// @notice The maximum allowed outflow in a market. uint128 maxOut; } struct FlowCapsConfig { /// @notice Market for which to change flow caps. Id id; /// @notice New flow caps for this market. FlowCaps caps; } struct Withdrawal { /// @notice The market from which to withdraw. MarketParams marketParams; /// @notice The amount to withdraw. uint128 amount; } /// @dev This interface is used for factorizing IPublicAllocatorStaticTyping and IPublicAllocator. /// @dev Consider using the IPublicAllocator interface instead of this one. interface IPublicAllocatorBase { /// @notice The Morpho contract. function MORPHO() external view returns (IMorpho); /// @notice The admin for a given vault. function admin(address vault) external view returns (address); /// @notice The current ETH fee for a given vault. function fee(address vault) external view returns (uint256); /// @notice The accrued ETH fee for a given vault. function accruedFee(address vault) external view returns (uint256); /// @notice Reallocates from a list of markets to one market. /// @param vault The MetaMorpho vault to reallocate. /// @param withdrawals The markets to withdraw from,and the amounts to withdraw. /// @param supplyMarketParams The market receiving total withdrawn to. /// @dev Will call MetaMorpho's `reallocate`. /// @dev Checks that the flow caps are respected. /// @dev Will revert when `withdrawals` contains a duplicate or is not sorted. /// @dev Will revert if `withdrawals` contains the supply market. /// @dev Will revert if a withdrawal amount is larger than available liquidity. function reallocateTo(address vault, Withdrawal[] calldata withdrawals, MarketParams calldata supplyMarketParams) external payable; /// @notice Sets the admin for a given vault. function setAdmin(address vault, address newAdmin) external; /// @notice Sets the fee for a given vault. function setFee(address vault, uint256 newFee) external; /// @notice Transfers the current balance to `feeRecipient` for a given vault. function transferFee(address vault, address payable feeRecipient) external; /// @notice Sets the maximum inflow and outflow through public allocation for some markets for a given vault. /// @dev Max allowed inflow/outflow is MAX_SETTABLE_FLOW_CAP. /// @dev Doesn't revert if it doesn't change the storage at all. function setFlowCaps(address vault, FlowCapsConfig[] calldata config) external; } /// @dev This interface is inherited by PublicAllocator so that function signatures are checked by the compiler. /// @dev Consider using the IPublicAllocator interface instead of this one. interface IPublicAllocatorStaticTyping is IPublicAllocatorBase { /// @notice Returns (maximum inflow, maximum outflow) through public allocation of a given market for a given vault. function flowCaps(address vault, Id) external view returns (uint128, uint128); } /// @title IPublicAllocator /// @author Morpho Labs /// @custom:contact [email protected] /// @dev Use this interface for PublicAllocator to have access to all the functions with the appropriate function /// signatures. interface IPublicAllocator is IPublicAllocatorBase { /// @notice Returns the maximum inflow and maximum outflow through public allocation of a given market for a given /// vault. function flowCaps(address vault, Id) external view returns (FlowCaps memory); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; import {IMorpho, Id, MarketParams} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; import {IERC4626} from "../../lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol"; import {IERC20Permit} from "../../lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {MarketConfig, PendingUint192, PendingAddress} from "../libraries/PendingLib.sol"; struct MarketAllocation { /// @notice The market to allocate. MarketParams marketParams; /// @notice The amount of assets to allocate. uint256 assets; } interface IMulticall { function multicall(bytes[] calldata) external returns (bytes[] memory); } interface IOwnable { function owner() external view returns (address); function transferOwnership(address) external; function renounceOwnership() external; function acceptOwnership() external; function pendingOwner() external view returns (address); } /// @dev This interface is used for factorizing IMetaMorphoStaticTyping and IMetaMorpho. /// @dev Consider using the IMetaMorpho interface instead of this one. interface IMetaMorphoBase { /// @notice The address of the Morpho contract. function MORPHO() external view returns (IMorpho); function DECIMALS_OFFSET() external view returns (uint8); /// @notice The address of the curator. function curator() external view returns (address); /// @notice Stores whether an address is an allocator or not. function isAllocator(address target) external view returns (bool); /// @notice The current guardian. Can be set even without the timelock set. function guardian() external view returns (address); /// @notice The current fee. function fee() external view returns (uint96); /// @notice The fee recipient. function feeRecipient() external view returns (address); /// @notice The skim recipient. function skimRecipient() external view returns (address); /// @notice The current timelock. function timelock() external view returns (uint256); /// @dev Stores the order of markets on which liquidity is supplied upon deposit. /// @dev Can contain any market. A market is skipped as soon as its supply cap is reached. function supplyQueue(uint256) external view returns (Id); /// @notice Returns the length of the supply queue. function supplyQueueLength() external view returns (uint256); /// @dev Stores the order of markets from which liquidity is withdrawn upon withdrawal. /// @dev Always contain all non-zero cap markets as well as all markets on which the vault supplies liquidity, /// without duplicate. function withdrawQueue(uint256) external view returns (Id); /// @notice Returns the length of the withdraw queue. function withdrawQueueLength() external view returns (uint256); /// @notice Stores the total assets managed by this vault when the fee was last accrued. /// @dev May be greater than `totalAssets()` due to removal of markets with non-zero supply or socialized bad debt. /// This difference will decrease the fee accrued until one of the functions updating `lastTotalAssets` is /// triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient). function lastTotalAssets() external view returns (uint256); /// @notice Submits a `newTimelock`. /// @dev Warning: Reverts if a timelock is already pending. Revoke the pending timelock to overwrite it. /// @dev In case the new timelock is higher than the current one, the timelock is set immediately. function submitTimelock(uint256 newTimelock) external; /// @notice Accepts the pending timelock. function acceptTimelock() external; /// @notice Revokes the pending timelock. /// @dev Does not revert if there is no pending timelock. function revokePendingTimelock() external; /// @notice Submits a `newSupplyCap` for the market defined by `marketParams`. /// @dev Warning: Reverts if a cap is already pending. Revoke the pending cap to overwrite it. /// @dev Warning: Reverts if a market removal is pending. /// @dev In case the new cap is lower than the current one, the cap is set immediately. function submitCap(MarketParams memory marketParams, uint256 newSupplyCap) external; /// @notice Accepts the pending cap of the market defined by `marketParams`. function acceptCap(MarketParams memory marketParams) external; /// @notice Revokes the pending cap of the market defined by `id`. /// @dev Does not revert if there is no pending cap. function revokePendingCap(Id id) external; /// @notice Submits a forced market removal from the vault, eventually losing all funds supplied to the market. /// @notice Funds can be recovered by enabling this market again and withdrawing from it (using `reallocate`), /// but funds will be distributed pro-rata to the shares at the time of withdrawal, not at the time of removal. /// @notice This forced removal is expected to be used as an emergency process in case a market constantly reverts. /// To softly remove a sane market, the curator role is expected to bundle a reallocation that empties the market /// first (using `reallocate`), followed by the removal of the market (using `updateWithdrawQueue`). /// @dev Warning: Removing a market with non-zero supply will instantly impact the vault's price per share. /// @dev Warning: Reverts for non-zero cap or if there is a pending cap. Successfully submitting a zero cap will /// prevent such reverts. function submitMarketRemoval(MarketParams memory marketParams) external; /// @notice Revokes the pending removal of the market defined by `id`. /// @dev Does not revert if there is no pending market removal. function revokePendingMarketRemoval(Id id) external; /// @notice Submits a `newGuardian`. /// @notice Warning: a malicious guardian could disrupt the vault's operation, and would have the power to revoke /// any pending guardian. /// @dev In case there is no guardian, the gardian is set immediately. /// @dev Warning: Submitting a gardian will overwrite the current pending gardian. function submitGuardian(address newGuardian) external; /// @notice Accepts the pending guardian. function acceptGuardian() external; /// @notice Revokes the pending guardian. function revokePendingGuardian() external; /// @notice Skims the vault `token` balance to `skimRecipient`. function skim(address) external; /// @notice Sets `newAllocator` as an allocator or not (`newIsAllocator`). function setIsAllocator(address newAllocator, bool newIsAllocator) external; /// @notice Sets `curator` to `newCurator`. function setCurator(address newCurator) external; /// @notice Sets the `fee` to `newFee`. function setFee(uint256 newFee) external; /// @notice Sets `feeRecipient` to `newFeeRecipient`. function setFeeRecipient(address newFeeRecipient) external; /// @notice Sets `skimRecipient` to `newSkimRecipient`. function setSkimRecipient(address newSkimRecipient) external; /// @notice Sets `supplyQueue` to `newSupplyQueue`. /// @param newSupplyQueue is an array of enabled markets, and can contain duplicate markets, but it would only /// increase the cost of depositing to the vault. function setSupplyQueue(Id[] calldata newSupplyQueue) external; /// @notice Updates the withdraw queue. Some markets can be removed, but no market can be added. /// @notice Removing a market requires the vault to have 0 supply on it, or to have previously submitted a removal /// for this market (with the function `submitMarketRemoval`). /// @notice Warning: Anyone can supply on behalf of the vault so the call to `updateWithdrawQueue` that expects a /// market to be empty can be griefed by a front-run. To circumvent this, the allocator can simply bundle a /// reallocation that withdraws max from this market with a call to `updateWithdrawQueue`. /// @dev Warning: Removing a market with supply will decrease the fee accrued until one of the functions updating /// `lastTotalAssets` is triggered (deposit/mint/withdraw/redeem/setFee/setFeeRecipient). /// @dev Warning: `updateWithdrawQueue` is not idempotent. Submitting twice the same tx will change the queue twice. /// @param indexes The indexes of each market in the previous withdraw queue, in the new withdraw queue's order. function updateWithdrawQueue(uint256[] calldata indexes) external; /// @notice Reallocates the vault's liquidity so as to reach a given allocation of assets on each given market. /// @notice The allocator can withdraw from any market, even if it's not in the withdraw queue, as long as the loan /// token of the market is the same as the vault's asset. /// @dev The behavior of the reallocation can be altered by state changes, including: /// - Deposits on the vault that supplies to markets that are expected to be supplied to during reallocation. /// - Withdrawals from the vault that withdraws from markets that are expected to be withdrawn from during /// reallocation. /// - Donations to the vault on markets that are expected to be supplied to during reallocation. /// - Withdrawals from markets that are expected to be withdrawn from during reallocation. /// @dev Sender is expected to pass `assets = type(uint256).max` with the last MarketAllocation of `allocations` to /// supply all the remaining withdrawn liquidity, which would ensure that `totalWithdrawn` = `totalSupplied`. function reallocate(MarketAllocation[] calldata allocations) external; } /// @dev This interface is inherited by MetaMorpho so that function signatures are checked by the compiler. /// @dev Consider using the IMetaMorpho interface instead of this one. interface IMetaMorphoStaticTyping is IMetaMorphoBase { /// @notice Returns the current configuration of each market. function config(Id) external view returns (uint184 cap, bool enabled, uint64 removableAt); /// @notice Returns the pending guardian. function pendingGuardian() external view returns (address guardian, uint64 validAt); /// @notice Returns the pending cap for each market. function pendingCap(Id) external view returns (uint192 value, uint64 validAt); /// @notice Returns the pending timelock. function pendingTimelock() external view returns (uint192 value, uint64 validAt); } /// @title IMetaMorpho /// @author Morpho Labs /// @custom:contact [email protected] /// @dev Use this interface for MetaMorpho to have access to all the functions with the appropriate function signatures. interface IMetaMorpho is IMetaMorphoBase, IERC4626, IERC20Permit, IOwnable, IMulticall { /// @notice Returns the current configuration of each market. function config(Id) external view returns (MarketConfig memory); /// @notice Returns the pending guardian. function pendingGuardian() external view returns (PendingAddress memory); /// @notice Returns the pending cap for each market. function pendingCap(Id) external view returns (PendingUint192 memory); /// @notice Returns the pending timelock. function pendingTimelock() external view returns (PendingUint192 memory); }
// 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 another chain sharing /// the same chain id 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. /// @dev Here is a list of properties on the market's dependencies that could break Morpho's liveness properties /// (funds could get stuck): /// - The token can revert on `transfer` and `transferFrom` for a reason other than an approval or balance issue. /// - A very high amount of assets (~1e35) supplied or borrowed can make the computation of `toSharesUp` and /// `toSharesDown` overflow. /// - The IRM can revert on `borrowRate`. /// - A very high borrow rate returned by the IRM can make the computation of `interest` in `_accrueInterest` /// overflow. /// - The oracle can revert on `price`. Note that this can be used to prevent `borrow`, `withdrawCollateral` and /// `liquidate` from being used under certain market conditions. /// - A very high price returned by the oracle can make the computation of `maxBorrow` in `_isHealthy` overflow, or /// the computation of `assetsRepaid` in `liquidate` 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 /// `onMorphoReplay` 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 [email protected] /// @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} from "../../lib/metamorpho/src/interfaces/IMetaMorpho.sol"; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing error messages. library ErrorsLib { /// @notice Thrown when the `msg.sender` is not the admin nor the owner of the vault. error NotAdminNorVaultOwner(); /// @notice Thrown when the reallocation fee given is wrong. error IncorrectFee(); /// @notice Thrown when the value is already set. error AlreadySet(); /// @notice Thrown when `withdrawals` is empty. error EmptyWithdrawals(); /// @notice Thrown when `withdrawals` contains a duplicate or is not sorted. error InconsistentWithdrawals(); /// @notice Thrown when the deposit market is in `withdrawals`. error DepositMarketInWithdrawals(); /// @notice Thrown when attempting to reallocate or set flows to non-zero values for a non-enabled market. error MarketNotEnabled(Id id); /// @notice Thrown when attempting to withdraw zero of a market. error WithdrawZero(Id id); /// @notice Thrown when attempting to set max inflow/outflow above the MAX_SETTABLE_FLOW_CAP. error MaxSettableFlowCapExceeded(); /// @notice Thrown when attempting to withdraw more than the available supply of a market. error NotEnoughSupply(Id id); /// @notice Thrown when attempting to withdraw more than the max outflow of a market. error MaxOutflowExceeded(Id id); /// @notice Thrown when attempting to supply more than the max inflow of a market. error MaxInflowExceeded(Id id); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; import {FlowCapsConfig, Id} from "../interfaces/IPublicAllocator.sol"; /// @title EventsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing events. library EventsLib { /// @notice Emitted during a public reallocation for each withdrawn-from market. event PublicWithdrawal(address indexed sender, address indexed vault, Id indexed id, uint256 withdrawnAssets); /// @notice Emitted at the end of a public reallocation. event PublicReallocateTo( address indexed sender, address indexed vault, Id indexed supplyMarketId, uint256 suppliedAssets ); /// @notice Emitted when the admin is set for a vault. event SetAdmin(address indexed sender, address indexed vault, address admin); /// @notice Emitted when the fee is set for a vault. event SetFee(address indexed sender, address indexed vault, uint256 fee); /// @notice Emitted when the fee is transfered for a vault. event TransferFee(address indexed sender, address indexed vault, uint256 amount, address indexed feeRecipient); /// @notice Emitted when the flow caps are set for a vault. event SetFlowCaps(address indexed sender, address indexed vault, FlowCapsConfig[] config); }
// 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 [email protected] /// @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 {Id, MarketParams} from "../interfaces/IMorpho.sol"; /// @title MarketParamsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @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 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 [email protected] /// @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. /// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from /// assets to shares. 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: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.20; import {IERC20} from "../token/ERC20/IERC20.sol"; import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. */ interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; struct MarketConfig { /// @notice The maximum amount of assets that can be allocated to the market. uint184 cap; /// @notice Whether the market is in the withdraw queue. bool enabled; /// @notice The timestamp at which the market can be instantly removed from the withdraw queue. uint64 removableAt; } struct PendingUint192 { /// @notice The pending value to set. uint192 value; /// @notice The timestamp at which the pending value becomes valid. uint64 validAt; } struct PendingAddress { /// @notice The pending value to set. address value; /// @notice The timestamp at which the pending value becomes valid. uint64 validAt; } /// @title PendingLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library to manage pending values and their validity timestamp. library PendingLib { /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp. /// @dev Assumes `timelock` <= `MAX_TIMELOCK`. function update(PendingUint192 storage pending, uint184 newValue, uint256 timelock) internal { pending.value = newValue; // Safe "unchecked" cast because timelock <= MAX_TIMELOCK. pending.validAt = uint64(block.timestamp + timelock); } /// @dev Updates `pending`'s value to `newValue` and its corresponding `validAt` timestamp. /// @dev Assumes `timelock` <= `MAX_TIMELOCK`. function update(PendingAddress storage pending, address newValue, uint256 timelock) internal { pending.value = newValue; // Safe "unchecked" cast because timelock <= MAX_TIMELOCK. pending.validAt = uint64(block.timestamp + timelock); } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @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: GPL-2.0-or-later pragma solidity >=0.5.0; import {MarketParams, Market} from "./IMorpho.sol"; /// @title IIrm /// @author Morpho Labs /// @custom:contact [email protected] /// @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; uint256 constant WAD = 1e18; /// @title MathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @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 pragma solidity ^0.8.0; import {IMorpho, Id} from "../../interfaces/IMorpho.sol"; import {MorphoStorageLib} from "./MorphoStorageLib.sol"; /// @title MorphoLib /// @author Morpho Labs /// @custom:contact [email protected] /// @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: GPL-2.0-or-later pragma solidity ^0.8.0; import {MathLib} from "./MathLib.sol"; /// @title SharesMathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @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: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// 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 [email protected] /// @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); } }
{ "remappings": [ "@openzeppelin/contracts/=lib/metamorpho/lib/openzeppelin-contracts/contracts/", "ds-test/=lib/metamorpho/lib/forge-std/lib/ds-test/src/", "erc4626-tests/=lib/metamorpho/lib/erc4626-tests/", "forge-std/=lib/metamorpho/lib/forge-std/src/", "metamorpho/=lib/metamorpho/", "morpho-blue-irm/=lib/metamorpho/lib/morpho-blue-irm/src/", "morpho-blue/=lib/metamorpho/lib/morpho-blue/", "openzeppelin-contracts/=lib/metamorpho/lib/openzeppelin-contracts/", "solmate/=lib/metamorpho/lib/morpho-blue-irm/lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 999999 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"morpho","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadySet","type":"error"},{"inputs":[],"name":"DepositMarketInWithdrawals","type":"error"},{"inputs":[],"name":"EmptyWithdrawals","type":"error"},{"inputs":[],"name":"InconsistentWithdrawals","type":"error"},{"inputs":[],"name":"IncorrectFee","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MarketNotEnabled","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MaxInflowExceeded","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"MaxOutflowExceeded","type":"error"},{"inputs":[],"name":"MaxSettableFlowCapExceeded","type":"error"},{"inputs":[],"name":"NotAdminNorVaultOwner","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"NotEnoughSupply","type":"error"},{"inputs":[{"internalType":"Id","name":"id","type":"bytes32"}],"name":"WithdrawZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"Id","name":"supplyMarketId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"suppliedAssets","type":"uint256"}],"name":"PublicReallocateTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"Id","name":"id","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"withdrawnAssets","type":"uint256"}],"name":"PublicWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"address","name":"admin","type":"address"}],"name":"SetAdmin","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"components":[{"internalType":"Id","name":"id","type":"bytes32"},{"components":[{"internalType":"uint128","name":"maxIn","type":"uint128"},{"internalType":"uint128","name":"maxOut","type":"uint128"}],"internalType":"struct FlowCaps","name":"caps","type":"tuple"}],"indexed":false,"internalType":"struct FlowCapsConfig[]","name":"config","type":"tuple[]"}],"name":"SetFlowCaps","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"}],"name":"TransferFee","type":"event"},{"inputs":[],"name":"MORPHO","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"accruedFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"admin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"Id","name":"","type":"bytes32"}],"name":"flowCaps","outputs":[{"internalType":"uint128","name":"maxIn","type":"uint128"},{"internalType":"uint128","name":"maxOut","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"components":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"marketParams","type":"tuple"},{"internalType":"uint128","name":"amount","type":"uint128"}],"internalType":"struct Withdrawal[]","name":"withdrawals","type":"tuple[]"},{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"}],"internalType":"struct MarketParams","name":"supplyMarketParams","type":"tuple"}],"name":"reallocateTo","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address","name":"newAdmin","type":"address"}],"name":"setAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"components":[{"internalType":"Id","name":"id","type":"bytes32"},{"components":[{"internalType":"uint128","name":"maxIn","type":"uint128"},{"internalType":"uint128","name":"maxOut","type":"uint128"}],"internalType":"struct FlowCaps","name":"caps","type":"tuple"}],"internalType":"struct FlowCapsConfig[]","name":"config","type":"tuple[]"}],"name":"setFlowCaps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"vault","type":"address"},{"internalType":"address payable","name":"feeRecipient","type":"address"}],"name":"transferFee","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60a03461008857601f6122f738819003918201601f19168301916001600160401b0383118484101761008d5780849260209460405283398101031261008857516001600160a01b038116908190036100885760805260405161225390816100a48239608051818181610e1801528181610f2501528181611053015281816110dd0152611baa0152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe608080604052600436101561001357600080fd5b60003560e01c9081630e4eecf814611bce575080633acb562414611b5f57806363a846f814611af95780636fcca69b14611a94578063833947fd1461089757806391b114b2146108325780639dbcd5b9146107a6578063c55b6bb71461064b578063e55156b5146104f55763f46180461461008d57600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576100c4611d1b565b6024906024359067ffffffffffffffff8083116104f057366023840112156104f05782600401359081116104f0576024830192606090602436918385020101116104f05773ffffffffffffffffffffffffffffffffffffffff80931694856000526020936000855280604060002054163314159081610479575b5061044f576000939291935b8281106101e95750506040519281838501848652526040840194926000905b83821061019a5787337f709e1cb4b0ac458eb1c1a9c708e841ee963b229247afbf1437bd39e01ae4aa14888a0389a3005b90919293958380600192893581526fffffffffffffffffffffffffffffffff806101c5878d016120ac565b16868301526101d660408c016120ac565b1660408201520197019493920190610169565b6101f88184889795969761209c565b356040517fcc718f76000000000000000000000000000000000000000000000000000000008152816004820152848185818c5afa8015610443578791600091610416575b50015115806103c8575b610398576fffffffffffffffffffffffffffffffff91906f7fffffffffffffffffffffffffffffff80846102858a61027f878c8f61209c565b0161200b565b1611908484898c8515610378575b505050505061034e576001926103046040610344936102b3868b8e61209c565b908d60005260038c5282600020906000528b5281600020936102d68c830161200b565b167fffffffffffffffffffffffffffffffff000000000000000000000000000000008554161784550161200b565b6fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b019392919361014a565b60046040517fb9860d64000000000000000000000000000000000000000000000000000000008152fd5b61038c939495509161027f9160409361209c565b1611388484898c610293565b9050604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b506fffffffffffffffffffffffffffffffff806103ea8861027f868a8d61209c565b1615908115916103fb575b50610246565b905061040d604061027f85898c61209c565b161515386103f5565b6104369150863d881161043c575b61042e8183611dc7565b810190611f3b565b3861023c565b503d610424565b6040513d6000823e3d90fd5b60046040517f26d3a7bf000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b00000000000000000000000000000000000000000000000000000000815285816004818b5afa908115610443576000916104c3575b50163314153861013e565b6104e39150863d88116104e9575b6104db8183611dc7565b810190611e08565b386104b8565b503d6104d1565b600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05761052c611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff809216918260005260209060008252806040600020541633141590816105e4575b5061044f57826000526001815281604060002054146105ba577f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb64908360005260018152826040600020556040519283523392a3005b60046040517fa741a045000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa9081156104435760009161062e575b501633141584610565565b6106459150833d85116104e9576104db8183611dc7565b85610623565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057610682611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff918282168092036104f0578216918260005260209060008252806040600020541633141580610740575b61044f578290846000526000835260406000205416146105ba577fc51248b3e510a1244e01043dffdc0132d10194bd4506382cbcf83d05f6ec57ef9083600052600081526040600020837fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790556040519283523392a3005b506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa8015610443578291600091610789575b50163314156106c4565b6107a09150843d86116104e9576104db8183611dc7565b8661077f565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff6107f2611d1b565b1660005260036020526040600020602435600052602052604080600020548151906fffffffffffffffffffffffffffffffff8116825260801c6020820152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff61087e611d1b565b1660005260026020526020604060002054604051908152f35b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576108c9611d1b565b60243567ffffffffffffffff81116104f057366023820112156104f05767ffffffffffffffff8160040135116104f05736602460c0836004013502830101116104f05760a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126104f05773ffffffffffffffffffffffffffffffffffffffff821660005260016020526040600020543403611a6a5734611a34575b806004013515611a0a5760a061097c36611e41565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff86165afa8015610443576020916000916119eb575b5001511561194d57600482013560018101919082106113c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610a36610a2084611fb4565b93610a2e6040519586611dc7565b808552611fb4565b0160005b81811061199d5750506000926000805b82600401358210610d2c57505073ffffffffffffffffffffffffffffffffffffffff821660005260036020526040600020856000526020526fffffffffffffffffffffffffffffffff93846040600020541694808216809610610cfb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92610b74602093610b989373ffffffffffffffffffffffffffffffffffffffff88166000526003865260406000208b60005286526040600020907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825491610b328582851661204c565b16911617905573ffffffffffffffffffffffffffffffffffffffff87166000526003855260406000208a6000528552610304604060002091825460801c612028565b610b8281600401358761207b565b51610b8c36611e41565b9052600401358561207b565b51015273ffffffffffffffffffffffffffffffffffffffff81163b156104f05760405180927f7299aa310000000000000000000000000000000000000000000000000000000082526024820160206004840152815180915260206044840192019060005b818110610c8957505050908060009203818373ffffffffffffffffffffffffffffffffffffffff86165af19182156104435773ffffffffffffffffffffffffffffffffffffffff92610c7a575b5060405192835216907ff8ae80b0854dfc3c73d3eb4b6160df1996a5859e6c1d11d10f3980a7f469199160203392a4005b610c8390611d97565b84610c49565b91935091602060c0600192828751610ce68382516080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b015160a0820152019401910191859392610bfc565b602487604051907f2e581b4e0000000000000000000000000000000000000000000000000000000082526004820152fd5b949060a0610d4b36610d4684876004013560248901611fcc565b611ed9565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff8a165afa80156104435760209160009161197e575b5001511561194d57610dc860a061027f84876004013560248901611fcc565b966fffffffffffffffffffffffffffffffff88161561191c578311156118f2578783146118c857610e0182856004013560248701611fcc565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163b156104f0576080604051917f151c1ade00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff610e8582611d3e565b16600484015273ffffffffffffffffffffffffffffffffffffffff610eac60208301611d3e565b16602484015273ffffffffffffffffffffffffffffffffffffffff610ed360408301611d3e565b16604484015273ffffffffffffffffffffffffffffffffffffffff610efa60608301611d3e565b1660648401520135608482015260008160a4818373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165af18015610443576118b9575b50610f6936610d4684876004013560248901611fcc565b60a08120604051602081019182526002604082015260408152610f8b81611dab565b519020604051602081019173ffffffffffffffffffffffffffffffffffffffff89168352604082015260408152610fc181611dab565b519020604051610fd081611d5f565b6001815260208101916020368437610fe78261206e565b526040519182917f7784c685000000000000000000000000000000000000000000000000000000008352602483019060206004850152518091526044830191906000905b80821061189d575050509080600092038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015610443576000906117f1575b61108d915061206e565b519060a0812090604051917f5c60e39a000000000000000000000000000000000000000000000000000000008352600483015260c08260248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa9182156104435760009261170b575b5060808201906111316fffffffffffffffffffffffffffffffff8351164261208f565b90811515806116ec575b806116c9575b6113f6575b5050506fffffffffffffffffffffffffffffffff602081835116920151166001820182116113c757620f4240810181116113c757611196926001620f424061119193019301906120e6565b6121e4565b73ffffffffffffffffffffffffffffffffffffffff861660005260036020526040600020846000526020526fffffffffffffffffffffffffffffffff881660406000205460801c10611396576fffffffffffffffffffffffffffffffff88168110611365576113078884936112f387948b6112d86112cf896112c98f9c8f73ffffffffffffffffffffffffffffffffffffffff60019f8e828216600052600360205260406000209060005260205260406000208d7fffffffffffffffffffffffffffffffff000000000000000000000000000000006fffffffffffffffffffffffffffffffff61128a845493828516612028565b16911617905516600052600360205260406000208d6000526020526112bb60406000206103048d825460801c61204c565b602481600401359101611fcc565b9361207b565b51913690611ed9565b90526fffffffffffffffffffffffffffffffff84169061208f565b60206112ff878d61207b565b510152612028565b976fffffffffffffffffffffffffffffffff6040519116815273ffffffffffffffffffffffffffffffffffffffff8716907f6218cdb9e8efb3d0e8136d32c91d9446eaf19e2e486bc67dfcb3d574ca60d50460203392a40190610a4a565b602484604051907f8c4bfb140000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fad5f61d30000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6114a46101648273ffffffffffffffffffffffffffffffffffffffff60606020950151166fffffffffffffffffffffffffffffffff60405197889586947f8c00bf6b00000000000000000000000000000000000000000000000000000000865260048601906080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b818a511660a485015281878b01511660c48501528160408b01511660e48501528160608b01511661010485015251166101248301526fffffffffffffffffffffffffffffffff60a0890151166101448301525afa91821561044357600092611693575b509061157a61153061156f936fffffffffffffffffffffffffffffffff604087015116936120e6565b91611574671bc16d674ec8000061154785806120e6565b0493670de0b6b3a764000095856729a2241af62c0000611568848a996120e6565b0492611e34565b611e34565b906120e6565b046fffffffffffffffffffffffffffffffff6115a4611598836120f9565b82604087015116612028565b1660408401526fffffffffffffffffffffffffffffffff6115d06115c7836120f9565b82865116612028565b1683526fffffffffffffffffffffffffffffffff60a0840151168015611146576115f9916120e6565b04611617816fffffffffffffffffffffffffffffffff84511661208f565b6fffffffffffffffffffffffffffffffff60208401511690620f4240820182116113c7576001810181116113c757611674611685926111916fffffffffffffffffffffffffffffffff95620f4240600161167996019301906120e6565b6120f9565b82602085015116612028565b1660208201528a8080611146565b91506020823d6020116116c1575b816116ae60209383611dc7565b810103126104f05790519061157a611507565b3d91506116a1565b5073ffffffffffffffffffffffffffffffffffffffff6060820151161515611141565b506fffffffffffffffffffffffffffffffff604085015116151561113b565b90915060c0813d60c0116117e9575b8161172760c09383611dc7565b810103126104f057604051908160c081011067ffffffffffffffff60c0840111176117ba5760a06117ae9160c08401604052611762816120c9565b8452611770602082016120c9565b6020850152611781604082016120c9565b6040850152611792606082016120c9565b60608501526117a3608082016120c9565b6080850152016120c9565b60a0820152908b61110e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d915061171a565b503d90816000823e6118038282611dc7565b60208183810103126104f05780519167ffffffffffffffff83116104f057808201601f8484010112156104f057828201519161183e83611fb4565b9361184c6040519586611dc7565b8385526020850192820160208560051b8385010101116104f057602081830101925b60208560051b8385010101841061188d57505050505061108d90611083565b835181526020938401930161186e565b919350916020806001928651815201940192018493929161102b565b6118c290611d97565b88610f52565b60046040517f898ca719000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc9527748000000000000000000000000000000000000000000000000000000008152fd5b602484604051907f9565ed900000000000000000000000000000000000000000000000000000000082526004820152fd5b602483604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b611997915060603d60601161043c5761042e8183611dc7565b8a610da9565b6020906040516119ac81611d5f565b6040516119b881611d7b565b60008152600084820152600060408201526000606082015260006080820152815260008382015282828701015201610a3a565b611a04915060603d60601161043c5761042e8183611dc7565b856109da565b60046040517f76da5945000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff821660005260026020526040600020611a63348254611e34565b9055610967565b60046040517fcd3cb2bb000000000000000000000000000000000000000000000000000000008152fd5b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff611ae0611d1b565b1660005260016020526020604060002054604051908152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602073ffffffffffffffffffffffffffffffffffffffff80611b48611d1b565b166000526000825260406000205416604051908152f35b346104f05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057611c05611d1b565b906024359173ffffffffffffffffffffffffffffffffffffffff908184168094036104f057811691826000526020916000835280604060002054163314159182611cb5575b505061044f578160005260028152604060002090600082549255600082858115611cab575b600092839283928392f115610443577f6ab9f885fa0bfd2af57586f4cdde83bbfc79294d0cd2d61d4b31e9a3d1be6e2c906040519283523392a4005b6108fc9250611c6f565b9091507f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481875afa90811561044357600091611cfe575b50163314158480611c4a565b611d159150833d85116104e9576104db8183611dc7565b85611cf2565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b6040810190811067ffffffffffffffff8211176117ba57604052565b60a0810190811067ffffffffffffffff8211176117ba57604052565b67ffffffffffffffff81116117ba57604052565b6060810190811067ffffffffffffffff8211176117ba57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176117ba57604052565b908160209103126104f0575173ffffffffffffffffffffffffffffffffffffffff811681036104f05790565b919082018092116113c757565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc60a09101126104f05760405190611e7882611d7b565b8173ffffffffffffffffffffffffffffffffffffffff60443581811681036104f057825260643581811681036104f057602083015260843581811681036104f057604083015260a43590811681036104f0576060820152608060c435910152565b91908260a09103126104f057604051611ef181611d7b565b6080808294611eff81611d3e565b8452611f0d60208201611d3e565b6020850152611f1e60408201611d3e565b6040850152611f2f60608201611d3e565b60608501520135910152565b908160609103126104f0576040519067ffffffffffffffff9060608301828111848210176117ba57604052805176ffffffffffffffffffffffffffffffffffffffffffffff811681036104f057835260208101519081151582036104f0576040916020850152015190811681036104f057604082015290565b67ffffffffffffffff81116117ba5760051b60200190565b9190811015611fdc5760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104f05790565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113c757565b6fffffffffffffffffffffffffffffffff91821690821603919082116113c757565b805115611fdc5760200190565b8051821015611fdc5760209160051b010190565b919082039182116113c757565b9190811015611fdc576060020190565b35906fffffffffffffffffffffffffffffffff821682036104f057565b51906fffffffffffffffffffffffffffffffff821682036104f057565b818102929181159184041417156113c757565b6fffffffffffffffffffffffffffffffff9060405161211781611d5f565b601481526020907f6d61782075696e7431323820657863656564656400000000000000000000000060208201528383116121515750501690565b60405180927f08c379a00000000000000000000000000000000000000000000000000000000082526020600483015282519283602484015260005b8481106121cd575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b81810183015186820160440152859350820161218c565b81156121ee570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220f28f85a78415b835820c74ee6024f92207b1f2686716124647e59802a3f4164164736f6c63430008180033000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Deployed Bytecode
0x608080604052600436101561001357600080fd5b60003560e01c9081630e4eecf814611bce575080633acb562414611b5f57806363a846f814611af95780636fcca69b14611a94578063833947fd1461089757806391b114b2146108325780639dbcd5b9146107a6578063c55b6bb71461064b578063e55156b5146104f55763f46180461461008d57600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576100c4611d1b565b6024906024359067ffffffffffffffff8083116104f057366023840112156104f05782600401359081116104f0576024830192606090602436918385020101116104f05773ffffffffffffffffffffffffffffffffffffffff80931694856000526020936000855280604060002054163314159081610479575b5061044f576000939291935b8281106101e95750506040519281838501848652526040840194926000905b83821061019a5787337f709e1cb4b0ac458eb1c1a9c708e841ee963b229247afbf1437bd39e01ae4aa14888a0389a3005b90919293958380600192893581526fffffffffffffffffffffffffffffffff806101c5878d016120ac565b16868301526101d660408c016120ac565b1660408201520197019493920190610169565b6101f88184889795969761209c565b356040517fcc718f76000000000000000000000000000000000000000000000000000000008152816004820152848185818c5afa8015610443578791600091610416575b50015115806103c8575b610398576fffffffffffffffffffffffffffffffff91906f7fffffffffffffffffffffffffffffff80846102858a61027f878c8f61209c565b0161200b565b1611908484898c8515610378575b505050505061034e576001926103046040610344936102b3868b8e61209c565b908d60005260038c5282600020906000528b5281600020936102d68c830161200b565b167fffffffffffffffffffffffffffffffff000000000000000000000000000000008554161784550161200b565b6fffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffff0000000000000000000000000000000083549260801b169116179055565b019392919361014a565b60046040517fb9860d64000000000000000000000000000000000000000000000000000000008152fd5b61038c939495509161027f9160409361209c565b1611388484898c610293565b9050604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b506fffffffffffffffffffffffffffffffff806103ea8861027f868a8d61209c565b1615908115916103fb575b50610246565b905061040d604061027f85898c61209c565b161515386103f5565b6104369150863d881161043c575b61042e8183611dc7565b810190611f3b565b3861023c565b503d610424565b6040513d6000823e3d90fd5b60046040517f26d3a7bf000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b00000000000000000000000000000000000000000000000000000000815285816004818b5afa908115610443576000916104c3575b50163314153861013e565b6104e39150863d88116104e9575b6104db8183611dc7565b810190611e08565b386104b8565b503d6104d1565b600080fd5b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05761052c611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff809216918260005260209060008252806040600020541633141590816105e4575b5061044f57826000526001815281604060002054146105ba577f44a6d70a601a6f8a85c075467e9d7245897140cbf6dd505c9d9d764459f5fb64908360005260018152826040600020556040519283523392a3005b60046040517fa741a045000000000000000000000000000000000000000000000000000000008152fd5b90506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa9081156104435760009161062e575b501633141584610565565b6106459150833d85116104e9576104db8183611dc7565b85610623565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057610682611d1b565b60243573ffffffffffffffffffffffffffffffffffffffff918282168092036104f0578216918260005260209060008252806040600020541633141580610740575b61044f578290846000526000835260406000205416146105ba577fc51248b3e510a1244e01043dffdc0132d10194bd4506382cbcf83d05f6ec57ef9083600052600081526040600020837fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790556040519283523392a3005b506040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481885afa8015610443578291600091610789575b50163314156106c4565b6107a09150843d86116104e9576104db8183611dc7565b8661077f565b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff6107f2611d1b565b1660005260036020526040600020602435600052602052604080600020548151906fffffffffffffffffffffffffffffffff8116825260801c6020820152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff61087e611d1b565b1660005260026020526020604060002054604051908152f35b60e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f0576108c9611d1b565b60243567ffffffffffffffff81116104f057366023820112156104f05767ffffffffffffffff8160040135116104f05736602460c0836004013502830101116104f05760a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc3601126104f05773ffffffffffffffffffffffffffffffffffffffff821660005260016020526040600020543403611a6a5734611a34575b806004013515611a0a5760a061097c36611e41565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff86165afa8015610443576020916000916119eb575b5001511561194d57600482013560018101919082106113c7577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0610a36610a2084611fb4565b93610a2e6040519586611dc7565b808552611fb4565b0160005b81811061199d5750506000926000805b82600401358210610d2c57505073ffffffffffffffffffffffffffffffffffffffff821660005260036020526040600020856000526020526fffffffffffffffffffffffffffffffff93846040600020541694808216809610610cfb577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff92610b74602093610b989373ffffffffffffffffffffffffffffffffffffffff88166000526003865260406000208b60005286526040600020907fffffffffffffffffffffffffffffffff00000000000000000000000000000000825491610b328582851661204c565b16911617905573ffffffffffffffffffffffffffffffffffffffff87166000526003855260406000208a6000528552610304604060002091825460801c612028565b610b8281600401358761207b565b51610b8c36611e41565b9052600401358561207b565b51015273ffffffffffffffffffffffffffffffffffffffff81163b156104f05760405180927f7299aa310000000000000000000000000000000000000000000000000000000082526024820160206004840152815180915260206044840192019060005b818110610c8957505050908060009203818373ffffffffffffffffffffffffffffffffffffffff86165af19182156104435773ffffffffffffffffffffffffffffffffffffffff92610c7a575b5060405192835216907ff8ae80b0854dfc3c73d3eb4b6160df1996a5859e6c1d11d10f3980a7f469199160203392a4005b610c8390611d97565b84610c49565b91935091602060c0600192828751610ce68382516080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b015160a0820152019401910191859392610bfc565b602487604051907f2e581b4e0000000000000000000000000000000000000000000000000000000082526004820152fd5b949060a0610d4b36610d4684876004013560248901611fcc565b611ed9565b20916040517fcc718f7600000000000000000000000000000000000000000000000000000000815283600482015260608160248173ffffffffffffffffffffffffffffffffffffffff8a165afa80156104435760209160009161197e575b5001511561194d57610dc860a061027f84876004013560248901611fcc565b966fffffffffffffffffffffffffffffffff88161561191c578311156118f2578783146118c857610e0182856004013560248701611fcc565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb163b156104f0576080604051917f151c1ade00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff610e8582611d3e565b16600484015273ffffffffffffffffffffffffffffffffffffffff610eac60208301611d3e565b16602484015273ffffffffffffffffffffffffffffffffffffffff610ed360408301611d3e565b16604484015273ffffffffffffffffffffffffffffffffffffffff610efa60608301611d3e565b1660648401520135608482015260008160a4818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb165af18015610443576118b9575b50610f6936610d4684876004013560248901611fcc565b60a08120604051602081019182526002604082015260408152610f8b81611dab565b519020604051602081019173ffffffffffffffffffffffffffffffffffffffff89168352604082015260408152610fc181611dab565b519020604051610fd081611d5f565b6001815260208101916020368437610fe78261206e565b526040519182917f7784c685000000000000000000000000000000000000000000000000000000008352602483019060206004850152518091526044830191906000905b80821061189d575050509080600092038173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb165afa8015610443576000906117f1575b61108d915061206e565b519060a0812090604051917f5c60e39a000000000000000000000000000000000000000000000000000000008352600483015260c08260248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb165afa9182156104435760009261170b575b5060808201906111316fffffffffffffffffffffffffffffffff8351164261208f565b90811515806116ec575b806116c9575b6113f6575b5050506fffffffffffffffffffffffffffffffff602081835116920151166001820182116113c757620f4240810181116113c757611196926001620f424061119193019301906120e6565b6121e4565b73ffffffffffffffffffffffffffffffffffffffff861660005260036020526040600020846000526020526fffffffffffffffffffffffffffffffff881660406000205460801c10611396576fffffffffffffffffffffffffffffffff88168110611365576113078884936112f387948b6112d86112cf896112c98f9c8f73ffffffffffffffffffffffffffffffffffffffff60019f8e828216600052600360205260406000209060005260205260406000208d7fffffffffffffffffffffffffffffffff000000000000000000000000000000006fffffffffffffffffffffffffffffffff61128a845493828516612028565b16911617905516600052600360205260406000208d6000526020526112bb60406000206103048d825460801c61204c565b602481600401359101611fcc565b9361207b565b51913690611ed9565b90526fffffffffffffffffffffffffffffffff84169061208f565b60206112ff878d61207b565b510152612028565b976fffffffffffffffffffffffffffffffff6040519116815273ffffffffffffffffffffffffffffffffffffffff8716907f6218cdb9e8efb3d0e8136d32c91d9446eaf19e2e486bc67dfcb3d574ca60d50460203392a40190610a4a565b602484604051907f8c4bfb140000000000000000000000000000000000000000000000000000000082526004820152fd5b602484604051907fad5f61d30000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6114a46101648273ffffffffffffffffffffffffffffffffffffffff60606020950151166fffffffffffffffffffffffffffffffff60405197889586947f8c00bf6b00000000000000000000000000000000000000000000000000000000865260048601906080809173ffffffffffffffffffffffffffffffffffffffff80825116855280602083015116602086015280604083015116604086015260608201511660608501520151910152565b818a511660a485015281878b01511660c48501528160408b01511660e48501528160608b01511661010485015251166101248301526fffffffffffffffffffffffffffffffff60a0890151166101448301525afa91821561044357600092611693575b509061157a61153061156f936fffffffffffffffffffffffffffffffff604087015116936120e6565b91611574671bc16d674ec8000061154785806120e6565b0493670de0b6b3a764000095856729a2241af62c0000611568848a996120e6565b0492611e34565b611e34565b906120e6565b046fffffffffffffffffffffffffffffffff6115a4611598836120f9565b82604087015116612028565b1660408401526fffffffffffffffffffffffffffffffff6115d06115c7836120f9565b82865116612028565b1683526fffffffffffffffffffffffffffffffff60a0840151168015611146576115f9916120e6565b04611617816fffffffffffffffffffffffffffffffff84511661208f565b6fffffffffffffffffffffffffffffffff60208401511690620f4240820182116113c7576001810181116113c757611674611685926111916fffffffffffffffffffffffffffffffff95620f4240600161167996019301906120e6565b6120f9565b82602085015116612028565b1660208201528a8080611146565b91506020823d6020116116c1575b816116ae60209383611dc7565b810103126104f05790519061157a611507565b3d91506116a1565b5073ffffffffffffffffffffffffffffffffffffffff6060820151161515611141565b506fffffffffffffffffffffffffffffffff604085015116151561113b565b90915060c0813d60c0116117e9575b8161172760c09383611dc7565b810103126104f057604051908160c081011067ffffffffffffffff60c0840111176117ba5760a06117ae9160c08401604052611762816120c9565b8452611770602082016120c9565b6020850152611781604082016120c9565b6040850152611792606082016120c9565b60608501526117a3608082016120c9565b6080850152016120c9565b60a0820152908b61110e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b3d915061171a565b503d90816000823e6118038282611dc7565b60208183810103126104f05780519167ffffffffffffffff83116104f057808201601f8484010112156104f057828201519161183e83611fb4565b9361184c6040519586611dc7565b8385526020850192820160208560051b8385010101116104f057602081830101925b60208560051b8385010101841061188d57505050505061108d90611083565b835181526020938401930161186e565b919350916020806001928651815201940192018493929161102b565b6118c290611d97565b88610f52565b60046040517f898ca719000000000000000000000000000000000000000000000000000000008152fd5b60046040517fc9527748000000000000000000000000000000000000000000000000000000008152fd5b602484604051907f9565ed900000000000000000000000000000000000000000000000000000000082526004820152fd5b602483604051907f6113d8c70000000000000000000000000000000000000000000000000000000082526004820152fd5b611997915060603d60601161043c5761042e8183611dc7565b8a610da9565b6020906040516119ac81611d5f565b6040516119b881611d7b565b60008152600084820152600060408201526000606082015260006080820152815260008382015282828701015201610a3a565b611a04915060603d60601161043c5761042e8183611dc7565b856109da565b60046040517f76da5945000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff821660005260026020526040600020611a63348254611e34565b9055610967565b60046040517fcd3cb2bb000000000000000000000000000000000000000000000000000000008152fd5b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f05773ffffffffffffffffffffffffffffffffffffffff611ae0611d1b565b1660005260016020526020604060002054604051908152f35b346104f05760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602073ffffffffffffffffffffffffffffffffffffffff80611b48611d1b565b166000526000825260406000205416604051908152f35b346104f05760007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb168152f35b346104f05760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f057611c05611d1b565b906024359173ffffffffffffffffffffffffffffffffffffffff908184168094036104f057811691826000526020916000835280604060002054163314159182611cb5575b505061044f578160005260028152604060002090600082549255600082858115611cab575b600092839283928392f115610443577f6ab9f885fa0bfd2af57586f4cdde83bbfc79294d0cd2d61d4b31e9a3d1be6e2c906040519283523392a4005b6108fc9250611c6f565b9091507f8da5cb5b0000000000000000000000000000000000000000000000000000000081528281600481875afa90811561044357600091611cfe575b50163314158480611c4a565b611d159150833d85116104e9576104db8183611dc7565b85611cf2565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b359073ffffffffffffffffffffffffffffffffffffffff821682036104f057565b6040810190811067ffffffffffffffff8211176117ba57604052565b60a0810190811067ffffffffffffffff8211176117ba57604052565b67ffffffffffffffff81116117ba57604052565b6060810190811067ffffffffffffffff8211176117ba57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176117ba57604052565b908160209103126104f0575173ffffffffffffffffffffffffffffffffffffffff811681036104f05790565b919082018092116113c757565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbc60a09101126104f05760405190611e7882611d7b565b8173ffffffffffffffffffffffffffffffffffffffff60443581811681036104f057825260643581811681036104f057602083015260843581811681036104f057604083015260a43590811681036104f0576060820152608060c435910152565b91908260a09103126104f057604051611ef181611d7b565b6080808294611eff81611d3e565b8452611f0d60208201611d3e565b6020850152611f1e60408201611d3e565b6040850152611f2f60608201611d3e565b60608501520135910152565b908160609103126104f0576040519067ffffffffffffffff9060608301828111848210176117ba57604052805176ffffffffffffffffffffffffffffffffffffffffffffff811681036104f057835260208101519081151582036104f0576040916020850152015190811681036104f057604082015290565b67ffffffffffffffff81116117ba5760051b60200190565b9190811015611fdc5760c0020190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b356fffffffffffffffffffffffffffffffff811681036104f05790565b9190916fffffffffffffffffffffffffffffffff808094169116019182116113c757565b6fffffffffffffffffffffffffffffffff91821690821603919082116113c757565b805115611fdc5760200190565b8051821015611fdc5760209160051b010190565b919082039182116113c757565b9190811015611fdc576060020190565b35906fffffffffffffffffffffffffffffffff821682036104f057565b51906fffffffffffffffffffffffffffffffff821682036104f057565b818102929181159184041417156113c757565b6fffffffffffffffffffffffffffffffff9060405161211781611d5f565b601481526020907f6d61782075696e7431323820657863656564656400000000000000000000000060208201528383116121515750501690565b60405180927f08c379a00000000000000000000000000000000000000000000000000000000082526020600483015282519283602484015260005b8481106121cd575050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f836000604480968601015201168101030190fd5b81810183015186820160440152859350820161218c565b81156121ee570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fdfea2646970667358221220f28f85a78415b835820c74ee6024f92207b1f2686716124647e59802a3f4164164736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
-----Decoded View---------------
Arg [0] : morpho (address): 0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000bbbbbbbbbb9cc5e90e3b3af64bdaf62c37eeffcb
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.