Skip to content

Commit

Permalink
fix(constructor): add parameter bounds
Browse files Browse the repository at this point in the history
  • Loading branch information
Rubilmax committed Nov 10, 2023
1 parent 7f09d96 commit 032e75a
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 23 deletions.
24 changes: 11 additions & 13 deletions src/SpeedJumpIrm.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IIrm} from "../lib/morpho-blue/src/interfaces/IIrm.sol";
import {UtilsLib} from "./libraries/UtilsLib.sol";
import {ErrorsLib} from "./libraries/ErrorsLib.sol";
import {MathLib, WAD_INT} from "./libraries/MathLib.sol";
import {AdaptativeCurveIrmLib} from "./libraries/AdaptativeCurveIrmLib.sol";
import {MarketParamsLib} from "../lib/morpho-blue/src/libraries/MarketParamsLib.sol";
import {Id, MarketParams, Market} from "../lib/morpho-blue/src/interfaces/IMorpho.sol";
import {WAD, MathLib as MorphoMathLib} from "../lib/morpho-blue/src/libraries/MathLib.sol";
Expand All @@ -28,19 +29,15 @@ contract AdaptativeCurveIrm is IIrm {

/* CONSTANTS */

/// @notice Maximum rate at target per second (scaled by WAD) (1B% APR).
uint256 public constant MAX_RATE_AT_TARGET = uint256(0.01e9 ether) / 365 days;
/// @notice Mininimum rate at target per second (scaled by WAD) (0.1% APR).
uint256 public constant MIN_RATE_AT_TARGET = uint256(0.001 ether) / 365 days;
/// @notice Address of Morpho.
address public immutable MORPHO;
/// @notice Curve steepness (scaled by WAD).
/// @dev Verified to be greater than 1 at construction.
/// @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 ethers / 365 days.
/// @dev Verified to be non-negative at construction.
/// 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.
Expand Down Expand Up @@ -70,13 +67,13 @@ contract AdaptativeCurveIrm is IIrm {
uint256 initialRateAtTarget
) {
require(morpho != address(0), ErrorsLib.ZERO_ADDRESS);
require(curveSteepness <= uint256(type(int256).max), ErrorsLib.INPUT_TOO_LARGE);
require(curveSteepness <= AdaptativeCurveIrmLib.MAX_CURVE_STEEPNESS, ErrorsLib.INPUT_TOO_LARGE);
require(curveSteepness >= WAD, ErrorsLib.INPUT_TOO_SMALL);
require(adjustmentSpeed <= uint256(type(int256).max), ErrorsLib.INPUT_TOO_LARGE);
require(adjustmentSpeed <= AdaptativeCurveIrmLib.MAX_ADJUSTMENT_SPEED, ErrorsLib.INPUT_TOO_LARGE);
require(targetUtilization < WAD, ErrorsLib.INPUT_TOO_LARGE);
require(targetUtilization > 0, ErrorsLib.ZERO_INPUT);
require(initialRateAtTarget >= MIN_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_SMALL);
require(initialRateAtTarget <= MAX_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_LARGE);
require(initialRateAtTarget >= AdaptativeCurveIrmLib.MIN_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_SMALL);
require(initialRateAtTarget <= AdaptativeCurveIrmLib.MAX_RATE_AT_TARGET, ErrorsLib.INPUT_TOO_LARGE);

MORPHO = morpho;
// Safe "unchecked" cast.
Expand Down Expand Up @@ -137,8 +134,9 @@ contract AdaptativeCurveIrm is IIrm {
int256 linearAdaptation = speed * int256(elapsed);
uint256 adaptationMultiplier = MathLib.wExp(linearAdaptation);
// endRateAtTarget is bounded between MIN_RATE_AT_TARGET and MAX_RATE_AT_TARGET.
uint256 endRateAtTarget =
startRateAtTarget.wMulDown(adaptationMultiplier).bound(MIN_RATE_AT_TARGET, MAX_RATE_AT_TARGET);
uint256 endRateAtTarget = startRateAtTarget.wMulDown(adaptationMultiplier).bound(
AdaptativeCurveIrmLib.MIN_RATE_AT_TARGET, AdaptativeCurveIrmLib.MAX_RATE_AT_TARGET
);
uint256 endBorrowRate = _curve(endRateAtTarget, err);

// Then we compute the average rate over the period.
Expand Down
16 changes: 16 additions & 0 deletions src/libraries/AdaptativeCurveIrmLib.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

library AdaptativeCurveIrmLib {
/// @notice Maximum rate at target per second (scaled by WAD) (1B% APR).
uint256 internal constant MAX_RATE_AT_TARGET = uint256(0.01e9 ether) / 365 days;

/// @notice Mininimum rate at target per second (scaled by WAD) (0.1% APR).
uint256 internal constant MIN_RATE_AT_TARGET = uint256(0.001 ether) / 365 days;

/// @notice Maximum curve steepness allowed (scaled by WAD).
uint256 internal constant MAX_CURVE_STEEPNESS = 10_000 ether;

/// @notice Maximum adjustment speed allowed (scaled by WAD).
uint256 internal constant MAX_ADJUSTMENT_SPEED = uint256(10_000 ether) / 365 days;
}
23 changes: 13 additions & 10 deletions test/SpeedJumpIrmTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract AdaptativeCurveIrmTest is Test {

event BorrowRateUpdate(Id indexed id, uint256 avgBorrowRate, uint256 rateAtTarget);

int256 internal constant CURVE_STEEPNESS = 4 ether;
uint256 internal constant CURVE_STEEPNESS = 4 ether;
int256 internal constant ADJUSTMENT_SPEED = 50 ether / int256(365 days);
int256 internal constant TARGET_UTILIZATION = 0.9 ether;
uint256 internal constant INITIAL_RATE_AT_TARGET = 0.01 ether / uint256(365 days);
Expand All @@ -26,7 +26,7 @@ contract AdaptativeCurveIrmTest is Test {

function setUp() public {
irm =
new AdaptativeCurveIrm(address(this), uint256(CURVE_STEEPNESS), uint256(ADJUSTMENT_SPEED), uint256(TARGET_UTILIZATION), INITIAL_RATE_AT_TARGET);
new AdaptativeCurveIrm(address(this), CURVE_STEEPNESS, uint256(ADJUSTMENT_SPEED), uint256(TARGET_UTILIZATION), INITIAL_RATE_AT_TARGET);
vm.warp(90 days);
}

Expand Down Expand Up @@ -172,16 +172,16 @@ contract AdaptativeCurveIrmTest is Test {
assertApproxEqRel(irm.rateAtTarget(marketParams.id()), expectedRateAtTarget, 0.001 ether, "rateAtTarget");
}

function testWExpWMulDownMaxRate() public view {
MathLib.wExp(MathLib.WEXP_UPPER_BOUND).wMulDown(irm.MAX_RATE_AT_TARGET());
function testWExpWMulDownMaxRate() public pure {
MathLib.wExp(MathLib.WEXP_UPPER_BOUND).wMulDown(AdaptativeCurveIrmLib.MAX_RATE_AT_TARGET);
}

function invariantMinRateAtTarget() public {
Market memory market;
market.totalBorrowAssets = 9 ether;
market.totalSupplyAssets = 10 ether;
assertGt(
irm.borrowRate(marketParams, market), uint256(int256(irm.MIN_RATE_AT_TARGET()).wDivDown(CURVE_STEEPNESS))
irm.borrowRate(marketParams, market), AdaptativeCurveIrmLib.MIN_RATE_AT_TARGET.wDivDown(CURVE_STEEPNESS)
);
}

Expand All @@ -190,7 +190,7 @@ contract AdaptativeCurveIrmTest is Test {
market.totalBorrowAssets = 9 ether;
market.totalSupplyAssets = 10 ether;
assertLt(
irm.borrowRate(marketParams, market), uint256(int256(irm.MAX_RATE_AT_TARGET()).wMulDown(CURVE_STEEPNESS))
irm.borrowRate(marketParams, market), AdaptativeCurveIrmLib.MAX_RATE_AT_TARGET.wMulDown(CURVE_STEEPNESS)
);
}

Expand All @@ -201,7 +201,9 @@ contract AdaptativeCurveIrmTest is Test {
int256 linearAdaptation = speed * int256(elapsed);
uint256 adaptationMultiplier = MathLib.wExp(linearAdaptation);
return (rateAtTarget > 0)
? rateAtTarget.wMulDown(adaptationMultiplier).bound(irm.MIN_RATE_AT_TARGET(), irm.MAX_RATE_AT_TARGET())
? rateAtTarget.wMulDown(adaptationMultiplier).bound(
AdaptativeCurveIrmLib.MIN_RATE_AT_TARGET, AdaptativeCurveIrmLib.MAX_RATE_AT_TARGET
)
: INITIAL_RATE_AT_TARGET;
}

Expand Down Expand Up @@ -229,12 +231,13 @@ contract AdaptativeCurveIrmTest is Test {
// Safe "unchecked" cast because err >= -1 (in WAD).
if (err < 0) {
return uint256(
(WAD_INT - WAD_INT.wDivDown(CURVE_STEEPNESS)).wMulDown(int256(rateAtTarget)).wMulDown(err)
(WAD_INT - WAD_INT.wDivDown(int256(CURVE_STEEPNESS))).wMulDown(int256(rateAtTarget)).wMulDown(err)
+ int256(rateAtTarget)
);
} else {
return
uint256((CURVE_STEEPNESS - WAD_INT).wMulDown(int256(rateAtTarget)).wMulDown(err) + int256(rateAtTarget));
return uint256(
(int256(CURVE_STEEPNESS) - WAD_INT).wMulDown(int256(rateAtTarget)).wMulDown(err) + int256(rateAtTarget)
);
}
}

Expand Down

0 comments on commit 032e75a

Please sign in to comment.