-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #106 from morpho-org/post-cantina
Post cantina
- Loading branch information
Showing
10 changed files
with
171 additions
and
167 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule morpho-blue
updated
63 files
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,12 @@ | |
pragma solidity 0.8.19; | ||
|
||
import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol"; | ||
import {IAdaptiveCurveIrm} from "./interfaces/IAdaptiveCurveIrm.sol"; | ||
|
||
import {UtilsLib} from "./libraries/UtilsLib.sol"; | ||
import {ErrorsLib} from "./libraries/ErrorsLib.sol"; | ||
import {MathLib, WAD_INT as WAD} from "./libraries/MathLib.sol"; | ||
import {ExpLib} from "./libraries/adaptive-curve/ExpLib.sol"; | ||
import {MathLib, WAD_INT as WAD} from "./libraries/MathLib.sol"; | ||
import {ConstantsLib} from "./libraries/adaptive-curve/ConstantsLib.sol"; | ||
import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol"; | ||
import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol"; | ||
|
@@ -15,11 +16,10 @@ import {MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib | |
/// @title AdaptiveCurveIrm | ||
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
contract AdaptiveCurveIrm is IIrm { | ||
contract AdaptiveCurveIrm is IAdaptiveCurveIrm { | ||
using MathLib for int256; | ||
using UtilsLib for int256; | ||
using MorphoMathLib for uint128; | ||
using MorphoMathLib for uint256; | ||
using MarketParamsLib for MarketParams; | ||
|
||
/* EVENTS */ | ||
|
@@ -29,63 +29,22 @@ contract AdaptiveCurveIrm is IIrm { | |
|
||
/* IMMUTABLES */ | ||
|
||
/// @notice Address of Morpho. | ||
/// @inheritdoc IAdaptiveCurveIrm | ||
address public immutable MORPHO; | ||
|
||
/// @notice Curve steepness (scaled by WAD). | ||
/// @dev Verified to be inside the expected range at construction. | ||
int256 public immutable CURVE_STEEPNESS; | ||
|
||
/// @notice Adjustment speed (scaled by WAD). | ||
/// @dev The speed is per second, so the rate moves at a speed of ADJUSTMENT_SPEED * err each second (while being | ||
/// continuously compounded). A typical value for the ADJUSTMENT_SPEED would be 10 ether / 365 days. | ||
/// @dev Verified to be inside the expected range at construction. | ||
int256 public immutable ADJUSTMENT_SPEED; | ||
|
||
/// @notice Target utilization (scaled by WAD). | ||
/// @dev Verified to be strictly between 0 and 1 at construction. | ||
int256 public immutable TARGET_UTILIZATION; | ||
|
||
/// @notice Initial rate at target per second (scaled by WAD). | ||
/// @dev Verified to be between MIN_RATE_AT_TARGET and MAX_RATE_AT_TARGET at contruction. | ||
int256 public immutable INITIAL_RATE_AT_TARGET; | ||
|
||
/* STORAGE */ | ||
|
||
/// @notice Rate at target utilization. | ||
/// @dev Tells the height of the curve. | ||
/// @inheritdoc IAdaptiveCurveIrm | ||
mapping(Id => int256) public rateAtTarget; | ||
|
||
/* CONSTRUCTOR */ | ||
|
||
/// @notice Constructor. | ||
/// @param morpho The address of Morpho. | ||
/// @param curveSteepness The curve steepness (scaled by WAD). | ||
/// @param adjustmentSpeed The adjustment speed (scaled by WAD). | ||
/// @param targetUtilization The target utilization (scaled by WAD). | ||
/// @param initialRateAtTarget The initial rate at target (scaled by WAD). | ||
constructor( | ||
address morpho, | ||
int256 curveSteepness, | ||
int256 adjustmentSpeed, | ||
int256 targetUtilization, | ||
int256 initialRateAtTarget | ||
) { | ||
constructor(address morpho) { | ||
require(morpho != address(0), ErrorsLib.ZERO_ADDRESS); | ||
require(curveSteepness >= WAD, ErrorsLib.INPUT_TOO_SMALL); | ||
require(curveSteepness <= ConstantsLib.MAX_CURVE_STEEPNESS, ErrorsLib.INPUT_TOO_LARGE); | ||
require(adjustmentSpeed >= 0, ErrorsLib.INPUT_TOO_SMALL); | ||
require(adjustmentSpeed <= ConstantsLib.MAX_ADJUSTMENT_SPEED, ErrorsLib.INPUT_TOO_LARGE); | ||
require(targetUtilization < WAD, ErrorsLib.INPUT_TOO_LARGE); | ||
require(targetUtilization > 0, ErrorsLib.ZERO_INPUT); | ||
require(initialRateAtTarget >= ConstantsLib.MIN_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_SMALL); | ||
require(initialRateAtTarget <= ConstantsLib.MAX_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_LARGE); | ||
|
||
MORPHO = morpho; | ||
CURVE_STEEPNESS = curveSteepness; | ||
ADJUSTMENT_SPEED = adjustmentSpeed; | ||
TARGET_UTILIZATION = targetUtilization; | ||
INITIAL_RATE_AT_TARGET = initialRateAtTarget; | ||
} | ||
|
||
/* BORROW RATES */ | ||
|
@@ -119,8 +78,10 @@ contract AdaptiveCurveIrm is IIrm { | |
int256 utilization = | ||
int256(market.totalSupplyAssets > 0 ? market.totalBorrowAssets.wDivDown(market.totalSupplyAssets) : 0); | ||
|
||
int256 errNormFactor = utilization > TARGET_UTILIZATION ? WAD - TARGET_UTILIZATION : TARGET_UTILIZATION; | ||
int256 err = (utilization - TARGET_UTILIZATION).wDivDown(errNormFactor); | ||
int256 errNormFactor = utilization > ConstantsLib.TARGET_UTILIZATION | ||
? WAD - ConstantsLib.TARGET_UTILIZATION | ||
: ConstantsLib.TARGET_UTILIZATION; | ||
int256 err = (utilization - ConstantsLib.TARGET_UTILIZATION).wDivToZero(errNormFactor); | ||
|
||
int256 startRateAtTarget = rateAtTarget[id]; | ||
|
||
|
@@ -129,12 +90,12 @@ contract AdaptiveCurveIrm is IIrm { | |
|
||
if (startRateAtTarget == 0) { | ||
// First interaction. | ||
avgRateAtTarget = INITIAL_RATE_AT_TARGET; | ||
endRateAtTarget = INITIAL_RATE_AT_TARGET; | ||
avgRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET; | ||
endRateAtTarget = ConstantsLib.INITIAL_RATE_AT_TARGET; | ||
} else { | ||
// Note that the speed is assumed constant between two interactions, but in theory it increases because of | ||
// interests. So the rate will be slightly underestimated. | ||
int256 speed = ADJUSTMENT_SPEED.wMulDown(err); | ||
// The speed is assumed constant between two updates, but it is in fact not constant because of interest. | ||
// So the rate is always underestimated. | ||
int256 speed = ConstantsLib.ADJUSTMENT_SPEED.wMulToZero(err); | ||
// market.lastUpdate != 0 because it is not the first interaction with this market. | ||
// Safe "unchecked" cast because block.timestamp - market.lastUpdate <= block.timestamp <= type(int256).max. | ||
int256 elapsed = int256(block.timestamp - market.lastUpdate); | ||
|
@@ -172,18 +133,18 @@ contract AdaptiveCurveIrm is IIrm { | |
/// The formula of the curve is the following: | ||
/// r = ((1-1/C)*err + 1) * rateAtTarget if err < 0 | ||
/// ((C-1)*err + 1) * rateAtTarget else. | ||
function _curve(int256 _rateAtTarget, int256 err) private view returns (int256) { | ||
function _curve(int256 _rateAtTarget, int256 err) private pure returns (int256) { | ||
// Non negative because 1 - 1/C >= 0, C - 1 >= 0. | ||
int256 coeff = err < 0 ? WAD - WAD.wDivDown(CURVE_STEEPNESS) : CURVE_STEEPNESS - WAD; | ||
int256 coeff = err < 0 ? WAD - WAD.wDivToZero(ConstantsLib.CURVE_STEEPNESS) : ConstantsLib.CURVE_STEEPNESS - WAD; | ||
// Non negative if _rateAtTarget >= 0 because if err < 0, coeff <= 1. | ||
return (coeff.wMulDown(err) + WAD).wMulDown(int256(_rateAtTarget)); | ||
return (coeff.wMulToZero(err) + WAD).wMulToZero(int256(_rateAtTarget)); | ||
} | ||
|
||
/// @dev Returns the new rate at target, for a given `startRateAtTarget` and a given `linearAdaptation`. | ||
/// The formula is: max(min(startRateAtTarget * exp(linearAdaptation), maxRateAtTarget), minRateAtTarget). | ||
function _newRateAtTarget(int256 startRateAtTarget, int256 linearAdaptation) private pure returns (int256) { | ||
// Non negative because MIN_RATE_AT_TARGET > 0. | ||
return startRateAtTarget.wMulDown(ExpLib.wExp(linearAdaptation)).bound( | ||
return startRateAtTarget.wMulToZero(ExpLib.wExp(linearAdaptation)).bound( | ||
ConstantsLib.MIN_RATE_AT_TARGET, ConstantsLib.MAX_RATE_AT_TARGET | ||
); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity >=0.5.0; | ||
|
||
import {IIrm} from "../../lib/morpho-blue/src/interfaces/IIrm.sol"; | ||
import {Id} from "../../lib/morpho-blue/src/interfaces/IMorpho.sol"; | ||
|
||
/// @title IAdaptiveCurveIrm | ||
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
/// @notice Interface exposed by the AdaptiveCurveIrm. | ||
interface IAdaptiveCurveIrm is IIrm { | ||
/// @notice Address of Morpho. | ||
function MORPHO() external view returns (address); | ||
|
||
/// @notice Rate at target utilization. | ||
/// @dev Tells the height of the curve. | ||
function rateAtTarget(Id id) external view returns (int256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,18 +6,9 @@ pragma solidity ^0.8.0; | |
/// @custom:contact [email protected] | ||
/// @notice Library exposing error messages. | ||
library ErrorsLib { | ||
/// @dev Thrown when the input is too large to fit in the expected type. | ||
string internal constant INPUT_TOO_LARGE = "input too large"; | ||
|
||
/// @dev Thrown when the input is too small. | ||
string internal constant INPUT_TOO_SMALL = "input too small"; | ||
|
||
/// @dev Thrown when passing the zero address. | ||
string internal constant ZERO_ADDRESS = "zero address"; | ||
|
||
/// @dev Thrown when passing the zero input. | ||
string internal constant ZERO_INPUT = "zero input"; | ||
|
||
/// @dev Thrown when the caller is not Morpho. | ||
string internal constant NOT_MORPHO = "not Morpho"; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,11 +10,13 @@ int256 constant WAD_INT = int256(WAD); | |
/// @custom:contact [email protected] | ||
/// @notice Library to manage fixed-point arithmetic on signed integers. | ||
library MathLib { | ||
function wMulDown(int256 a, int256 b) internal pure returns (int256) { | ||
return a * b / WAD_INT; | ||
/// @dev Returns the multiplication of `x` by `y` (in WAD) rounded towards 0. | ||
function wMulToZero(int256 x, int256 y) internal pure returns (int256) { | ||
return (x * y) / WAD_INT; | ||
} | ||
|
||
function wDivDown(int256 a, int256 b) internal pure returns (int256) { | ||
return a * WAD_INT / b; | ||
/// @dev Returns the division of `x` by `y` (in WAD) rounded towards 0. | ||
function wDivToZero(int256 x, int256 y) internal pure returns (int256) { | ||
return (x * WAD_INT) / y; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,29 @@ pragma solidity ^0.8.0; | |
/// @author Morpho Labs | ||
/// @custom:contact [email protected] | ||
library ConstantsLib { | ||
/// @notice Maximum rate at target per second (scaled by WAD) (1B% APR). | ||
int256 internal constant MAX_RATE_AT_TARGET = int256(0.01e9 ether) / 365 days; | ||
/// @notice Curve steepness (scaled by WAD). | ||
/// @dev Curve steepness = 4. | ||
int256 public constant CURVE_STEEPNESS = 4 ether; | ||
|
||
/// @notice Mininimum rate at target per second (scaled by WAD) (0.1% APR). | ||
int256 internal constant MIN_RATE_AT_TARGET = int256(0.001 ether) / 365 days; | ||
/// @notice Adjustment speed per second (scaled by WAD). | ||
/// @dev The speed is per second, so the rate moves at a speed of ADJUSTMENT_SPEED * err each second (while being | ||
/// continuously compounded). | ||
/// @dev Adjustment speed = 50/year. | ||
int256 public constant ADJUSTMENT_SPEED = 50 ether / int256(365 days); | ||
|
||
/// @notice Maximum curve steepness allowed (scaled by WAD). | ||
int256 internal constant MAX_CURVE_STEEPNESS = 100 ether; | ||
/// @notice Target utilization (scaled by WAD). | ||
/// @dev Target utilization = 90%. | ||
int256 public constant TARGET_UTILIZATION = 0.9 ether; | ||
|
||
/// @notice Maximum adjustment speed allowed (scaled by WAD). | ||
int256 internal constant MAX_ADJUSTMENT_SPEED = int256(1_000 ether) / 365 days; | ||
/// @notice Initial rate at target per second (scaled by WAD). | ||
/// @dev Initial rate at target = 4% (rate between 1% and 16%). | ||
int256 public constant INITIAL_RATE_AT_TARGET = 0.04 ether / int256(365 days); | ||
|
||
/// @notice Minimum rate at target per second (scaled by WAD). | ||
/// @dev Minimum rate at target = 0.1% (minimum rate = 0.025%). | ||
int256 public constant MIN_RATE_AT_TARGET = 0.001 ether / int256(365 days); | ||
|
||
/// @notice Maximum rate at target per second (scaled by WAD). | ||
/// @dev Maximum rate at target = 200% (maximum rate = 800%). | ||
int256 public constant MAX_RATE_AT_TARGET = 2.0 ether / int256(365 days); | ||
} |
Oops, something went wrong.