Address Contract Partially Verified
Address
0x2A66F992bF227D2e50eF19EDD21503C3c4F3f682
Balance
0 ETH
Nonce
1
Code Size
6411 bytes
Creator
0x1226858E...D06B at tx 0x7aabe74c...592c73
Indexed Transactions
2 (24,437,331 → 24,437,333)
Gas Used (indexed)
853,785
Contract Bytecode
6411 bytes
0x604060808152600480361015610013575f80fd5b5f3560e01c9081632b97236d146105bc5781633acb56241461054e57816348c4cf8c146105095781635d150a29146104585781637bde82f21461032c57816383df6747146102d857816389a302711461026a578163cb6d71bc146101fc578163f7f6ba531461018e578163f866af0f14610107575063fcd72b8614610096575f80fd5b34610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc168152f35b5f80fd5b82346101035760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101035760209135906101566101476106a5565b61014f610781565b9084610bcf565b918151908152606435848201527f51bd79fe925d34dd35ece291269c5b8870db05f360c336d1cb6b778d2b252cc1823392a251908152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f689555121e529ff0463e191f9bd9d1e496164a7168152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e168152f35b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168152f35b82346101035760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576103256020926103166106a5565b61031e610781565b9135610bcf565b9051908152f35b823461010357807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101035760206103c6926103696106a5565b83517fba087652000000000000000000000000000000000000000000000000000000008152823592810192835273ffffffffffffffffffffffffffffffffffffffff90911660208301523360408301529384918291606090910190565b03815f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc165af190811561044f575f9161041a575b6020925051908152f35b90506020823d602011610447575b8161043560209383610637565b81010312610103576020915190610410565b3d9150610428565b513d5f823e3d90fd5b905034610103576101207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261010357610493366106c8565b9060e4359173ffffffffffffffffffffffffffffffffffffffff8316830361010357610104359267ffffffffffffffff928385116101035736602386011215610103578401359283116101035736602484860101116101035760246104fd94019160c43590610a55565b82519182526020820152f35b82346101035760e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576104fd610545366106c8565b60c435906107a6565b8234610103575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576020905173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc168152f35b8234610103576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610103576105f9610545366106c8565b825182815260e43560208201527f1b7ae6a70016176e99f0d91a76a774e1209b170912caca81475e3a2d85863af1843392a282519182526020820152f35b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761067857604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361010357565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60c0910112610103576040519060c0820182811067ffffffffffffffff8211176106785760405273ffffffffffffffffffffffffffffffffffffffff826004358281168103610103578152602435828116810361010357602082015260443582811681036101035760408201526064358281168103610103576060820152608435608082015260a43591821682036101035760a00152565b60443590811515820361010357565b9190826040910312610103576020825192015190565b91909173ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e166040928351957fc6e6f59200000000000000000000000000000000000000000000000000000000875260048701526020928387602481865afa968715610a0d575f976109da575b5061014485925f6108b89385519a8b9586947f107ddb03000000000000000000000000000000000000000000000000000000008652600486019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b60c48401528160e484015233610104840152306101248401527f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af19485156109d0575f905f96610994575b5083517fba0876520000000000000000000000000000000000000000000000000000000081526004810191909152336024820152306044820152919081908390815f81606481015b03925af192831561044f57505f92610966575b50509190565b90809250813d831161098d575b61097d8183610637565b8101031261010357515f80610960565b503d610973565b839296505f9391506109be61094d91863d88116109c9575b6109b68183610637565b810190610790565b979394509150610905565b503d6109ac565b83513d5f823e3d90fd5b919096508382813d8311610a06575b6109f38183610637565b8101031261010357905195610144610828565b503d6109e9565b85513d5f823e3d90fd5b601f82602094937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe093818652868601375f8582860101520116010190565b9194939291907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103610a92575093610a8e9394611016565b9091565b92610b475f60409493610b6d610aa88833610f33565b9573ffffffffffffffffffffffffffffffffffffffff958689519c8d998a9889977f58e83ce0000000000000000000000000000000000000000000000000000000008952600489019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b60c48701528660e487015216610104850152610140610124850152610144840191610a17565b03927f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af1928315610bc4575f93610ba557509190565b610bbf91935060403d6040116109c9576109b68183610637565b905090565b6040513d5f823e3d90fd5b9173ffffffffffffffffffffffffffffffffffffffff9182811691823303610f0957610c1f853033877f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481661176c565b15610e5b57827f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc166040948551937f46aa2f1200000000000000000000000000000000000000000000000000000000855260048501526020938481602481865afa908115610e51579082915f91610e20575b5010610dc35785517f6e553f65000000000000000000000000000000000000000000000000000000008082526004820192909252306024820152918490839060449082905f905af1918215610db9575f92610d82575b508551908152600481019190915273ffffffffffffffffffffffffffffffffffffffff90911660248201529181908390815f81604481015b03927f000000000000000000000000f689555121e529ff0463e191f9bd9d1e496164a7165af192831561044f57505f92610d5857505090565b90809250813d8311610d7b575b610d6f8183610637565b81010312610103575190565b503d610d65565b929150938383813d8311610db2575b610d9b8183610637565b810103126101035791519193909190610d1f610ce7565b503d610d91565b86513d5f823e3d90fd5b6064848751907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152601560248201527f4465706f7369742065786365656473206c696d697400000000000000000000006044820152fd5b809250868092503d8311610e4a575b610e398183610637565b81010312610103578190515f610c91565b503d610e2f565b87513d5f823e3d90fd5b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481019490945273ffffffffffffffffffffffffffffffffffffffff16602484015250602090829060449082905f907f000000000000000000000000056b269eb1f75477a8666ae8c7fe01b64dd55ecc165af1908115610bc4575f91610ee6575090565b90506020813d602011610f01575b81610d6f60209383610637565b3d9150610ef4565b60046040517f82b42900000000000000000000000000000000000000000000000000000000008152fd5b610fc26020915f93610f7f8173ffffffffffffffffffffffffffffffffffffffff933090857f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481661176c565b6040517f6e553f65000000000000000000000000000000000000000000000000000000008152600481019190915230602482015293849283919082906044820190565b03927f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e165af1908115610bc4575f91610ee6575090565b51906fffffffffffffffffffffffffffffffff8216820361010357565b93929360c08120925f956040516040810181811067ffffffffffffffff82111761067857604052600181526020368183013780511561173f5773ffffffffffffffffffffffffffffffffffffffff8516602082015273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc163b156101035760405180917f78c56d650000000000000000000000000000000000000000000000000000000082528760048301526040602483015280518060448401526020606484019201905f5b8181106117105750505090805f9203818373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af18015610bc4576116f3575b506040517f93c5206200000000000000000000000000000000000000000000000000000000815285600482015273ffffffffffffffffffffffffffffffffffffffff8516602482015260608160448173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165afa9081156114ee57889161167e575b506fffffffffffffffffffffffffffffffff6020820151161561167457604051957f5c60e39a000000000000000000000000000000000000000000000000000000008752600487015260e08660248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165afa9586156114ee5788966115ad575b506fffffffffffffffffffffffffffffffff6020820151166fffffffffffffffffffffffffffffffff60608160408a015116980151166001880180981161158057620f42408101918282116115535788810298818a04149015171561158057620f423f908282820111611553578801018097116115265780156114f957604051967fb3d7f6b900000000000000000000000000000000000000000000000000000000885204600487015260208660248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000d4fa2d31b7968e448877f69a96de69f5de8cd23e165afa9586156114ee5788966114b0575b50611431926040949273ffffffffffffffffffffffffffffffffffffffff6fffffffffffffffffffffffffffffffff6020611457956113a78c33610f33565b5001511697875198899788977f58e83ce0000000000000000000000000000000000000000000000000000000008952600489019060a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b8c60c488015260e487015216610104850152610140610124850152610144840191610a17565b03818773ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000de6e08ac208088cc62812ba30608d852c6b0ecbc165af19384156114a45793610ba557509190565b604051903d90823e3d90fd5b909550602093919293813d6020116114e6575b816114d060209383610637565b8101031261010357519490929190611431611368565b3d91506114c3565b6040513d8a823e3d90fd5b6024897f4e487b710000000000000000000000000000000000000000000000000000000081526012600452fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60248b7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b60248a7f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b90955060e0813d60e01161166c575b816115c960e09383610637565b81010312611668576040519060e0820182811067ffffffffffffffff8211176106785761165c9160c0916040526115ff81610ff9565b845261160d60208201610ff9565b602085015261161e60408201610ff9565b604085015261162f60608201610ff9565b606085015261164060808201610ff9565b608085015261165160a08201610ff9565b60a085015201610ff9565b60c0820152945f611271565b8780fd5b3d91506115bc565b5050505050508190565b90506060813d6060116116eb575b8161169960609383610637565b8101031261166857604051906060820182811067ffffffffffffffff821117610678576116e0916040918252805184526116d560208201610ff9565b602085015201610ff9565b60408201525f6111db565b3d915061168c565b90965067ffffffffffffffff8111610678576040525f955f611145565b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110f0565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b91909273ffffffffffffffffffffffffffffffffffffffff80931692833b156118e157604051928160208501967f23b872dd00000000000000000000000000000000000000000000000000000000885216602485015216604483015260648201526064815260a081019167ffffffffffffffff9382841085851117610678575f809493819460405251925af13d156118d8573d918211610678576040519161183c60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8401160184610637565b82523d5f602084013e5b156118ae57805190811515918261188a575b505061186057565b60046040517f1eded19c000000000000000000000000000000000000000000000000000000008152fd5b81925090602091810103126101035760200151801590811503610103575f80611858565b60046040517fe65b7a77000000000000000000000000000000000000000000000000000000008152fd5b60609150611846565b60046040517ff046a714000000000000000000000000000000000000000000000000000000008152fd
Verified Source Code Partial Match
Compiler: v0.8.22+commit.4fc1097e
EVM: shanghai
Optimization: Yes (999999 runs)
Helper.sol 163 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IMorpho, IMorphoCredit, Id, Market, Position} from "./interfaces/IMorpho.sol";
import {MarketParams} from "./interfaces/IMorpho.sol";
import {IHelper} from "./interfaces/IHelper.sol";
import {IUSD3} from "./interfaces/IUSD3.sol";
import {IERC20} from "./interfaces/IERC20.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {SafeTransferLib} from "./libraries/SafeTransferLib.sol";
import {SharesMathLib} from "./libraries/SharesMathLib.sol";
import {MarketParamsLib} from "./libraries/MarketParamsLib.sol";
import {IERC4626} from "../lib/forge-std/src/interfaces/IERC4626.sol";
/// @title Helper
/// @author 3Jane
/// @custom:contact [email protected]
contract Helper is IHelper {
using SafeTransferLib for IERC20;
using SharesMathLib for uint256;
using MarketParamsLib for MarketParams;
/// @inheritdoc IHelper
address public immutable MORPHO;
/// @inheritdoc IHelper
address public immutable USD3;
/// @inheritdoc IHelper
address public immutable sUSD3;
/// @inheritdoc IHelper
address public immutable USDC;
/// @inheritdoc IHelper
address public immutable WAUSDC;
/* CONSTRUCTOR */
constructor(address morpho, address usd3, address susd3, address usdc, address wausdc) {
if (morpho == address(0)) revert ErrorsLib.ZeroAddress();
if (usd3 == address(0)) revert ErrorsLib.ZeroAddress();
if (susd3 == address(0)) revert ErrorsLib.ZeroAddress();
if (usdc == address(0)) revert ErrorsLib.ZeroAddress();
if (wausdc == address(0)) revert ErrorsLib.ZeroAddress();
MORPHO = morpho;
USD3 = usd3;
sUSD3 = susd3;
USDC = usdc;
WAUSDC = wausdc;
// Set max approvals
IERC20(USDC).approve(WAUSDC, type(uint256).max);
IERC20(USDC).approve(USD3, type(uint256).max);
IERC20(WAUSDC).approve(MORPHO, type(uint256).max);
IERC20(USD3).approve(sUSD3, type(uint256).max);
}
/// @inheritdoc IHelper
function deposit(uint256 assets, address receiver, bool hop, bytes32 referral) external returns (uint256 shares) {
(shares) = deposit(assets, receiver, hop);
emit DepositReferred(msg.sender, assets, referral);
}
/// @inheritdoc IHelper
function deposit(uint256 assets, address receiver, bool hop) public returns (uint256) {
if (msg.sender != receiver) revert ErrorsLib.Unauthorized();
IERC20(USDC).safeTransferFrom(msg.sender, address(this), assets);
if (hop) {
require(IUSD3(USD3).availableDepositLimit(receiver) >= assets, "Deposit exceeds limit");
uint256 usd3Shares = IUSD3(USD3).deposit(assets, address(this));
return IERC4626(sUSD3).deposit(usd3Shares, receiver);
} else {
return IUSD3(USD3).deposit(assets, receiver);
}
}
/// @inheritdoc IHelper
function redeem(uint256 shares, address receiver) external returns (uint256) {
uint256 usdcAssets = IUSD3(USD3).redeem(shares, receiver, msg.sender);
return usdcAssets;
}
/// @inheritdoc IHelper
function borrow(MarketParams memory marketParams, uint256 assets, bytes32 referral)
external
returns (uint256 amount, uint256 shares)
{
(amount, shares) = borrow(marketParams, assets);
emit BorrowReferred(msg.sender, amount, referral);
}
/// @inheritdoc IHelper
function borrow(MarketParams memory marketParams, uint256 assets) public returns (uint256, uint256) {
uint256 waUsdcShares = IERC4626(WAUSDC).convertToShares(assets);
(uint256 waUSDCAmount, uint256 shares) =
IMorpho(MORPHO).borrow(marketParams, waUsdcShares, 0, msg.sender, address(this));
uint256 usdcAmount = IERC4626(WAUSDC).redeem(waUSDCAmount, msg.sender, address(this));
return (usdcAmount, shares);
}
/// @inheritdoc IHelper
function repay(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
external
returns (uint256, uint256)
{
// Check if this is a full repayment request
if (assets == type(uint256).max) {
return _repayFull(marketParams, onBehalf, data);
} else {
// Normal partial repayment flow
uint256 waUSDCAmount = _wrap(msg.sender, assets);
(, uint256 shares) = IMorpho(MORPHO).repay(marketParams, waUSDCAmount, 0, onBehalf, data);
return (assets, shares);
}
}
function _repayFull(MarketParams memory marketParams, address onBehalf, bytes calldata data)
internal
returns (uint256, uint256)
{
Id id = marketParams.id();
// Accrue premium first to get accurate borrow shares
_accruePremiumsForBorrower(id, onBehalf);
// Get current borrow shares after premium accrual
Position memory pos = IMorpho(MORPHO).position(id, onBehalf);
// If no debt, return early
if (pos.borrowShares == 0) {
return (0, 0);
}
// Get market state to calculate assets needed
Market memory market = IMorpho(MORPHO).market(id);
// Calculate waUSDC assets needed (rounds up like Morpho does)
uint256 waUsdcNeeded = uint256(pos.borrowShares).toAssetsUp(market.totalBorrowAssets, market.totalBorrowShares);
// Convert to USDC amount needed (preview how much USDC needed to mint waUsdcNeeded)
uint256 usdcNeeded = IERC4626(WAUSDC).previewMint(waUsdcNeeded);
// Pull USDC from user and wrap to waUSDC
_wrap(msg.sender, usdcNeeded);
// Repay with shares to ensure complete repayment
(, uint256 sharesRepaid) = IMorpho(MORPHO).repay(marketParams, 0, pos.borrowShares, onBehalf, data);
return (usdcNeeded, sharesRepaid);
}
function _wrap(address from, uint256 assets) internal returns (uint256) {
IERC20(USDC).safeTransferFrom(from, address(this), assets);
return IERC4626(WAUSDC).deposit(assets, address(this));
}
function _accruePremiumsForBorrower(Id id, address borrower) internal {
address[] memory borrowers = new address[](1);
borrowers[0] = borrower;
IMorphoCredit(MORPHO).accruePremiumsForBorrowers(id, borrowers);
}
}
IMorpho.sol 432 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;
type Id is bytes32;
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
address creditLine;
}
/// @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.
/// @dev Warning: `totalMarkdownAmount` may be stale as markdowns are only updated when borrowers are touched.
struct Market {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
uint128 totalMarkdownAmount; // Running tally of all borrower markdowns
}
/// @notice Per-borrower premium tracking
/// @param lastAccrualTime Timestamp of the last premium accrual for this borrower
/// @param rate Current risk premium rate per second (scaled by WAD)
/// @param borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
struct BorrowerPremium {
uint128 lastAccrualTime;
uint128 rate;
uint128 borrowAssetsAtLastAccrual;
}
/// @notice Repayment tracking structures
enum RepaymentStatus {
Current,
GracePeriod,
Delinquent,
Default
}
struct PaymentCycle {
uint256 endDate;
}
struct RepaymentObligation {
uint128 paymentCycleId;
uint128 amountDue;
uint128 endingBalance;
}
/// @notice Markdown state for tracking defaulted debt value reduction
/// @param lastCalculatedMarkdown Last calculated markdown amount
struct MarkdownState {
uint128 lastCalculatedMarkdown;
}
struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
/// @notice The EIP-712 domain separator.
/// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
/// same chain id and on forks because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice The owner of the contract.
/// @dev It has the power to change the owner.
/// @dev It has the power to set fees on markets and set the fee recipient.
/// @dev It has the power to enable but not disable IRMs and LLTVs.
function owner() external view returns (address);
/// @notice The fee recipient of all markets.
/// @dev The recipient receives the fees of a given market through a supply position on that market.
function feeRecipient() external view returns (address);
/// @notice Whether the `irm` is enabled.
function isIrmEnabled(address irm) external view returns (bool);
/// @notice Whether the `lltv` is enabled.
function isLltvEnabled(uint256 lltv) external view returns (bool);
/// @notice 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 assumptions on the market's dependencies which, if broken, could break Morpho's liveness
/// properties (funds could get stuck):
/// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
/// - The amount of assets supplied and borrowed should not go above ~1e35 (otherwise the computation of
/// `toSharesUp` and `toSharesDown` can overflow).
/// - The IRM should not revert on `borrowRate`.
/// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
/// `_accrueInterest` can overflow).
/// - The oracle should not revert `price`.
/// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
/// `assetsRepaid` in `liquidate` can overflow).
/// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
/// the point where `totalBorrowShares` is very large and borrowing overflows.
function createMarket(MarketParams memory marketParams) external;
/// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupply` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
/// amount of shares is given for full compatibility and precision.
/// @dev Supplying a large amount can revert for overflow.
/// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to supply assets to.
/// @param assets The amount of assets to supply.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased supply position.
/// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
/// @return assetsSupplied The amount of assets supplied.
/// @return sharesSupplied The amount of shares minted.
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
/// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
/// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
/// conversion roundings between shares and assets.
/// @param marketParams The market to withdraw assets from.
/// @param assets The amount of assets to withdraw.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the supply position.
/// @param receiver The address that will receive the withdrawn assets.
/// @return assetsWithdrawn The amount of assets withdrawn.
/// @return sharesWithdrawn The amount of shares burned.
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
/// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
/// given for full compatibility and precision.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Borrowing a large amount can revert for overflow.
/// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to borrow assets from.
/// @param assets The amount of assets to borrow.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased borrow position.
/// @param receiver The address that will receive the borrowed assets.
/// @return assetsBorrowed The amount of assets borrowed.
/// @return sharesBorrowed The amount of shares minted.
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
/// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoRepay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice 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,
uint128 totalMarkdownAmount
);
/// @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,
address creditLine
);
}
/// @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);
}
/// @title IMorphoCredit
/// @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 IMorphoCredit {
/// @notice The helper of the contract.
function helper() external view returns (address);
/// @notice The usd3 contract
function usd3() external view returns (address);
/// @notice The protocol config of the contract.
function protocolConfig() external view returns (address);
/// @notice Sets `helper` as `helper` of the contract.
/// @param newHelper The new helper address
function setHelper(address newHelper) external;
/// @notice Sets `usd3` as `usd3` of the contract.
/// @param newUsd3 The new usd3 address
function setUsd3(address newUsd3) external;
/// @notice Sets the credit line and premium rate for a borrower
/// @param id The market ID
/// @param borrower The borrower address
/// @param credit The credit line amount
/// @param drp The drp per second in WAD
function setCreditLine(Id id, address borrower, uint256 credit, uint128 drp) external;
/// @notice Returns the premium data for a specific borrower in a market
/// @param id The market ID
/// @param borrower The borrower address
/// @return lastAccrualTime Timestamp of the last premium accrual
/// @return rate Current risk premium rate per second (scaled by WAD)
/// @return borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
function borrowerPremium(Id id, address borrower)
external
view
returns (uint128 lastAccrualTime, uint128 rate, uint128 borrowAssetsAtLastAccrual);
/// @notice Batch accrue premiums for multiple borrowers
/// @param id Market ID
/// @param borrowers Array of borrower addresses
/// @dev Gas usage scales linearly with array size. Callers should manage batch sizes based on block gas limits.
function accruePremiumsForBorrowers(Id id, address[] calldata borrowers) external;
/// @notice Close a payment cycle and post obligations for multiple borrowers
/// @param id Market ID
/// @param endDate Cycle end date
/// @param borrowers Array of borrower addresses
/// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
/// @param endingBalances Array of ending balances for penalty calculations
function closeCycleAndPostObligations(
Id id,
uint256 endDate,
address[] calldata borrowers,
uint256[] calldata repaymentBps,
uint256[] calldata endingBalances
) external;
/// @notice Add obligations to the latest payment cycle
/// @param id Market ID
/// @param borrowers Array of borrower addresses
/// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
/// @param endingBalances Array of ending balances
function addObligationsToLatestCycle(
Id id,
address[] calldata borrowers,
uint256[] calldata repaymentBps,
uint256[] calldata endingBalances
) external;
/// @notice Get repayment obligation for a borrower
/// @param id Market ID
/// @param borrower Borrower address
/// @return cycleId The payment cycle ID
/// @return amountDue The amount due
/// @return endingBalance The ending balance for penalty calculations
function repaymentObligation(Id id, address borrower)
external
view
returns (uint128 cycleId, uint128 amountDue, uint128 endingBalance);
/// @notice Get payment cycle end date
/// @param id Market ID
/// @param cycleId Cycle ID
/// @return endDate The cycle end date
function paymentCycle(Id id, uint256 cycleId) external view returns (uint256 endDate);
/// @notice Settle a borrower's account by writing off all remaining debt
/// @dev Only callable by credit line contract
/// @dev Should be called after any partial repayments have been made
/// @param marketParams The market parameters
/// @param borrower The borrower whose account to settle
/// @return writtenOffAssets Amount of assets written off
/// @return writtenOffShares Amount of shares written off
function settleAccount(MarketParams memory marketParams, address borrower)
external
returns (uint256 writtenOffAssets, uint256 writtenOffShares);
/// @notice Get markdown state for a borrower
/// @param id Market ID
/// @param borrower Borrower address
/// @return lastCalculatedMarkdown Last calculated markdown amount
function markdownState(Id id, address borrower) external view returns (uint128 lastCalculatedMarkdown);
}
IHelper.sol 95 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IERC4626} from "../../lib/forge-std/src/interfaces/IERC4626.sol";
import {MarketParams} from "./IMorpho.sol";
/// @title IHelper
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper contract to simplify interactions with 3Jane's token ecosystem and Morpho protocol
/// @dev This contract handles token conversions for USD3 (post-reinitialize) and MorphoCredit operations
/// @dev After USD3's reinitialize(), the flow is: USDC → USD3 (→ sUSD3 if hop=true)
/// @dev MorphoCredit still uses waUSDC, so borrow/repay operations handle the USDC ↔ waUSDC conversion
interface IHelper {
event DepositReferred(address indexed depositor, uint256 amount, bytes32 code);
event BorrowReferred(address indexed borrower, uint256 amount, bytes32 code);
/// @notice The Morpho protocol contract address
/// @return The address of the Morpho contract
function MORPHO() external view returns (address);
/// @notice The USD3 token address (ERC4626 vault, accepts USDC directly after reinitialize)
/// @return The address of the USD3 token
function USD3() external view returns (address);
/// @notice The sUSD3 token address (subordinate/staked USD3, also ERC4626)
/// @return The address of the sUSD3 token
function sUSD3() external view returns (address);
/// @notice The USDC token address (base stablecoin)
/// @return The address of the USDC token
function USDC() external view returns (address);
/// @notice The waUSDC token address (wrapped asset USDC, ERC4626 vault)
/// @return The address of the waUSDC token
function WAUSDC() external view returns (address);
/// @notice Deposits USDC into USD3 (and optionally sUSD3)
/// @dev After USD3's reinitialize(), flow is: USDC → USD3 (→ sUSD3 if hop=true)
/// @dev USD3 handles USDC directly and manages waUSDC wrapping internally
/// @param assets The amount of USDC to deposit
/// @param receiver The address that will receive the USD3/sUSD3 shares
/// @param hop If true, deposits into sUSD3; if false, stops at USD3
/// @return The amount of shares minted (USD3 or sUSD3 depending on hop)
function deposit(uint256 assets, address receiver, bool hop) external returns (uint256);
/// @notice Deposits USDC into USD3 (and optionally sUSD3) with referral tracking
/// @dev Same as deposit() but emits DepositReferred event for referral tracking
/// @param assets The amount of USDC to deposit
/// @param receiver The address that will receive the USD3/sUSD3 shares
/// @param hop If true, deposits into sUSD3; if false, stops at USD3
/// @param referral Referral code for tracking purposes
/// @return The amount of shares minted (USD3 or sUSD3 depending on hop)
function deposit(uint256 assets, address receiver, bool hop, bytes32 referral) external returns (uint256);
/// @notice Redeems USD3 shares back to USDC
/// @dev After USD3's reinitialize(), USD3 returns USDC directly
/// @dev Caller must have approved Helper for USD3 spending
/// @param shares The amount of USD3 shares to redeem
/// @param receiver The address that will receive the USDC
/// @return The amount of USDC received
function redeem(uint256 shares, address receiver) external returns (uint256);
/// @notice Borrows assets from a Morpho market and unwraps to USDC
/// @dev The borrowed waUSDC is automatically unwrapped to USDC for the borrower
/// @param marketParams The market parameters defining which market to borrow from
/// @param assets The amount of assets to borrow (in waUSDC terms)
/// @return usdcAmount The amount of USDC received by the borrower
/// @return shares The amount of borrow shares created
function borrow(MarketParams memory marketParams, uint256 assets) external returns (uint256, uint256);
/// @notice Borrows assets from a Morpho market and unwraps to USDC with referral tracking
/// @dev Same as borrow() but emits BorrowReferred event for referral tracking
/// @param marketParams The market parameters defining which market to borrow from
/// @param assets The amount of assets to borrow (in waUSDC terms)
/// @param referral Referral code for tracking purposes
/// @return usdcAmount The amount of USDC received by the borrower
/// @return shares The amount of borrow shares created
function borrow(MarketParams memory marketParams, uint256 assets, bytes32 referral)
external
returns (uint256, uint256);
/// @notice Repays a loan by wrapping USDC to waUSDC
/// @dev Flow: USDC → waUSDC → Morpho repay. Caller must have approved Helper for USDC spending
/// @param marketParams The market parameters defining which market to repay
/// @param assets The amount of USDC to repay
/// @param onBehalf The address whose debt is being repaid
/// @param data Additional data for the repay operation (e.g., for callbacks)
/// @return usdcAmount The amount of USDC used from the caller
/// @return shares The amount of borrow shares repaid
function repay(MarketParams memory marketParams, uint256 assets, address onBehalf, bytes calldata data)
external
returns (uint256, uint256);
}
IUSD3.sol 17 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
import {IERC4626} from "../../lib/forge-std/src/interfaces/IERC4626.sol";
/// @title IUSD3
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface for USD3 token, which is an ERC4626 vault
interface IUSD3 is IERC4626 {
// USD3 inherits all ERC4626 functions including:
// - deposit(uint256 assets, address receiver) returns (uint256 shares)
// - redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)
// - And all other ERC4626 standard functions
function whitelist(address user) external view returns (bool);
function availableDepositLimit(address owner) external view returns (uint256);
}
IERC20.sol 11 lines
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title IERC20 /// @author Morpho Labs /// @custom:contact [email protected] /// @dev Empty because we only call library functions. It prevents calling transfer (transferFrom) instead of /// safeTransfer (safeTransferFrom). interface IERC20 { function approve(address spender, uint256 value) external returns (bool); }
ErrorsLib.sol 146 lines
// 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 custom errors. library ErrorsLib { /// @notice Thrown when the caller is not the owner. error NotOwner(); /// @notice Thrown when the caller is not the market's credit line. error NotCreditLine(); /// @notice Thrown when the caller is not the market's helper. error NotHelper(); /// @notice Thrown when the caller is not the market's usd3. error NotUsd3(); /// @notice Thrown when the caller is not the owner or ozd. error NotOwnerOrOzd(); /// @notice Thrown when the user is unverified. error Unverified(); /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV. error MaxLltvExceeded(); /// @notice Thrown when the LTV to enable exceeds the maximum LTV. error MaxLtvExceeded(); /// @notice Thrown when the VV to enable exceeds the maximum VV. error MaxVvExceeded(); /// @notice Thrown when the credit to enable exceeds the maximum credit. error MaxCreditLineExceeded(); /// @notice Thrown when the credit to enable is below the minimum credit. error MinCreditLineExceeded(); /// @notice Thrown when the fee to set exceeds the maximum fee. error MaxFeeExceeded(); /// @notice Thrown when the value is already set. error AlreadySet(); /// @notice Thrown when the IRM is not enabled at market creation. error IrmNotEnabled(); /// @notice Thrown when the LLTV is not enabled at market creation. error LltvNotEnabled(); /// @notice Thrown when the market is already created. error MarketAlreadyCreated(); /// @notice Thrown when a token to transfer doesn't have code. error NoCode(); /// @notice Thrown when the market is not created. error MarketNotCreated(); /// @notice Thrown when not exactly one of the input amount is zero. error InconsistentInput(); /// @notice Thrown when zero assets is passed as input. error ZeroAssets(); /// @notice Thrown when a zero address is passed as input. error ZeroAddress(); /// @notice Thrown when an array has an invalid length. error InvalidArrayLength(); /// @notice Thrown when the caller is not authorized to conduct an action. error Unauthorized(); /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`. error InsufficientCollateral(); /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`. error InsufficientLiquidity(); /// @notice Thrown when borrowing shares would result in borrowing zero assets. error InsufficientBorrowAmount(); /// @notice Thrown when a token transfer reverted. error TransferReverted(); /// @notice Thrown when a token transfer returned false. error TransferReturnedFalse(); /// @notice Thrown when a token transferFrom reverted. error TransferFromReverted(); /// @notice Thrown when a token transferFrom returned false error TransferFromReturnedFalse(); /// @notice Thrown when the maximum uint128 is exceeded. error MaxUint128Exceeded(); /// @notice Thrown when the premium rate exceeds the maximum allowed. error MaxDrpExceeded(); /// @notice Thrown when the borrower has outstanding repayment obligations. error OutstandingRepayment(); /// @notice Thrown when the protocol is paused. error Paused(); /// @notice Thrown when trying to close a future cycle. error CannotCloseFutureCycle(); /// @notice Thrown when cycle duration is invalid. error InvalidCycleDuration(); /// @notice Thrown when no payment cycles exist. error NoCyclesExist(); /// @notice Thrown when cycle ID is invalid. error InvalidCycleId(); /// @notice Thrown when partial payment is attempted but full obligation payment is required. error MustPayFullObligation(); /// @notice Thrown when repayment basis points exceed 100%. error RepaymentExceedsHundredPercent(); /// @notice Thrown when an invalid markdown manager is set. error InvalidMarkdownManager(); /// @notice Thrown when trying to settle non-existent debt. error NoAccountToSettle(); /// @notice Thrown when the cover amount exceeds the assets amount. error InvalidCoverAmount(); /// @notice Thrown when attempting operations on a frozen market. error MarketFrozen(); /// @notice Thrown when a borrow would exceed the protocol debt cap. error DebtCapExceeded(); /// @notice Thrown when borrow or repay would result in debt below minimum borrow amount. error BelowMinimumBorrow(); }
SafeTransferLib.sol 36 lines
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IERC20} from "../interfaces/IERC20.sol";
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
interface IERC20Internal {
function transfer(address to, uint256 value) external returns (bool);
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
/// @title SafeTransferLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to manage transfers of tokens, even if calls to the transfer or transferFrom functions are not
/// returning a boolean.
library SafeTransferLib {
function safeTransfer(IERC20 token, address to, uint256 value) internal {
if (address(token).code.length == 0) revert ErrorsLib.NoCode();
(bool success, bytes memory returndata) =
address(token).call(abi.encodeCall(IERC20Internal.transfer, (to, value)));
if (!success) revert ErrorsLib.TransferReverted();
if (returndata.length != 0 && !abi.decode(returndata, (bool))) revert ErrorsLib.TransferReturnedFalse();
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
if (address(token).code.length == 0) revert ErrorsLib.NoCode();
(bool success, bytes memory returndata) =
address(token).call(abi.encodeCall(IERC20Internal.transferFrom, (from, to, value)));
if (!success) revert ErrorsLib.TransferFromReverted();
if (returndata.length != 0 && !abi.decode(returndata, (bool))) revert ErrorsLib.TransferFromReturnedFalse();
}
}
SharesMathLib.sol 45 lines
// 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);
}
}
MarketParamsLib.sol 21 lines
// 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 6 * 32 because `MarketParams` has 6 variables of 32 bytes each.
uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 6 * 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)
}
}
}
IERC4626.sol 190 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import {IERC20} from "./IERC20.sol";
/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
/// https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 is IERC20 {
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
);
/// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
/// @dev
/// - MUST be an ERC-20 token contract.
/// - MUST NOT revert.
function asset() external view returns (address assetTokenAddress);
/// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
/// @dev
/// - 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);
/// @notice 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.
/// @dev
/// - 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);
/// @notice 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.
/// @dev
/// - 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);
/// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
/// through a deposit call.
/// @dev
/// - 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);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
/// current on-chain conditions.
/// @dev
/// - 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);
/// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
/// @dev
/// - 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);
/// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
/// @dev
/// - 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);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
/// current on-chain conditions.
/// @dev
/// - 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);
/// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
/// @dev
/// - 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);
/// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
/// Vault, through a withdrawal call.
/// @dev
/// - 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);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
/// given current on-chain conditions.
/// @dev
/// - 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);
/// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
/// @dev
/// - 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 withdrawal.
/// - 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);
/// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
/// through a redeem call.
/// @dev
/// - 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);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
/// given current on-chain conditions.
/// @dev
/// - 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);
/// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
/// @dev
/// - 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);
}
MathLib.sol 75 lines
// 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; } /// @dev Computes the inverse of wTaylorCompounded, finding the rate that produces the given growth factor. /// Uses a 3-term Taylor series approximation of ln(x) to solve for rate in the compound interest formula. /// Formula: rate = ln(x) / n ≈ [(x-1) - (x-1)²/2 + (x-1)³/3] / n /// /// Accuracy notes: /// - The Taylor approximation of ln(x) is most accurate for x close to 1 /// - At growth factor x = 1.69*WAD (69% growth), approximation error < 2% /// - At growth factor x = 2*WAD (100% growth, where ln(2) ≈ 0.69), approximation error < 5% /// - Accuracy decreases for larger growth factors; not recommended for x > 2.5*WAD (150% growth) /// /// Example: If debt grew from 1000 to 1105 over 1 year (10.5% growth): /// - x = 1.105*WAD (growth factor) /// - n = 365 days (time period) /// - Returns ~10% APR as rate per second /// /// @param x The growth factor scaled by WAD (e.g., 1.1*WAD for 10% growth). Must be >= WAD. /// @param n The time period over which the growth occurred (in seconds) /// @return The continuously compounded rate per second that would produce this growth (scaled by WAD) function wInverseTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { require(x >= WAD, "ln undefined"); uint256 firstTerm = x - WAD; uint256 secondTerm = wMulDown(firstTerm, firstTerm); uint256 thirdTerm = wMulDown(secondTerm, firstTerm); uint256 series = firstTerm - secondTerm / 2 + thirdTerm / 3; return series / n; } }
IERC20.sol 43 lines
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
/// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
event Transfer(address indexed from, address indexed to, uint256 value);
/// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
/// is the new allowance.
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice Returns the amount of tokens in existence.
function totalSupply() external view returns (uint256);
/// @notice Returns the amount of tokens owned by `account`.
function balanceOf(address account) external view returns (uint256);
/// @notice Moves `amount` tokens from the caller's account to `to`.
function transfer(address to, uint256 amount) external returns (bool);
/// @notice Returns the remaining number of tokens that `spender` is allowed
/// to spend on behalf of `owner`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
/// `amount` is then deducted from the caller's allowance.
function transferFrom(address from, address to, uint256 amount) external returns (bool);
/// @notice Returns the name of the token.
function name() external view returns (string memory);
/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
/// @notice Returns the decimals places of the token.
function decimals() external view returns (uint8);
}
Read Contract
MORPHO 0x3acb5624 → address
USD3 0xfcd72b86 → address
USDC 0x89a30271 → address
WAUSDC 0xcb6d71bc → address
sUSD3 0xf7f6ba53 → address
Write Contract 6 functions
These functions modify contract state and require a wallet transaction to execute.
borrow 0x5a36289e
tuple marketParams
uint256 assets
bytes32 referral
returns: uint256, uint256
borrow 0x0011c388
tuple marketParams
uint256 assets
returns: uint256, uint256
deposit 0x83df6747
uint256 assets
address receiver
bool hop
returns: uint256
deposit 0xf866af0f
uint256 assets
address receiver
bool hop
bytes32 referral
returns: uint256
redeem 0x7bde82f2
uint256 shares
address receiver
returns: uint256
repay 0x118df897
tuple marketParams
uint256 assets
address onBehalf
bytes data
returns: uint256, uint256
Top Interactions
| Address | Txns | Sent | Received |
|---|---|---|---|
| 0xB93D314D...8d19 | 1 | 1 | |
| 0xFf2C13A2...EfdE | 1 | 1 |
Token Balances (1)
View Transfers →Recent Transactions
|
| Hash | Block | Age | From/To | Value | |
|---|---|---|---|---|---|
| 0x1d956148...08ebf7 | 24,437,333 | IN | 0xB93D314D...8d19 | 0 ETH | |
| 0x00d2aa06...a9ac6b | 24,437,331 | IN | 0xFf2C13A2...EfdE | 0 ETH |