// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.19;
// Common.sol
//
// Common mathematical functions needed by both SD59x18 and UD60x18. Note that these global functions do not
// always operate with SD59x18 and UD60x18 numbers.
/*//////////////////////////////////////////////////////////////////////////
CUSTOM ERRORS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Thrown when the resultant value in {mulDiv} overflows uint256.
error PRBMath_MulDiv_Overflow(uint256 x, uint256 y, uint256 denominator);
/// @notice Thrown when the resultant value in {mulDiv18} overflows uint256.
error PRBMath_MulDiv18_Overflow(uint256 x, uint256 y);
/// @notice Thrown when one of the inputs passed to {mulDivSigned} is `type(int256).min`.
error PRBMath_MulDivSigned_InputTooSmall();
/// @notice Thrown when the resultant value in {mulDivSigned} overflows int256.
error PRBMath_MulDivSigned_Overflow(int256 x, int256 y);
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
/// @dev The maximum value a uint128 number can have.
uint128 constant MAX_UINT128 = type(uint128).max;
/// @dev The maximum value a uint40 number can have.
uint40 constant MAX_UINT40 = type(uint40).max;
/// @dev The unit number, which the decimal precision of the fixed-point types.
uint256 constant UNIT = 1e18;
/// @dev The unit number inverted mod 2^256.
uint256 constant UNIT_INVERSE = 78156646155174841979727994598816262306175212592076161876661_508869554232690281;
/// @dev The the largest power of two that divides the decimal value of `UNIT`. The logarithm of this value is the least significant
/// bit in the binary representation of `UNIT`.
uint256 constant UNIT_LPOTD = 262144;
/*//////////////////////////////////////////////////////////////////////////
FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
/// @notice Calculates the binary exponent of x using the binary fraction method.
/// @dev Has to use 192.64-bit fixed-point numbers. See https://ethereum.stackexchange.com/a/96594/24693.
/// @param x The exponent as an unsigned 192.64-bit fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function exp2(uint256 x) pure returns (uint256 result) {
unchecked {
// Start from 0.5 in the 192.64-bit fixed-point format.
result = 0x800000000000000000000000000000000000000000000000;
// The following logic multiplies the result by $\sqrt{2^{-i}}$ when the bit at position i is 1. Key points:
//
// 1. Intermediate results will not overflow, as the starting point is 2^191 and all magic factors are under 2^65.
// 2. The rationale for organizing the if statements into groups of 8 is gas savings. If the result of performing
// a bitwise AND operation between x and any value in the array [0x80; 0x40; 0x20; 0x10; 0x08; 0x04; 0x02; 0x01] is 1,
// we know that `x & 0xFF` is also 1.
if (x & 0xFF00000000000000 > 0) {
if (x & 0x8000000000000000 > 0) {
result = (result * 0x16A09E667F3BCC909) >> 64;
}
if (x & 0x4000000000000000 > 0) {
result = (result * 0x1306FE0A31B7152DF) >> 64;
}
if (x & 0x2000000000000000 > 0) {
result = (result * 0x1172B83C7D517ADCE) >> 64;
}
if (x & 0x1000000000000000 > 0) {
result = (result * 0x10B5586CF9890F62A) >> 64;
}
if (x & 0x800000000000000 > 0) {
result = (result * 0x1059B0D31585743AE) >> 64;
}
if (x & 0x400000000000000 > 0) {
result = (result * 0x102C9A3E778060EE7) >> 64;
}
if (x & 0x200000000000000 > 0) {
result = (result * 0x10163DA9FB33356D8) >> 64;
}
if (x & 0x100000000000000 > 0) {
result = (result * 0x100B1AFA5ABCBED61) >> 64;
}
}
if (x & 0xFF000000000000 > 0) {
if (x & 0x80000000000000 > 0) {
result = (result * 0x10058C86DA1C09EA2) >> 64;
}
if (x & 0x40000000000000 > 0) {
result = (result * 0x1002C605E2E8CEC50) >> 64;
}
if (x & 0x20000000000000 > 0) {
result = (result * 0x100162F3904051FA1) >> 64;
}
if (x & 0x10000000000000 > 0) {
result = (result * 0x1000B175EFFDC76BA) >> 64;
}
if (x & 0x8000000000000 > 0) {
result = (result * 0x100058BA01FB9F96D) >> 64;
}
if (x & 0x4000000000000 > 0) {
result = (result * 0x10002C5CC37DA9492) >> 64;
}
if (x & 0x2000000000000 > 0) {
result = (result * 0x1000162E525EE0547) >> 64;
}
if (x & 0x1000000000000 > 0) {
result = (result * 0x10000B17255775C04) >> 64;
}
}
if (x & 0xFF0000000000 > 0) {
if (x & 0x800000000000 > 0) {
result = (result * 0x1000058B91B5BC9AE) >> 64;
}
if (x & 0x400000000000 > 0) {
result = (result * 0x100002C5C89D5EC6D) >> 64;
}
if (x & 0x200000000000 > 0) {
result = (result * 0x10000162E43F4F831) >> 64;
}
if (x & 0x100000000000 > 0) {
result = (result * 0x100000B1721BCFC9A) >> 64;
}
if (x & 0x80000000000 > 0) {
result = (result * 0x10000058B90CF1E6E) >> 64;
}
if (x & 0x40000000000 > 0) {
result = (result * 0x1000002C5C863B73F) >> 64;
}
if (x & 0x20000000000 > 0) {
result = (result * 0x100000162E430E5A2) >> 64;
}
if (x & 0x10000000000 > 0) {
result = (result * 0x1000000B172183551) >> 64;
}
}
if (x & 0xFF00000000 > 0) {
if (x & 0x8000000000 > 0) {
result = (result * 0x100000058B90C0B49) >> 64;
}
if (x & 0x4000000000 > 0) {
result = (result * 0x10000002C5C8601CC) >> 64;
}
if (x & 0x2000000000 > 0) {
result = (result * 0x1000000162E42FFF0) >> 64;
}
if (x & 0x1000000000 > 0) {
result = (result * 0x10000000B17217FBB) >> 64;
}
if (x & 0x800000000 > 0) {
result = (result * 0x1000000058B90BFCE) >> 64;
}
if (x & 0x400000000 > 0) {
result = (result * 0x100000002C5C85FE3) >> 64;
}
if (x & 0x200000000 > 0) {
result = (result * 0x10000000162E42FF1) >> 64;
}
if (x & 0x100000000 > 0) {
result = (result * 0x100000000B17217F8) >> 64;
}
}
if (x & 0xFF000000 > 0) {
if (x & 0x80000000 > 0) {
result = (result * 0x10000000058B90BFC) >> 64;
}
if (x & 0x40000000 > 0) {
result = (result * 0x1000000002C5C85FE) >> 64;
}
if (x & 0x20000000 > 0) {
result = (result * 0x100000000162E42FF) >> 64;
}
if (x & 0x10000000 > 0) {
result = (result * 0x1000000000B17217F) >> 64;
}
if (x & 0x8000000 > 0) {
result = (result * 0x100000000058B90C0) >> 64;
}
if (x & 0x4000000 > 0) {
result = (result * 0x10000000002C5C860) >> 64;
}
if (x & 0x2000000 > 0) {
result = (result * 0x1000000000162E430) >> 64;
}
if (x & 0x1000000 > 0) {
result = (result * 0x10000000000B17218) >> 64;
}
}
if (x & 0xFF0000 > 0) {
if (x & 0x800000 > 0) {
result = (result * 0x1000000000058B90C) >> 64;
}
if (x & 0x400000 > 0) {
result = (result * 0x100000000002C5C86) >> 64;
}
if (x & 0x200000 > 0) {
result = (result * 0x10000000000162E43) >> 64;
}
if (x & 0x100000 > 0) {
result = (result * 0x100000000000B1721) >> 64;
}
if (x & 0x80000 > 0) {
result = (result * 0x10000000000058B91) >> 64;
}
if (x & 0x40000 > 0) {
result = (result * 0x1000000000002C5C8) >> 64;
}
if (x & 0x20000 > 0) {
result = (result * 0x100000000000162E4) >> 64;
}
if (x & 0x10000 > 0) {
result = (result * 0x1000000000000B172) >> 64;
}
}
if (x & 0xFF00 > 0) {
if (x & 0x8000 > 0) {
result = (result * 0x100000000000058B9) >> 64;
}
if (x & 0x4000 > 0) {
result = (result * 0x10000000000002C5D) >> 64;
}
if (x & 0x2000 > 0) {
result = (result * 0x1000000000000162E) >> 64;
}
if (x & 0x1000 > 0) {
result = (result * 0x10000000000000B17) >> 64;
}
if (x & 0x800 > 0) {
result = (result * 0x1000000000000058C) >> 64;
}
if (x & 0x400 > 0) {
result = (result * 0x100000000000002C6) >> 64;
}
if (x & 0x200 > 0) {
result = (result * 0x10000000000000163) >> 64;
}
if (x & 0x100 > 0) {
result = (result * 0x100000000000000B1) >> 64;
}
}
if (x & 0xFF > 0) {
if (x & 0x80 > 0) {
result = (result * 0x10000000000000059) >> 64;
}
if (x & 0x40 > 0) {
result = (result * 0x1000000000000002C) >> 64;
}
if (x & 0x20 > 0) {
result = (result * 0x10000000000000016) >> 64;
}
if (x & 0x10 > 0) {
result = (result * 0x1000000000000000B) >> 64;
}
if (x & 0x8 > 0) {
result = (result * 0x10000000000000006) >> 64;
}
if (x & 0x4 > 0) {
result = (result * 0x10000000000000003) >> 64;
}
if (x & 0x2 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
if (x & 0x1 > 0) {
result = (result * 0x10000000000000001) >> 64;
}
}
// In the code snippet below, two operations are executed simultaneously:
//
// 1. The result is multiplied by $(2^n + 1)$, where $2^n$ represents the integer part, and the additional 1
// accounts for the initial guess of 0.5. This is achieved by subtracting from 191 instead of 192.
// 2. The result is then converted to an unsigned 60.18-decimal fixed-point format.
//
// The underlying logic is based on the relationship $2^{191-ip} = 2^{ip} / 2^{191}$, where $ip$ denotes the,
// integer part, $2^n$.
result *= UNIT;
result >>= (191 - (x >> 64));
}
}
/// @notice Finds the zero-based index of the first 1 in the binary representation of x.
///
/// @dev See the note on "msb" in this Wikipedia article: https://en.wikipedia.org/wiki/Find_first_set
///
/// Each step in this implementation is equivalent to this high-level code:
///
/// ```solidity
/// if (x >= 2 ** 128) {
/// x >>= 128;
/// result += 128;
/// }
/// ```
///
/// Where 128 is replaced with each respective power of two factor. See the full high-level implementation here:
/// https://gist.github.com/PaulRBerg/f932f8693f2733e30c4d479e8e980948
///
/// The Yul instructions used below are:
///
/// - "gt" is "greater than"
/// - "or" is the OR bitwise operator
/// - "shl" is "shift left"
/// - "shr" is "shift right"
///
/// @param x The uint256 number for which to find the index of the most significant bit.
/// @return result The index of the most significant bit as a uint256.
/// @custom:smtchecker abstract-function-nondet
function msb(uint256 x) pure returns (uint256 result) {
// 2^128
assembly ("memory-safe") {
let factor := shl(7, gt(x, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^64
assembly ("memory-safe") {
let factor := shl(6, gt(x, 0xFFFFFFFFFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^32
assembly ("memory-safe") {
let factor := shl(5, gt(x, 0xFFFFFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^16
assembly ("memory-safe") {
let factor := shl(4, gt(x, 0xFFFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^8
assembly ("memory-safe") {
let factor := shl(3, gt(x, 0xFF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^4
assembly ("memory-safe") {
let factor := shl(2, gt(x, 0xF))
x := shr(factor, x)
result := or(result, factor)
}
// 2^2
assembly ("memory-safe") {
let factor := shl(1, gt(x, 0x3))
x := shr(factor, x)
result := or(result, factor)
}
// 2^1
// No need to shift x any more.
assembly ("memory-safe") {
let factor := gt(x, 0x1)
result := or(result, factor)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev Credits to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - The denominator must not be zero.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as a uint256.
/// @param y The multiplier as a uint256.
/// @param denominator The divisor as a uint256.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function mulDiv(uint256 x, uint256 y, uint256 denominator) pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512-bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
unchecked {
return prod0 / denominator;
}
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (prod1 >= denominator) {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
////////////////////////////////////////////////////////////////////////////
// 512 by 256 division
////////////////////////////////////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using the mulmod Yul instruction.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512-bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
unchecked {
// Calculate the largest power of two divisor of the denominator using the unary operator ~. This operation cannot overflow
// because the denominator cannot be zero at this point in the function execution. The result is always >= 1.
// For more detail, see https://cs.stackexchange.com/q/138556/92363.
uint256 lpotdod = denominator & (~denominator + 1);
uint256 flippedLpotdod;
assembly ("memory-safe") {
// Factor powers of two out of denominator.
denominator := div(denominator, lpotdod)
// Divide [prod1 prod0] by lpotdod.
prod0 := div(prod0, lpotdod)
// Get the flipped value `2^256 / lpotdod`. If the `lpotdod` is zero, the flipped value is one.
// `sub(0, lpotdod)` produces the two's complement version of `lpotdod`, which is equivalent to flipping all the bits.
// However, `div` interprets this value as an unsigned value: https://ethereum.stackexchange.com/q/147168/24693
flippedLpotdod := add(div(sub(0, lpotdod), lpotdod), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * flippedLpotdod;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
}
}
/// @notice Calculates x*y÷1e18 with 512-bit precision.
///
/// @dev A variant of {mulDiv} with constant folding, i.e. in which the denominator is hard coded to 1e18.
///
/// Notes:
/// - The body is purposely left uncommented; to understand how this works, see the documentation in {mulDiv}.
/// - The result is rounded toward zero.
/// - We take as an axiom that the result cannot be `MAX_UINT256` when x and y solve the following system of equations:
///
/// $$
/// \begin{cases}
/// x * y = MAX\_UINT256 * UNIT \\
/// (x * y) \% UNIT \geq \frac{UNIT}{2}
/// \end{cases}
/// $$
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - The result must fit in uint256.
///
/// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
/// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
/// @return result The result as an unsigned 60.18-decimal fixed-point number.
/// @custom:smtchecker abstract-function-nondet
function mulDiv18(uint256 x, uint256 y) pure returns (uint256 result) {
uint256 prod0;
uint256 prod1;
assembly ("memory-safe") {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
unchecked {
return prod0 / UNIT;
}
}
if (prod1 >= UNIT) {
revert PRBMath_MulDiv18_Overflow(x, y);
}
uint256 remainder;
assembly ("memory-safe") {
remainder := mulmod(x, y, UNIT)
result :=
mul(
or(
div(sub(prod0, remainder), UNIT_LPOTD),
mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, UNIT_LPOTD), UNIT_LPOTD), 1))
),
UNIT_INVERSE
)
}
}
/// @notice Calculates x*y÷denominator with 512-bit precision.
///
/// @dev This is an extension of {mulDiv} for signed numbers, which works by computing the signs and the absolute values separately.
///
/// Notes:
/// - The result is rounded toward zero.
///
/// Requirements:
/// - Refer to the requirements in {mulDiv}.
/// - None of the inputs can be `type(int256).min`.
/// - The result must fit in int256.
///
/// @param x The multiplicand as an int256.
/// @param y The multiplier as an int256.
/// @param denominator The divisor as an int256.
/// @return result The result as an int256.
/// @custom:smtchecker abstract-function-nondet
function mulDivSigned(int256 x, int256 y, int256 denominator) pure returns (int256 result) {
if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
revert PRBMath_MulDivSigned_InputTooSmall();
}
// Get hold of the absolute values of x, y and the denominator.
uint256 xAbs;
uint256 yAbs;
uint256 dAbs;
unchecked {
xAbs = x < 0 ? uint256(-x) : uint256(x);
yAbs = y < 0 ? uint256(-y) : uint256(y);
dAbs = denominator < 0 ? uint256(-denominator) : uint256(denominator);
}
// Compute the absolute value of x*y÷denominator. The result must fit in int256.
uint256 resultAbs = mulDiv(xAbs, yAbs, dAbs);
if (resultAbs > uint256(type(int256).max)) {
revert PRBMath_MulDivSigned_Overflow(x, y);
}
// Get the signs of x, y and the denominator.
uint256 sx;
uint256 sy;
uint256 sd;
assembly ("memory-safe") {
// "sgt" is the "signed greater than" assembly instruction and "sub(0,1)" is -1 in two's complement.
sx := sgt(x, sub(0, 1))
sy := sgt(y, sub(0, 1))
sd := sgt(denominator, sub(0, 1))
}
// XOR over sx, sy and sd. What this does is to check whether there are 1 or 3 negative signs in the inputs.
// If there are, the result should be negative. Otherwise, it should be positive.
unchecked {
result = sx ^ sy ^ sd == 0 ? -int256(resultAbs) : int256(resultAbs);
}
}
/// @notice Calculates the square root of x using the Babylonian method.
///
/// @dev See https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
///
/// Notes:
/// - If x is not a perfect square, the result is rounded down.
/// - Credits to OpenZeppelin for the explanations in comments below.
///
/// @param x The uint256 number for which to calculate the square root.
/// @return result The result as a uint256.
/// @custom:smtchecker abstract-function-nondet
function sqrt(uint256 x) pure returns (uint256 result) {
if (x == 0) {
return 0;
}
// For our first guess, we calculate the biggest power of 2 which is smaller than the square root of x.
//
// We know that the "msb" (most significant bit) of x is a power of 2 such that we have:
//
// $$
// msb(x) <= x <= 2*msb(x)$
// $$
//
// We write $msb(x)$ as $2^k$, and we get:
//
// $$
// k = log_2(x)
// $$
//
// Thus, we can write the initial inequality as:
//
// $$
// 2^{log_2(x)} <= x <= 2*2^{log_2(x)+1} \\
// sqrt(2^k) <= sqrt(x) < sqrt(2^{k+1}) \\
// 2^{k/2} <= sqrt(x) < 2^{(k+1)/2} <= 2^{(k/2)+1}
// $$
//
// Consequently, $2^{log_2(x) /2} is a good first approximation of sqrt(x) with at least one correct bit.
uint256 xAux = uint256(x);
result = 1;
if (xAux >= 2 ** 128) {
xAux >>= 128;
result <<= 64;
}
if (xAux >= 2 ** 64) {
xAux >>= 64;
result <<= 32;
}
if (xAux >= 2 ** 32) {
xAux >>= 32;
result <<= 16;
}
if (xAux >= 2 ** 16) {
xAux >>= 16;
result <<= 8;
}
if (xAux >= 2 ** 8) {
xAux >>= 8;
result <<= 4;
}
if (xAux >= 2 ** 4) {
xAux >>= 4;
result <<= 2;
}
if (xAux >= 2 ** 2) {
result <<= 1;
}
// At this point, `result` is an estimation with at least one bit of precision. We know the true value has at
// most 128 bits, since it is the square root of a uint256. Newton's method converges quadratically (precision
// doubles at every iteration). We thus need at most 7 iteration to turn our partial result with one bit of
// precision into the expected uint128 result.
unchecked {
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
result = (result + x / result) >> 1;
// If x is not a perfect square, round the result toward zero.
uint256 roundedResult = x / result;
if (result >= roundedResult) {
result = roundedResult;
}
}
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/CommonEventsAndErrors.sol)
/// @notice A collection of common events and errors thrown within the Origami contracts
library CommonEventsAndErrors {
error InsufficientBalance(address token, uint256 required, uint256 balance);
error InvalidToken(address token);
error InvalidParam();
error InvalidAddress(address addr);
error InvalidAmount(address token, uint256 amount);
error ExpectedNonZero();
error Slippage(uint256 minAmountExpected, uint256 actualAmount);
error IsPaused();
error UnknownExecuteError(bytes returndata);
error InvalidAccess();
error BreachedMaxTotalSupply(uint256 totalSupply, uint256 maxTotalSupply);
event TokenRecovered(address indexed to, address indexed token, uint256 amount);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @dev The maximum fee a market can have (25%).
uint256 constant MAX_FEE = 0.25e18;
/// @dev Oracle price scale.
uint256 constant ORACLE_PRICE_SCALE = 1e36;
/// @dev Liquidation cursor.
uint256 constant LIQUIDATION_CURSOR = 0.3e18;
/// @dev Max liquidation incentive factor.
uint256 constant MAX_LIQUIDATION_INCENTIVE_FACTOR = 1.15e18;
/// @dev The EIP-712 typeHash for EIP712Domain.
bytes32 constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
/// @dev The EIP-712 typeHash for Authorization.
bytes32 constant AUTHORIZATION_TYPEHASH =
keccak256("Authorization(address authorizer,address authorized,bool isAuthorized,uint256 nonce,uint256 deadline)");
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title ErrorsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing error messages.
library ErrorsLib {
/// @notice Thrown when the caller is not the owner.
string internal constant NOT_OWNER = "not owner";
/// @notice Thrown when the LLTV to enable exceeds the maximum LLTV.
string internal constant MAX_LLTV_EXCEEDED = "max LLTV exceeded";
/// @notice Thrown when the fee to set exceeds the maximum fee.
string internal constant MAX_FEE_EXCEEDED = "max fee exceeded";
/// @notice Thrown when the value is already set.
string internal constant ALREADY_SET = "already set";
/// @notice Thrown when the IRM is not enabled at market creation.
string internal constant IRM_NOT_ENABLED = "IRM not enabled";
/// @notice Thrown when the LLTV is not enabled at market creation.
string internal constant LLTV_NOT_ENABLED = "LLTV not enabled";
/// @notice Thrown when the market is already created.
string internal constant MARKET_ALREADY_CREATED = "market already created";
/// @notice Thrown when a token to transfer doesn't have code.
string internal constant NO_CODE = "no code";
/// @notice Thrown when the market is not created.
string internal constant MARKET_NOT_CREATED = "market not created";
/// @notice Thrown when not exactly one of the input amount is zero.
string internal constant INCONSISTENT_INPUT = "inconsistent input";
/// @notice Thrown when zero assets is passed as input.
string internal constant ZERO_ASSETS = "zero assets";
/// @notice Thrown when a zero address is passed as input.
string internal constant ZERO_ADDRESS = "zero address";
/// @notice Thrown when the caller is not authorized to conduct an action.
string internal constant UNAUTHORIZED = "unauthorized";
/// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`.
string internal constant INSUFFICIENT_COLLATERAL = "insufficient collateral";
/// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`.
string internal constant INSUFFICIENT_LIQUIDITY = "insufficient liquidity";
/// @notice Thrown when the position to liquidate is healthy.
string internal constant HEALTHY_POSITION = "position is healthy";
/// @notice Thrown when the authorization signature is invalid.
string internal constant INVALID_SIGNATURE = "invalid signature";
/// @notice Thrown when the authorization signature is expired.
string internal constant SIGNATURE_EXPIRED = "signature expired";
/// @notice Thrown when the nonce is invalid.
string internal constant INVALID_NONCE = "invalid nonce";
/// @notice Thrown when a token transfer reverted.
string internal constant TRANSFER_REVERTED = "transfer reverted";
/// @notice Thrown when a token transfer returned false.
string internal constant TRANSFER_RETURNED_FALSE = "transfer returned false";
/// @notice Thrown when a token transferFrom reverted.
string internal constant TRANSFER_FROM_REVERTED = "transferFrom reverted";
/// @notice Thrown when a token transferFrom returned false
string internal constant TRANSFER_FROM_RETURNED_FALSE = "transferFrom returned false";
/// @notice Thrown when the maximum uint128 is exceeded.
string internal constant MAX_UINT128_EXCEEDED = "max uint128 exceeded";
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @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.
*/
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].
*/
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.5.0;
import {MarketParams, Market} from "./IMorpho.sol";
/// @title IIrm
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
/// storage.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.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 security@morpho.org
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title IMorphoLiquidateCallback
/// @notice Interface that liquidators willing to use `liquidate`'s callback must implement.
interface IMorphoLiquidateCallback {
/// @notice Callback called when a liquidation occurs.
/// @dev The callback is called only if data is not empty.
/// @param repaidAssets The amount of repaid assets.
/// @param data Arbitrary data passed to the `liquidate` function.
function onMorphoLiquidate(uint256 repaidAssets, bytes calldata data) external;
}
/// @title IMorphoRepayCallback
/// @notice Interface that users willing to use `repay`'s callback must implement.
interface IMorphoRepayCallback {
/// @notice Callback called when a repayment occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of repaid assets.
/// @param data Arbitrary data passed to the `repay` function.
function onMorphoRepay(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoSupplyCallback
/// @notice Interface that users willing to use `supply`'s callback must implement.
interface IMorphoSupplyCallback {
/// @notice Callback called when a supply occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of supplied assets.
/// @param data Arbitrary data passed to the `supply` function.
function onMorphoSupply(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoSupplyCollateralCallback
/// @notice Interface that users willing to use `supplyCollateral`'s callback must implement.
interface IMorphoSupplyCollateralCallback {
/// @notice Callback called when a supply of collateral occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of supplied collateral.
/// @param data Arbitrary data passed to the `supplyCollateral` function.
function onMorphoSupplyCollateral(uint256 assets, bytes calldata data) external;
}
/// @title IMorphoFlashLoanCallback
/// @notice Interface that users willing to use `flashLoan`'s callback must implement.
interface IMorphoFlashLoanCallback {
/// @notice Callback called when a flash loan occurs.
/// @dev The callback is called only if data is not empty.
/// @param assets The amount of assets that was flash loaned.
/// @param data Arbitrary data passed to the `flashLoan` function.
function onMorphoFlashLoan(uint256 assets, bytes calldata data) external;
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title IOracle
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Interface that oracles used by Morpho must implement.
/// @dev It is the user's responsibility to select markets with safe oracles.
interface IOracle {
/// @notice Returns the price of 1 asset of collateral token quoted in 1 asset of loan token, scaled by 1e36.
/// @dev It corresponds to the price of 10**(collateral token decimals) assets of collateral token quoted in
/// 10**(loan token decimals) assets of loan token with `36 + loan token decimals - collateral token decimals`
/// decimals of precision.
function price() external view returns (uint256);
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol)
/**
* @notice An Origami abstraction over a borrow/lend money market for
* a single `supplyToken` and a single `borrowToken`, for a given `positionOwner`
*/
interface IOrigamiBorrowAndLend {
event PositionOwnerSet(address indexed account);
event SurplusDebtReclaimed(uint256 amount, address indexed recipient);
/**
* @notice Set the position owner who can borrow/lend via this contract
*/
function setPositionOwner(address account) external;
/**
* @notice Supply tokens as collateral
*/
function supply(
uint256 supplyAmount
) external;
/**
* @notice Withdraw collateral tokens to recipient
* @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
*/
function withdraw(
uint256 withdrawAmount,
address recipient
) external returns (uint256 amountWithdrawn);
/**
* @notice Borrow tokens and send to recipient
*/
function borrow(
uint256 borrowAmount,
address recipient
) external;
/**
* @notice Repay debt.
* @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
* to that outstanding debt balance
* `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
* Any surplus debtTokens (if debt fully repaid) will remain in this contract
*/
function repay(
uint256 repayAmount
) external returns (uint256 debtRepaidAmount);
/**
* @notice Repay debt and withdraw collateral in one step
* @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
* to that outstanding debt balance
* Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
* `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
* Any surplus debtTokens (if debt fully repaid) will remain in this contract
*/
function repayAndWithdraw(
uint256 repayAmount,
uint256 withdrawAmount,
address recipient
) external returns (
uint256 debtRepaidAmount,
uint256 withdrawnAmount
);
/**
* @notice Supply collateral and borrow in one step
*/
function supplyAndBorrow(
uint256 supplyAmount,
uint256 borrowAmount,
address recipient
) external;
/**
* @notice The approved owner of the borrow/lend position
*/
function positionOwner() external view returns (address);
/**
* @notice The token supplied as collateral
*/
function supplyToken() external view returns (address);
/**
* @notice The token which is borrowed
*/
function borrowToken() external view returns (address);
/**
* @notice The current (manually tracked) balance of tokens supplied
*/
function suppliedBalance() external view returns (uint256);
/**
* @notice The current debt balance of tokens borrowed
*/
function debtBalance() external view returns (uint256);
/**
* @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
* money market parameters
*/
function isSafeAlRatio(uint256 alRatio) external view returns (bool);
/**
* @notice How many `supplyToken` are available to withdraw from collateral
* from the entire protocol, assuming this contract has fully paid down its debt
*/
function availableToWithdraw() external view returns (uint256);
/**
* @notice How much more capacity is available to supply
*/
function availableToSupply() external view returns (
uint256 supplyCap,
uint256 available
);
/**
* @notice How many `borrowToken` are available to borrow
* from the entire protocol
*/
function availableToBorrow() external view returns (uint256);
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/access/IOrigamiElevatedAccess.sol)
/**
* @notice Inherit to add Owner roles for DAO elevated access.
*/
interface IOrigamiElevatedAccess {
event ExplicitAccessSet(address indexed account, bytes4 indexed fnSelector, bool indexed value);
event NewOwnerProposed(address indexed oldOwner, address indexed oldProposedOwner, address indexed newProposedOwner);
event NewOwnerAccepted(address indexed oldOwner, address indexed newOwner);
struct ExplicitAccess {
bytes4 fnSelector;
bool allowed;
}
/**
* @notice The address of the current owner.
*/
function owner() external view returns (address);
/**
* @notice Explicit approval for an address to execute a function.
* allowedCaller => function selector => true/false
*/
function explicitFunctionAccess(address contractAddr, bytes4 functionSelector) external view returns (bool);
/**
* @notice Proposes a new Owner.
* Can only be called by the current owner
*/
function proposeNewOwner(address account) external;
/**
* @notice Caller accepts the role as new Owner.
* Can only be called by the proposed owner
*/
function acceptOwner() external;
/**
* @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
* @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
*/
function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external;
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol)
import { IOrigamiBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiBorrowAndLend.sol";
import { IMorpho, Id as MorphoMarketId, MarketParams as MorphoMarketParams } from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
/**
* @notice An Origami abstraction over a borrow/lend money market for
* a single `supplyToken` and a single `borrowToken`.
* This is a Morpho specific interface
*/
interface IOrigamiMorphoBorrowAndLend is IOrigamiBorrowAndLend {
event MaxSafeLtvSet(uint256 _maxSafeLtv);
event SwapperSet(address indexed swapper);
/**
* @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
* @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
*/
function setMaxSafeLtv(uint256 _maxSafeLtv) external;
/**
* @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
*/
function setSwapper(address _swapper) external;
/**
* @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
* and borrowing `borrowToken` and swapping that back to `supplyToken`
* @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
*/
function increaseLeverage(
uint256 supplyCollateralAmount,
uint256 borrowAmount,
bytes memory swapData,
uint256 supplyCollateralSurplusThreshold
) external returns (uint256 totalCollateralSupplied);
/**
* @notice Decrease the leverage of the existing position, by repaying `borrowToken`
* and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
*/
function decreaseLeverage(
uint256 repayAmount,
uint256 withdrawCollateralAmount,
bytes memory swapData,
uint256 repaySurplusThreshold
) external returns (
uint256 debtRepaidAmount,
uint256 surplusDebtRepaid
);
/**
* @notice The morpho singleton contract
*/
function morpho() external view returns (IMorpho);
/**
* @notice The Morpho oracle used for the target market
*/
function morphoMarketOracle() external view returns (address);
/**
* @notice The Morpho Interest Rate Model used for the target market
*/
function morphoMarketIrm() external view returns (address);
/**
* @notice The Morpho Liquidation LTV for the target market
*/
function morphoMarketLltv() external view returns (uint96);
/**
* @notice The Morpho market parameters
*/
function getMarketParams() external view returns (MorphoMarketParams memory);
/**
* @notice The derived Morpho market ID given the market parameters
*/
function marketId() external view returns (MorphoMarketId);
/**
* @notice The max LTV we will allow when borrowing or withdrawing collateral.
* @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
*/
function maxSafeLtv() external view returns (uint256);
/**
* @notice The swapper for `borrowToken` <--> `supplyToken`
*/
function swapper() external view returns (IOrigamiSwapper);
/**
* @notice Returns the curent Morpho position data
*/
function debtAccountData() external view returns (
uint256 collateral,
uint256 collateralPrice,
uint256 borrowed,
uint256 maxBorrow,
uint256 currentLtv,
uint256 healthFactor
);
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (interfaces/common/swappers/IOrigamiSwapper.sol)
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @notice An on chain swapper contract to integrate with the 1Inch router | 0x proxy,
* possibly others which obtain quote calldata offchain and then execute via a low level call
* to perform the swap onchain
*/
interface IOrigamiSwapper {
error UnknownSwapError(bytes result);
error InvalidSwap();
error InvalidRouter(address router);
event Swap(address indexed sellToken, uint256 sellTokenAmount, address indexed buyToken, uint256 buyTokenAmount);
event RouterWhitelisted(address indexed router, bool allowed);
/**
* @notice Pull tokens from sender then execute the swap
*/
function execute(
IERC20 sellToken,
uint256 sellTokenAmount,
IERC20 buyToken,
bytes memory swapData
) external returns (uint256 buyTokenAmount);
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams} from "../interfaces/IMorpho.sol";
/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to convert a market to its id.
library MarketParamsLib {
/// @notice The length of the data used to compute the id of a market.
/// @dev The length is 5 * 32 because `MarketParams` has 5 variables of 32 bytes each.
uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 5 * 32;
/// @notice Returns the id of the market `marketParams`.
function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
assembly ("memory-safe") {
marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
}
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
uint256 constant WAD = 1e18;
/// @title MathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library to manage fixed-point arithmetic.
library MathLib {
/// @dev Returns (`x` * `y`) / `WAD` rounded down.
function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded down.
function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
/// @dev Returns (`x` * `WAD`) / `y` rounded up.
function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
/// @dev Returns (`x` * `y`) / `d` rounded down.
function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
return (x * y) / d;
}
/// @dev Returns (`x` * `y`) / `d` rounded up.
function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) {
return (x * y + (d - 1)) / d;
}
/// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a
/// continuous compound interest rate.
function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) {
uint256 firstTerm = x * n;
uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD);
uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD);
return firstTerm + secondTerm + thirdTerm;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";
import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";
/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
using MathLib for uint256;
using MathLib for uint128;
using UtilsLib for uint256;
using MorphoLib for IMorpho;
using SharesMathLib for uint256;
using MarketParamsLib for MarketParams;
/// @notice Returns the expected market balances of a market after having accrued interest.
/// @return The expected total supply assets.
/// @return The expected total supply shares.
/// @return The expected total borrow assets.
/// @return The expected total borrow shares.
function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256, uint256, uint256, uint256)
{
Id id = marketParams.id();
Market memory market = morpho.market(id);
uint256 elapsed = block.timestamp - market.lastUpdate;
// Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
market.totalBorrowAssets += interest.toUint128();
market.totalSupplyAssets += interest.toUint128();
if (market.fee != 0) {
uint256 feeAmount = interest.wMulDown(market.fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already updated.
uint256 feeShares =
feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
market.totalSupplyShares += feeShares.toUint128();
}
}
return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
}
/// @notice Returns the expected total supply assets of a market after having accrued interest.
function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyAssets)
{
(totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total borrow assets of a market after having accrued interest.
function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalBorrowAssets)
{
(,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total supply shares of a market after having accrued interest.
function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyShares)
{
(, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
/// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
/// @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: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";
/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
return uint256(morpho.extSloads(slot)[0]);
}
function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function fee(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function _array(bytes32 x) private pure returns (bytes32[] memory) {
bytes32[] memory res = new bytes32[](1);
res[0] = x;
return res;
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id} from "../../interfaces/IMorpho.sol";
/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
/* SLOTS */
uint256 internal constant OWNER_SLOT = 0;
uint256 internal constant FEE_RECIPIENT_SLOT = 1;
uint256 internal constant POSITION_SLOT = 2;
uint256 internal constant MARKET_SLOT = 3;
uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
uint256 internal constant IS_AUTHORIZED_SLOT = 6;
uint256 internal constant NONCE_SLOT = 7;
uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 8;
/* SLOT OFFSETS */
uint256 internal constant LOAN_TOKEN_OFFSET = 0;
uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
uint256 internal constant ORACLE_OFFSET = 2;
uint256 internal constant IRM_OFFSET = 3;
uint256 internal constant LLTV_OFFSET = 4;
uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;
uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;
/* GETTERS */
function ownerSlot() internal pure returns (bytes32) {
return bytes32(OWNER_SLOT);
}
function feeRecipientSlot() internal pure returns (bytes32) {
return bytes32(FEE_RECIPIENT_SLOT);
}
function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
return bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
);
}
function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
return bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
+ BORROW_SHARES_AND_COLLATERAL_OFFSET
);
}
function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
}
function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
}
function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
}
function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
}
function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
}
function isAuthorizedSlot(address authorizer, address authorizee) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizee, keccak256(abi.encode(authorizer, IS_AUTHORIZED_SLOT))));
}
function nonceSlot(address authorizer) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizer, NONCE_SLOT));
}
function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
}
function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
}
function idToOracleSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
}
function idToIrmSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
}
function idToLltvSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
}
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)
import { OrigamiElevatedAccessBase } from "contracts/common/access/OrigamiElevatedAccessBase.sol";
/**
* @notice Inherit to add Owner roles for DAO elevated access.
*/
abstract contract OrigamiElevatedAccess is OrigamiElevatedAccessBase {
constructor(address initialOwner) {
_init(initialOwner);
}
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/access/OrigamiElevatedAccessBase.sol)
import { IOrigamiElevatedAccess } from "contracts/interfaces/common/access/IOrigamiElevatedAccess.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
/**
* @notice Inherit to add Owner roles for DAO elevated access.
*/
abstract contract OrigamiElevatedAccessBase is IOrigamiElevatedAccess {
/**
* @notice The address of the current owner.
*/
address public override owner;
/**
* @notice Explicit approval for an address to execute a function.
* allowedCaller => function selector => true/false
*/
mapping(address => mapping(bytes4 => bool)) public override explicitFunctionAccess;
/// @dev Track proposed owner
address private _proposedNewOwner;
function _init(address initialOwner) internal {
if (owner != address(0)) revert CommonEventsAndErrors.InvalidAccess();
if (initialOwner == address(0)) revert CommonEventsAndErrors.InvalidAddress(address(0));
owner = initialOwner;
}
/**
* @notice Proposes a new Owner.
* Can only be called by the current owner
*/
function proposeNewOwner(address account) external override onlyElevatedAccess {
if (account == address(0)) revert CommonEventsAndErrors.InvalidAddress(account);
emit NewOwnerProposed(owner, _proposedNewOwner, account);
_proposedNewOwner = account;
}
/**
* @notice Caller accepts the role as new Owner.
* Can only be called by the proposed owner
*/
function acceptOwner() external override {
if (msg.sender != _proposedNewOwner) revert CommonEventsAndErrors.InvalidAccess();
emit NewOwnerAccepted(owner, msg.sender);
owner = msg.sender;
delete _proposedNewOwner;
}
/**
* @notice Grant `allowedCaller` the rights to call the function selectors in the access list.
* @dev fnSelector == bytes4(keccak256("fn(argType1,argType2,...)"))
*/
function setExplicitAccess(address allowedCaller, ExplicitAccess[] calldata access) external override onlyElevatedAccess {
if (allowedCaller == address(0)) revert CommonEventsAndErrors.InvalidAddress(allowedCaller);
ExplicitAccess memory _access;
for (uint256 i; i < access.length; ++i) {
_access = access[i];
emit ExplicitAccessSet(allowedCaller, _access.fnSelector, _access.allowed);
explicitFunctionAccess[allowedCaller][_access.fnSelector] = _access.allowed;
}
}
function isElevatedAccess(address caller, bytes4 fnSelector) internal view returns (bool) {
return (
caller == owner ||
explicitFunctionAccess[caller][fnSelector]
);
}
/**
* @notice The owner is allowed to call, or if explicit access has been given to the caller.
* @dev Important: Only for use when called from an *external* contract.
* If a function with this modifier is called internally then the `msg.sig`
* will still refer to the top level externally called function.
*/
modifier onlyElevatedAccess() {
if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
_;
}
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (libraries/OrigamiMath.sol)
import { mulDiv as prbMulDiv, PRBMath_MulDiv_Overflow } from "@prb/math/src/Common.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
/**
* @notice Utilities to operate on fixed point math multipliation and division
* taking rounding into consideration
*/
library OrigamiMath {
enum Rounding {
ROUND_DOWN,
ROUND_UP
}
uint256 public constant BASIS_POINTS_DIVISOR = 10_000;
function scaleUp(uint256 amount, uint256 scalar) internal pure returns (uint256) {
// Special case for scalar == 1, as it's common for token amounts to not need
// scaling if decimal places are the same
return scalar == 1 ? amount : amount * scalar;
}
function scaleDown(
uint256 amount,
uint256 scalar,
Rounding roundingMode
) internal pure returns (uint256 result) {
// Special case for scalar == 1, as it's common for token amounts to not need
// scaling if decimal places are the same
unchecked {
if (scalar == 1) {
result = amount;
} else if (roundingMode == Rounding.ROUND_DOWN) {
result = amount / scalar;
} else {
// ROUND_UP uses the same logic as OZ Math.ceilDiv()
result = amount == 0 ? 0 : (amount - 1) / scalar + 1;
}
}
}
/**
* @notice Calculates x * y / denominator with full precision,
* rounding up
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding roundingMode
) internal pure returns (uint256 result) {
result = prbMulDiv(x, y, denominator);
if (roundingMode == Rounding.ROUND_UP) {
if (mulmod(x, y, denominator) != 0) {
if (result < type(uint256).max) {
unchecked {
result = result + 1;
}
} else {
revert PRBMath_MulDiv_Overflow(x, y, denominator);
}
}
}
}
function subtractBps(
uint256 inputAmount,
uint256 basisPoints,
Rounding roundingMode
) internal pure returns (uint256 result) {
uint256 numeratorBps;
unchecked {
numeratorBps = BASIS_POINTS_DIVISOR - basisPoints;
}
result = basisPoints < BASIS_POINTS_DIVISOR
? mulDiv(
inputAmount,
numeratorBps,
BASIS_POINTS_DIVISOR,
roundingMode
) : 0;
}
function addBps(
uint256 inputAmount,
uint256 basisPoints,
Rounding roundingMode
) internal pure returns (uint256 result) {
uint256 numeratorBps;
unchecked {
numeratorBps = BASIS_POINTS_DIVISOR + basisPoints;
}
// Round up for max amounts out expected
result = mulDiv(
inputAmount,
numeratorBps,
BASIS_POINTS_DIVISOR,
roundingMode
);
}
/**
* @notice Split the `inputAmount` into two parts based on the `basisPoints` fraction.
* eg: 3333 BPS (33.3%) can be used to split an input amount of 600 into: (result=400, removed=200).
* @dev The rounding mode is applied to the `result`
*/
function splitSubtractBps(
uint256 inputAmount,
uint256 basisPoints,
Rounding roundingMode
) internal pure returns (uint256 result, uint256 removed) {
result = subtractBps(inputAmount, basisPoints, roundingMode);
unchecked {
removed = inputAmount - result;
}
}
/**
* @notice Reverse the fractional amount of an input.
* eg: For 3333 BPS (33.3%) and the remainder=400, the result is 600
*/
function inverseSubtractBps(
uint256 remainderAmount,
uint256 basisPoints,
Rounding roundingMode
) internal pure returns (uint256 result) {
if (basisPoints == 0) return remainderAmount; // gas shortcut for 0
if (basisPoints >= BASIS_POINTS_DIVISOR) revert CommonEventsAndErrors.InvalidParam();
uint256 denominatorBps;
unchecked {
denominatorBps = BASIS_POINTS_DIVISOR - basisPoints;
}
result = mulDiv(
remainderAmount,
BASIS_POINTS_DIVISOR,
denominatorBps,
roundingMode
);
}
/**
* @notice Calculate the relative difference of a value to a reference
* @dev `value` and `referenceValue` must have the same precision
* The denominator is always the referenceValue
*/
function relativeDifferenceBps(
uint256 value,
uint256 referenceValue,
Rounding roundingMode
) internal pure returns (uint256) {
if (referenceValue == 0) revert CommonEventsAndErrors.InvalidParam();
uint256 absDelta;
unchecked {
absDelta = value < referenceValue
? referenceValue - value
: value - referenceValue;
}
return mulDiv(
absDelta,
BASIS_POINTS_DIVISOR,
referenceValue,
roundingMode
);
}
}
pragma solidity 0.8.19;
// SPDX-License-Identifier: AGPL-3.0-or-later
// Origami (common/borrowAndLend/OrigamiMorphoBorrowAndLend.sol)
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {
IMorpho,
Id as MorphoMarketId,
MarketParams as MorphoMarketParams
} from "@morpho-org/morpho-blue/src/interfaces/IMorpho.sol";
import { IOracle as IMorphoOracle } from "@morpho-org/morpho-blue/src/interfaces/IOracle.sol";
import { ORACLE_PRICE_SCALE as MORPHO_ORACLE_PRICE_SCALE } from "@morpho-org/morpho-blue/src/libraries/ConstantsLib.sol";
import { IMorphoSupplyCollateralCallback } from "@morpho-org/morpho-blue/src/interfaces/IMorphoCallbacks.sol";
import { MorphoBalancesLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoBalancesLib.sol";
import { MorphoLib } from "@morpho-org/morpho-blue/src/libraries/periphery/MorphoLib.sol";
import { MarketParamsLib } from "@morpho-org/morpho-blue/src/libraries/MarketParamsLib.sol";
import { IOrigamiSwapper } from "contracts/interfaces/common/swappers/IOrigamiSwapper.sol";
import { IOrigamiMorphoBorrowAndLend } from "contracts/interfaces/common/borrowAndLend/IOrigamiMorphoBorrowAndLend.sol";
import { CommonEventsAndErrors } from "contracts/libraries/CommonEventsAndErrors.sol";
import { OrigamiElevatedAccess } from "contracts/common/access/OrigamiElevatedAccess.sol";
import { OrigamiMath } from "contracts/libraries/OrigamiMath.sol";
/**
* @notice An Origami abstraction over a borrow/lend money market for
* a single `supplyToken` and a single `borrowToken`.
* This is a Morpho specific interface
*/
contract OrigamiMorphoBorrowAndLend is IOrigamiMorphoBorrowAndLend, IMorphoSupplyCollateralCallback, OrigamiElevatedAccess {
using SafeERC20 for IERC20;
using MorphoBalancesLib for IMorpho;
using MorphoLib for IMorpho;
using MarketParamsLib for MorphoMarketParams;
using OrigamiMath for uint256;
/**
* @notice The morpho singleton contract
*/
IMorpho public immutable override morpho;
/**
* @notice The token supplied as collateral
*/
IERC20 private immutable _supplyToken;
/**
* @notice The token which is borrowed
*/
IERC20 private immutable _borrowToken;
/**
* @notice The Morpho oracle used for the target market
*/
address public override immutable morphoMarketOracle;
/**
* @notice The Morpho Interest Rate Model used for the target market
*/
address public override immutable morphoMarketIrm;
/**
* @notice The Morpho Liquidation LTV for the target market
*/
uint96 public override immutable morphoMarketLltv;
/**
* @notice The derived Morpho market ID given the market parameters
*/
MorphoMarketId public override immutable marketId;
/**
* @notice The approved owner of the borrow/lend position
*/
address public override positionOwner;
/**
* @notice The max LTV we will allow when borrowing or withdrawing collateral.
* @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
*/
uint256 public override maxSafeLtv;
/**
* @notice The swapper for `borrowToken` <--> `supplyToken`
*/
IOrigamiSwapper public override swapper;
/**
* @dev Factor when converting the Morpho LTV (1e18) to an Origami Assets/Liabilities (1e18)
*/
uint256 private constant LTV_TO_AL_FACTOR = 1e36;
/// @dev internal serialization of callback data for increasing leverage
struct IncreaseLeverageData {
/// @dev The amount of new `borrowToken` to borrow
uint256 borrowAmount;
/// @dev The encoded swap data for `borrowToken` to `supplyToken`
bytes swapData;
}
/// @dev internal serialization of callback data for decreasing leverage
struct DecreaseLeverageData {
/// @dev The amount of `supplyToken` to withdraw from collateral
uint256 withdrawCollateralAmount;
/// @dev The encoded swap data for `supplyToken` to `borrowToken`
bytes swapData;
}
constructor(
address _initialOwner,
address __supplyToken,
address __borrowToken,
address _morphoAddress,
address _morphoMarketOracle,
address _morphoMarketIrm,
uint96 _morphoMarketLltv,
uint256 _maxSafeLtv
) OrigamiElevatedAccess(_initialOwner) {
_supplyToken = IERC20(__supplyToken);
_borrowToken = IERC20(__borrowToken);
morpho = IMorpho(_morphoAddress);
morphoMarketOracle = _morphoMarketOracle;
morphoMarketIrm = _morphoMarketIrm;
morphoMarketLltv = _morphoMarketLltv;
if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
maxSafeLtv = _maxSafeLtv;
marketId = getMarketParams().id();
// Verify that the market is valid
if (morpho.lastUpdate(marketId) == 0) revert CommonEventsAndErrors.InvalidParam();
// Approve the supply and borrow to the Morpho singleton upfront
_supplyToken.forceApprove(_morphoAddress, type(uint256).max);
_borrowToken.forceApprove(_morphoAddress, type(uint256).max);
}
/**
* @notice Set the position owner who can borrow/lend via this contract
*/
function setPositionOwner(address account) external override onlyElevatedAccess {
positionOwner = account;
emit PositionOwnerSet(account);
}
/**
* @notice Set the max LTV we will allow when borrowing or withdrawing collateral.
* @dev The morpho LTV is the liquidation LTV only, we don't want to allow up to that limit
* so we set a more restrictive 'safe' LTV'
*/
function setMaxSafeLtv(uint256 _maxSafeLtv) external override onlyElevatedAccess {
if (_maxSafeLtv >= morphoMarketLltv) revert CommonEventsAndErrors.InvalidParam();
maxSafeLtv = _maxSafeLtv;
emit MaxSafeLtvSet(_maxSafeLtv);
}
/**
* @notice Set the swapper responsible for `borrowToken` <--> `supplyToken` swaps
*/
function setSwapper(address _swapper) external override onlyElevatedAccess {
if (_swapper == address(0)) revert CommonEventsAndErrors.InvalidAddress(_swapper);
// Update the approval's for both `supplyToken` and `borrowToken`
address _oldSwapper = address(swapper);
if (_oldSwapper != address(0)) {
_supplyToken.forceApprove(_oldSwapper, 0);
_borrowToken.forceApprove(_oldSwapper, 0);
}
_supplyToken.forceApprove(_swapper, type(uint256).max);
_borrowToken.forceApprove(_swapper, type(uint256).max);
emit SwapperSet(_swapper);
swapper = IOrigamiSwapper(_swapper);
}
/**
* @notice Supply tokens as collateral
*/
function supply(
uint256 supplyAmount
) external override onlyPositionOwnerOrElevated {
_supply(supplyAmount, getMarketParams(), "");
}
/**
* @notice Withdraw collateral tokens to recipient
* @dev Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
*/
function withdraw(
uint256 withdrawAmount,
address recipient
) external override onlyPositionOwnerOrElevated returns (uint256 amountWithdrawn) {
amountWithdrawn = _withdraw(withdrawAmount, recipient, getMarketParams());
}
/**
* @notice Borrow tokens and send to recipient
*/
function borrow(
uint256 borrowAmount,
address recipient
) external override onlyPositionOwnerOrElevated {
_borrow(borrowAmount, recipient, getMarketParams());
}
/**
* @notice Repay debt.
* @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
* to that outstanding debt balance
* `debtRepaidAmount` return parameter will be capped to the outstanding debt balance.
* Any surplus debtTokens (if debt fully repaid) will remain in this contract
*/
function repay(
uint256 repayAmount
) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount) {
debtRepaidAmount = _repay(repayAmount, getMarketParams(), "");
}
/**
* @notice Repay debt and withdraw collateral in one step
* @dev If `repayAmount` is set higher than the actual outstanding debt balance, it will be capped
* to that outstanding debt balance
* Set `withdrawAmount` to type(uint256).max in order to withdraw the whole balance
* `debtRepaidAmount` return parameter will be capped to the outstanding debt amount.
* Any surplus debtTokens (if debt fully repaid) will remain in this contract
*/
function repayAndWithdraw(
uint256 repayAmount,
uint256 withdrawAmount,
address recipient
) external override onlyPositionOwnerOrElevated returns (uint256 debtRepaidAmount, uint256 withdrawnAmount) {
MorphoMarketParams memory marketParams = getMarketParams();
debtRepaidAmount = _repay(repayAmount, marketParams, "");
withdrawnAmount = _withdraw(withdrawAmount, recipient, marketParams);
}
/**
* @notice Supply collateral and borrow in one step
*/
function supplyAndBorrow(
uint256 supplyAmount,
uint256 borrowAmount,
address recipient
) external override onlyPositionOwnerOrElevated {
MorphoMarketParams memory marketParams = getMarketParams();
_supply(supplyAmount, marketParams, "");
_borrow(borrowAmount, recipient, marketParams);
}
/**
* @notice Increase the leverage of the existing position, by supplying `supplyToken` as collateral
* and borrowing `borrowToken` and swapping that back to `supplyToken`
* @dev The totalCollateralSupplied may include any surplus after swapping from the debt to collateral
*/
function increaseLeverage(
uint256 supplyAmount,
uint256 borrowAmount,
bytes memory swapData,
uint256 supplyCollateralSurplusThreshold
) external override onlyPositionOwnerOrElevated returns (uint256 totalCollateralSupplied) {
MorphoMarketParams memory marketParams = getMarketParams();
_supply(
supplyAmount,
marketParams,
abi.encode(IncreaseLeverageData(
borrowAmount,
swapData
))
);
totalCollateralSupplied = supplyAmount;
// There may be a suplus of `supplyToken` in this contract after the leverage increase
// If over the threshold, supply any surplus back in as collateral to morpho
uint256 surplusAfterLeverage = _supplyToken.balanceOf(address(this));
if (surplusAfterLeverage > supplyCollateralSurplusThreshold) {
_supply(surplusAfterLeverage, marketParams, "");
totalCollateralSupplied = totalCollateralSupplied + surplusAfterLeverage;
}
}
/**
* @notice Callback called when a supply of collateral occurs in Morpho.
* @dev The callback is called only if data is not empty.
* @param supplyAmount The amount of supplied collateral.
* @param data Arbitrary data passed to the `supplyCollateral` function.
*/
function onMorphoSupplyCollateral(uint256 supplyAmount, bytes calldata data) external override {
if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
IncreaseLeverageData memory decoded = abi.decode(data, (IncreaseLeverageData));
MorphoMarketParams memory marketParams = getMarketParams();
// Perform the borrow
_borrow(decoded.borrowAmount, address(this), marketParams);
// Swap from [borrowToken] to [supplyToken]
// The expected amount of [supplyToken] received after swapping from [borrowToken]
// needs to at least cover the supplyAmount
uint256 collateralReceived = swapper.execute(_borrowToken, decoded.borrowAmount, _supplyToken, decoded.swapData);
if (collateralReceived < supplyAmount) {
revert CommonEventsAndErrors.Slippage(supplyAmount, collateralReceived);
}
}
/**
* @notice Decrease the leverage of the existing position, by repaying `borrowToken`
* and withdrawing `supplyToken` collateral then swapping that back to `borrowToken`
*/
function decreaseLeverage(
uint256 repayAmount,
uint256 withdrawCollateralAmount,
bytes memory swapData,
uint256 repaySurplusThreshold
) external override onlyPositionOwnerOrElevated returns (
uint256 debtRepaidAmount,
uint256 surplusDebtRepaid
) {
MorphoMarketParams memory marketParams = getMarketParams();
debtRepaidAmount = _repay(
repayAmount,
marketParams,
abi.encode(DecreaseLeverageData(
withdrawCollateralAmount,
swapData
))
);
// There may be a suplus of `borrowToken` in this contract after the delverage
// If over the threshold, repay any surplus back to morpho
uint256 surplusAfterDeleverage = _borrowToken.balanceOf(address(this));
if (surplusAfterDeleverage > repaySurplusThreshold) {
surplusDebtRepaid = _repay(surplusAfterDeleverage, marketParams, "");
}
}
/**
* @notice Callback called when a repayment occurs.
* @dev The callback is called only if data is not empty.
* @param repayAmount The amount of repaid assets.
* @param data Arbitrary data passed to the `repay` function.
*/
function onMorphoRepay(uint256 repayAmount, bytes calldata data) external {
if (msg.sender != address(morpho)) revert CommonEventsAndErrors.InvalidAccess();
DecreaseLeverageData memory decoded = abi.decode(data, (DecreaseLeverageData));
MorphoMarketParams memory marketParams = getMarketParams();
// Withdraw collateral
uint256 _amountWithdrawn = _withdraw(decoded.withdrawCollateralAmount, address(this), marketParams);
if (_amountWithdrawn != decoded.withdrawCollateralAmount) {
revert CommonEventsAndErrors.InvalidAmount(address(_supplyToken), decoded.withdrawCollateralAmount);
}
// Swap from [supplyToken] to [borrowToken]
// The expected amount of [borrowToken] received after swapping from [supplyToken]
// needs to at least cover the repayAmount
uint256 borrowTokenReceived = swapper.execute(_supplyToken, decoded.withdrawCollateralAmount, _borrowToken, decoded.swapData);
if (borrowTokenReceived < repayAmount) {
revert CommonEventsAndErrors.Slippage(repayAmount, borrowTokenReceived);
}
}
/**
* @notice Recover accidental donations.
* @dev Does not allow for recovery of supplyToken or borrowToken if there is an outstanding
* morpho debt on this pool
* @param token Token to recover
* @param to Recipient address
* @param amount Amount to recover
*/
function recoverToken(address token, address to, uint256 amount) external onlyElevatedAccess {
if (debtBalance() != 0) {
if (token == address(_supplyToken) || token == address(_borrowToken)) {
revert CommonEventsAndErrors.InvalidToken(token);
}
}
emit CommonEventsAndErrors.TokenRecovered(to, token, amount);
IERC20(token).safeTransfer(to, amount);
}
/**
* @notice The Morpho market parameters
*/
function getMarketParams() public override view returns (MorphoMarketParams memory) {
return MorphoMarketParams({
loanToken: address(_borrowToken),
collateralToken: address(_supplyToken),
oracle: morphoMarketOracle,
irm: morphoMarketIrm,
lltv: morphoMarketLltv
});
}
/**
* @notice The token supplied as collateral
*/
function supplyToken() public override view returns (address) {
return address(_supplyToken);
}
/**
* @notice The token which is borrowed
*/
function borrowToken() public override view returns (address) {
return address(_borrowToken);
}
/**
* @notice The current (manually tracked) balance of tokens supplied
*/
function suppliedBalance() public override view returns (uint256) {
return morpho.collateral(marketId, address(this));
}
/**
* @notice The current debt balance of tokens borrowed
*/
function debtBalance() public override view returns (uint256) {
return morpho.expectedBorrowAssets(getMarketParams(), address(this));
}
/**
* @notice Whether a given Assets/Liabilities Ratio is safe, given the upstream
* money market parameters
*/
function isSafeAlRatio(uint256 alRatio) external override view returns (bool) {
return alRatio >= LTV_TO_AL_FACTOR / maxSafeLtv;
}
/**
* @notice How many `supplyToken` are available to withdraw from collateral
* from the entire protocol, assuming this contract has fully paid down its debt
*/
function availableToWithdraw() external override view returns (uint256) {
// The collateral (for borrows) never gets used as they are siloed markets,
// this contracts collateral is always available to be withdrawn.
// There's no morpho metric for the entire collateral supplied, instead
// this just returns our collateral - so the same as `suppliedBalance()`
return suppliedBalance();
}
/**
* @notice How many `borrowToken` are available to borrow
* from the entire protocol
*/
function availableToBorrow() external override view returns (uint256) {
uint256 totalSupplyAssets = morpho.totalSupplyAssets(marketId);
uint256 totalBorrowAssets = morpho.totalBorrowAssets(marketId);
return totalSupplyAssets > totalBorrowAssets ? totalSupplyAssets - totalBorrowAssets : 0;
}
/**
* @notice How much more capacity is available to supply
*/
function availableToSupply() external override pure returns (
uint256 supplyCap,
uint256 available
) {
return (
type(uint256).max,
type(uint256).max
);
}
/**
* @notice Returns the curent Morpho position data
*/
function debtAccountData() external override view returns (
uint256 collateral,
uint256 collateralPrice,
uint256 borrowed,
uint256 maxBorrow,
uint256 currentLtv,
uint256 healthFactor
) {
// supplyToken decimals
collateral = suppliedBalance();
// `36 + borrowToken decimals - supplyToken decimals` decimals of precision.
collateralPrice = IMorphoOracle(morphoMarketOracle).price();
// borrowToken decimals
borrowed = debtBalance();
uint256 _collateralInBorrowTerms = collateral.mulDiv(
collateralPrice,
MORPHO_ORACLE_PRICE_SCALE,
OrigamiMath.Rounding.ROUND_DOWN
);
maxBorrow = _collateralInBorrowTerms.mulDiv(
morphoMarketLltv,
1e18,
OrigamiMath.Rounding.ROUND_DOWN
);
if (borrowed == 0) {
healthFactor = type(uint256).max;
} else {
currentLtv = borrowed.mulDiv(
1e18,
_collateralInBorrowTerms,
OrigamiMath.Rounding.ROUND_UP
);
healthFactor = maxBorrow.mulDiv(1e18, borrowed, OrigamiMath.Rounding.ROUND_DOWN);
}
}
function _supply(uint256 supplyAmount, MorphoMarketParams memory marketParams, bytes memory data) internal {
morpho.supplyCollateral(marketParams, supplyAmount, address(this), data);
}
function _withdraw(uint256 withdrawAmount, address recipient, MorphoMarketParams memory marketParams) internal returns (uint256 amountWithdrawn) {
// If `withdrawAmount` == uint256.max, then set the the current supplied collateral balance
amountWithdrawn = withdrawAmount == type(uint256).max ? suppliedBalance() : withdrawAmount;
morpho.withdrawCollateral(marketParams, amountWithdrawn, address(this), recipient);
}
function _borrow(uint256 borrowAmount, address recipient, MorphoMarketParams memory marketParams) internal {
(uint256 assetsBorrowed,) = morpho.borrow(
marketParams, borrowAmount, 0, address(this), recipient
);
if (assetsBorrowed != borrowAmount) revert CommonEventsAndErrors.InvalidAmount(address(_borrowToken), assetsBorrowed);
}
function _repay(uint256 repayAmount, MorphoMarketParams memory marketParams, bytes memory data) internal returns (uint256 debtRepaidAmount) {
uint256 _debtBalance = debtBalance();
if (_debtBalance != 0) {
// If the repayment amount gte the current balance, then repay 100% of the debt.
if (repayAmount < _debtBalance) {
// Repay via the amount (not shares)
(debtRepaidAmount, ) = morpho.repay(marketParams, repayAmount, 0, address(this), data);
} else {
// Calculate the current morpho shares owed, and repay via the shares (not amount)
// Do this when equal to the debt balance to avoid Morpho rounding underflow
uint256 _repayShares = morpho.position(marketId, address(this)).borrowShares;
(debtRepaidAmount, ) = morpho.repay(marketParams, 0, _repayShares, address(this), data);
}
}
}
/**
* @dev Only the positionOwner or Elevated Access is allowed to call.
*/
modifier onlyPositionOwnerOrElevated() {
if (msg.sender != address(positionOwner)) {
if (!isElevatedAccess(msg.sender, msg.sig)) revert CommonEventsAndErrors.InvalidAccess();
}
_;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {MathLib} from "./MathLib.sol";
/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
using MathLib for uint256;
/// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
/// high precision computations.
/// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
/// stays low enough not to inflate these assets to a significant value.
/// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
uint256 internal constant VIRTUAL_SHARES = 1e6;
/// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
/// empty.
uint256 internal constant VIRTUAL_ASSETS = 1;
/// @dev Calculates the value of `assets` quoted in shares, rounding down.
function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding down.
function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
/// @dev Calculates the value of `assets` quoted in shares, rounding up.
function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding up.
function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
}
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact security@morpho.org
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
/// @dev Returns true if there is exactly one zero among `x` and `y`.
function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
/// @dev Returns the min of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `x` safely cast to uint128.
function toUint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max, ErrorsLib.MAX_UINT128_EXCEEDED);
return uint128(x);
}
/// @dev Returns max(0, x - y).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}
{
"compilationTarget": {
"contracts/common/borrowAndLend/OrigamiMorphoBorrowAndLend.sol": "OrigamiMorphoBorrowAndLend"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 10000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"_initialOwner","type":"address"},{"internalType":"address","name":"__supplyToken","type":"address"},{"internalType":"address","name":"__borrowToken","type":"address"},{"internalType":"address","name":"_morphoAddress","type":"address"},{"internalType":"address","name":"_morphoMarketOracle","type":"address"},{"internalType":"address","name":"_morphoMarketIrm","type":"address"},{"internalType":"uint96","name":"_morphoMarketLltv","type":"uint96"},{"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidAccess","type":"error"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"InvalidAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidParam","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"InvalidToken","type":"error"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"},{"internalType":"uint256","name":"denominator","type":"uint256"}],"name":"PRBMath_MulDiv_Overflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"minAmountExpected","type":"uint256"},{"internalType":"uint256","name":"actualAmount","type":"uint256"}],"name":"Slippage","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"indexed":true,"internalType":"bool","name":"value","type":"bool"}],"name":"ExplicitAccessSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"name":"MaxSafeLtvSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwnerAccepted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldOwner","type":"address"},{"indexed":true,"internalType":"address","name":"oldProposedOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newProposedOwner","type":"address"}],"name":"NewOwnerProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"PositionOwnerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"SurplusDebtReclaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"swapper","type":"address"}],"name":"SwapperSet","type":"event"},{"inputs":[],"name":"acceptOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"availableToBorrow","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"availableToSupply","outputs":[{"internalType":"uint256","name":"supplyCap","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"availableToWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtAccountData","outputs":[{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"collateralPrice","type":"uint256"},{"internalType":"uint256","name":"borrowed","type":"uint256"},{"internalType":"uint256","name":"maxBorrow","type":"uint256"},{"internalType":"uint256","name":"currentLtv","type":"uint256"},{"internalType":"uint256","name":"healthFactor","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"debtBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawCollateralAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"repaySurplusThreshold","type":"uint256"}],"name":"decreaseLeverage","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"},{"internalType":"uint256","name":"surplusDebtRepaid","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"bytes4","name":"","type":"bytes4"}],"name":"explicitFunctionAccess","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarketParams","outputs":[{"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":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"bytes","name":"swapData","type":"bytes"},{"internalType":"uint256","name":"supplyCollateralSurplusThreshold","type":"uint256"}],"name":"increaseLeverage","outputs":[{"internalType":"uint256","name":"totalCollateralSupplied","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"alRatio","type":"uint256"}],"name":"isSafeAlRatio","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketId","outputs":[{"internalType":"Id","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSafeLtv","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morpho","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketIrm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketLltv","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"morphoMarketOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoRepay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onMorphoSupplyCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"positionOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"proposeNewOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"recoverToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"repayAndWithdraw","outputs":[{"internalType":"uint256","name":"debtRepaidAmount","type":"uint256"},{"internalType":"uint256","name":"withdrawnAmount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"allowedCaller","type":"address"},{"components":[{"internalType":"bytes4","name":"fnSelector","type":"bytes4"},{"internalType":"bool","name":"allowed","type":"bool"}],"internalType":"struct IOrigamiElevatedAccess.ExplicitAccess[]","name":"access","type":"tuple[]"}],"name":"setExplicitAccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSafeLtv","type":"uint256"}],"name":"setMaxSafeLtv","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"setPositionOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"suppliedBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"}],"name":"supply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"supplyAmount","type":"uint256"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"supplyAndBorrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"supplyToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapper","outputs":[{"internalType":"contract IOrigamiSwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"withdrawAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"amountWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]