fluid-contracts-public/test/foundry/libraries/liquidityCalcs/liquidityCalcsBorrowLimit.t.sol
2024-07-11 13:05:09 +00:00

1109 lines
44 KiB
Solidity

//SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import { LibraryLiquidityCalcsBaseTest } from "./liquidityCalcsBaseTest.t.sol";
import { BigMathMinified } from "../../../../contracts/libraries/bigMathMinified.sol";
import "forge-std/console2.sol";
contract LibraryLiquidityCalcsBorrowLimitsBaseTest is LibraryLiquidityCalcsBaseTest {
uint256 constant DEFAULT_EXPAND_PERCENTAGE = 20 * 1e2; // 20%
uint256 constant DEFAULT_BASE_LIMIT = 5 ether;
uint256 constant DEFAULT_MAX_LIMIT = 10 ether;
uint256 constant DEFAULT_EXPAND_DURATION = 200 seconds;
uint256 immutable DEFAULT_BASE_LIMIT_AFTER_BIG_MATH;
uint256 immutable DEFAULT_MAX_LIMIT_AFTER_BIG_MATH;
constructor() {
DEFAULT_BASE_LIMIT_AFTER_BIG_MATH = BigMathMinified.fromBigNumber(
BigMathMinified.toBigNumber(DEFAULT_BASE_LIMIT, 10, _DEFAULT_EXPONENT_SIZE, BigMathMinified.ROUND_DOWN),
8,
0xff
);
DEFAULT_MAX_LIMIT_AFTER_BIG_MATH = BigMathMinified.fromBigNumber(
BigMathMinified.toBigNumber(DEFAULT_MAX_LIMIT, 10, _DEFAULT_EXPONENT_SIZE, BigMathMinified.ROUND_DOWN),
8,
0xff
);
}
function setUp() public virtual override {
super.setUp();
vm.warp(block.timestamp + 10_000); // skip ahead to not cause an underflow for last update timestamp
}
function _getUserBorrowDataForBeforeOperate(
uint256 userBorrow,
uint256 previousLimit,
uint256 lastUpdateTimestamp,
uint256 expandPercentage,
uint256 expandDuration,
uint256 baseLimit,
uint256 maxLimit
) internal pure returns (uint256) {
return
_simulateUserBorrowDataFull(
1, // interest mode irrelevant for borrow limit calculation
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false // user pause status irrelevant for borrow limit calculation
);
}
function _getUserBorrowDataForAfterOperate(
uint256 userBorrow,
uint256 expandPercentage,
uint256 expandDuration,
uint256 baseLimit,
uint256 maxLimit
) internal pure returns (uint256) {
return
_simulateUserBorrowDataFull(
1, // interest mode irrelevant for borrow limit calculation
userBorrow,
0, // previous limit in storage should never matter for after operate limit
0, // last update timestamp in storage should never matter for after operate limit
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false // user pause status irrelevant for borrow limit calculation
);
}
}
contract LibraryLiquidityCalcsBorrowLimitBeforeOperateTests is LibraryLiquidityCalcsBorrowLimitsBaseTest {
function test_calcBorrowLimitBeforeOperate_WhenLastUpdateTimestampIsZero() public {
uint256 userBorrow = 0;
uint256 previousLimit = 0;
uint256 lastUpdateTimestamp = 0;
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH); // limit should be base limit
}
function test_calcBorrowLimitBeforeOperate_WhenFullExpansionIsBelowBaseLimit() public {
uint256 userBorrow = 2 ether; // full expansion will be 2.8 ether
uint256 previousLimit = 0;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // fully expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH); // limit should be base limit
}
function test_calcBorrowLimitBeforeOperate_WhenFullExpansionIsExactlyBaseLimit() public {
uint256 userBorrow = 4.8 ether;
uint256 previousLimit = DEFAULT_BASE_LIMIT_AFTER_BIG_MATH;
uint256 lastUpdateTimestamp = block.timestamp; // 0 difference so limit should be previous limit
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH); // limit should be base limit
}
function test_calcBorrowLimitBeforeOperate_WhenLastUpdateTimestampIsBlockTimestamp() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 6 ether;
uint256 lastUpdateTimestamp = block.timestamp; // 0 difference so limit should be previous limit
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, previousLimit);
}
function test_calcBorrowLimitBeforeOperate_WhenExpandPercentZero() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 8 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // fully expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
0, // expand percent = 0
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, userBorrow); // should be user Borrow
}
function test_calcBorrowLimitBeforeOperate_WhenExpandPercent100() public {
uint256 userBorrow = 7 ether; // full expansion will be 14 ether
uint256 previousLimit = 8 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // fully expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
100 * 1e2, // expand percent = 100%
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
100 ether // ignore max limit for this test
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, 14 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenExpandPercentMax() public {
uint256 userBorrow = 7 ether; // full expansion will be 18,4681 ether
uint256 previousLimit = 8 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 2; // half expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
16_383, // expand percent = 163,83%. max expand percent is 16_383 (14 bits)
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
100 ether // ignore max limit for this test
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// total expansion is 11,4681 ether. half of that has elapsed so 5,73405 ether. starting from 8 ether.
// so limit should be 8 ether + 5,73405 ether = 13,73405 ether
assertEq(limit, 13.73405 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenShrinking() public {
// because of new payback -> shrinking. previous limit higher than expansion
uint256 userBorrow = 6 ether; // full expansion will be 7.2 ether
uint256 previousLimit = 8 ether;
uint256 lastUpdateTimestamp = block.timestamp - 1;
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// shrinking must be instant
assertEq(limit, 7.2 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenShrinkingMaxExpansionAbovePreviousLimit() public {
uint256 userBorrow = 6 ether; // full expansion will be 7.2 ether
uint256 previousLimit = 7 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 10; // 10% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// 10% expanded of 1.2 ether = 0.12 ether, starting from 7 ether. so limit should be 7.12 ether
assertEq(limit, 7.12 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenExpanding() public {
// because of new borrow -> expansion. previous limit lower than expansion
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 7.5 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 5; // 20% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// 20% expanded of 1.4 ether = 0.28 ether, starting from 7.5 ether. so limit should be 7.78 ether
assertEq(limit, 7.78 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenExpandingBorrowedExactlyToLimit() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 7 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, 8.4 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenShrinkingToBelowBaseLimit() public {
uint256 userBorrow = 4 ether; // full expansion will be 4.8 ether
uint256 previousLimit = 8 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// shrinking to base limit is instant
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitBeforeOperate_WhenMoreThanExpandDurationElapsed() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 7.5 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION * 5; // way more than 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, 8.4 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenUserBorrow0() public {
uint256 userBorrow = 0;
uint256 previousLimit = 0;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// limit should be base limit
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitBeforeOperate_When10PercentOf30PercentExpanded() public {
uint256 userBorrow = 7 ether; // full expansion will be 9.1 ether
uint256 previousLimit = 7.5 ether;
uint256 expandPercentage = 30 * 1e2;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 10; // 10% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// 10% expanded of 2.1 ether = 0.21 ether
assertEq(limit, 7.71 ether);
}
function test_calcBorrowLimitBeforeOperate_When10PercentOf30PercentExpandedFromPreviousLimitHigher() public {
uint256 userBorrow = 7 ether; // full expansion will be 9.1 ether
uint256 previousLimit = 10 ether;
uint256 expandPercentage = 30 * 1e2;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 10; // 10% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// shrinked to max expansion immediately because last limit > max expansion
assertEq(limit, 9.1 ether);
}
function test_calcBorrowLimitBeforeOperate_When50PercentOf20PercentExpanded() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 7.5 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION / 2; // 50% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// 50% expanded of 1.4 ether = 0.7 ether
assertEq(limit, 8.2 ether);
}
function test_calcBorrowLimitBeforeOperate_When80PercentOf20PercentExpanded() public {
uint256 userBorrow = 7 ether; // full expansion will be 8.4 ether
uint256 previousLimit = 7.5 ether;
uint256 lastUpdateTimestamp = block.timestamp - (DEFAULT_EXPAND_DURATION * 8) / 10; // 80% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
// 80% expanded of 1.4 ether = 1.12 ether. starting from 7.5 ether so already above max expansion.
assertEq(limit, 8.4 ether);
}
function test_calcBorrowLimitBeforeOperate_WhenAboveHardMaxLimit() public {
uint256 userBorrow = 9 ether; // full expansion will be 10.8 ether
uint256 previousLimit = 9 ether;
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, DEFAULT_MAX_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitBeforeOperate_WhenAboveHardMaxLimitWithPreviousLimitAbove() public {
uint256 userBorrow = 9 ether; // full expansion will be 10.8 ether
uint256 previousLimit = 11 ether; // should not change outcome of limit being max hard cap
uint256 lastUpdateTimestamp = block.timestamp - DEFAULT_EXPAND_DURATION; // 100% expanded
uint256 userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
previousLimit,
lastUpdateTimestamp,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, DEFAULT_MAX_LIMIT_AFTER_BIG_MATH);
}
}
contract LibraryLiquidityCalcsBorrowLimitAfterOperateTests is LibraryLiquidityCalcsBorrowLimitsBaseTest {
function test_calcBorrowLimitAfterOperate_WhenUserBorrow0() public {
// limit should be base limit
uint256 userBorrow = 0;
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 0; // irrelevant for this test
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitAfterOperate_WhenMaxExpansionIsBelowBaseLimit() public {
// limit should be base limit
uint256 userBorrow = 4 ether; // max expansion is to 4.8 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 0; // irrelevant for this test
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitAfterOperate_WhenMaxExpansionIsExactlyBaseLimit() public {
// limit should be base limit
uint256 userBorrow = DEFAULT_BASE_LIMIT_AFTER_BIG_MATH; // max expansion is to 4.8 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
0, // 0% expansion
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether; // limit before operate can only ever be >= userBorrow.
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitAfterOperate_WhenExpandPercent0() public {
uint256 userBorrow = 6 ether;
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
0, // 0% expansion
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 6 ether;
// limit before operate can only ever be >= userBorrow.
// because even for payback, limit beforeOperate would be userBorrow before operate. (so > than after because of payback)
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, userBorrow);
}
function test_calcBorrowLimitAfterOperate_WhenExpandPercent0WithPreviousLimitHigher() public {
uint256 userBorrow = 6 ether;
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
0, // 0% expansion
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, userBorrow);
}
function test_calcBorrowLimitAfterOperate_WhenExpandPercent100() public {
uint256 userBorrow = 6 ether;
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
100 * 1e2, // 100% expansion
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 8 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, 8 ether); // should be previous limit and expand from there
// cross-test expansion at next beforeOperate limit
userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
limit,
block.timestamp - DEFAULT_EXPAND_DURATION / 10, // 10% of 6 ether expanded
100 * 1e2, // 100% expansion
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, 8.6 ether);
}
function test_calcBorrowLimitAfterOperate_WhenExpandPercentMax() public {
uint256 userBorrow = 6 ether;
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
16_383, // expand percent = 163,83%. max expand percent is 16_383 (14 bits)
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, limitFromBeforeOperate); // should be previous limit and expand from there
}
function test_calcBorrowLimitAfterOperate_WhenMaxExpandedIsAboveHardMaxLimit() public {
uint256 userBorrow = 9 ether; // would expand to 10.8 ether (above hard max of 10 ether)
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 12 ether; // shrinking to max hard limit should be instant
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, DEFAULT_MAX_LIMIT_AFTER_BIG_MATH);
}
function test_calcBorrowLimitAfterOperate_WhenShrinkingInstantlyOnRepay() public {
// shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant)
uint256 userBorrow = 6 ether; // expands to 7.2 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 9 ether; // shrinking to max expansion limit should be instant
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, 7.2 ether);
}
function test_calcBorrowLimitAfterOperate_WhenExpandingFromLimitBeforeOperate() public {
// previous limit will be limit before operate. E.g. new Borrow that causes expansion
uint256 userBorrow = 6 ether; // expands to 7.2 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 6.5 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, limitFromBeforeOperate);
}
function test_calcBorrowLimitAfterOperate_WhenSimulateBigBorrowToLimit() public {
// borrow to full limit
uint256 userBorrow = 6 ether; // expands to 7.2 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 6 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, limitFromBeforeOperate);
// cross-test expansion at next beforeOperate limit
userBorrowData = _getUserBorrowDataForBeforeOperate(
userBorrow,
limit,
block.timestamp - DEFAULT_EXPAND_DURATION / 10, // 10% of 1.2 ether expanded
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
limit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
assertEq(limit, 6.12 ether);
}
function test_calcBorrowLimitAfterOperate_WhenSimulateSmallBorrow() public {
// e.g. user borrow before was 5.83333 ether fully expanded to 7 ether borrow limit
uint256 userBorrow = 6 ether; // expands to 7.2 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, limitFromBeforeOperate);
}
function test_calcBorrowLimitAfterOperate_WhenSimulatePayback() public {
// e.g. user borrow before was 5.83333 ether fully expanded to 7 ether borrow limit
uint256 userBorrow = 5.5 ether; // expands to 6.6 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, 6.6 ether); // should be shrinked to fully expanded instantly
}
function test_calcBorrowLimitAfterOperate_WhenSimulateBigPaybackToBelowBaseLimit() public {
// e.g. user borrow before was 5.83333 ether fully expanded to 7 ether borrow limit
uint256 userBorrow = 4 ether; // expands to 4.8 ether
uint256 userBorrowData = _getUserBorrowDataForAfterOperate(
userBorrow,
DEFAULT_EXPAND_PERCENTAGE,
DEFAULT_EXPAND_DURATION,
DEFAULT_BASE_LIMIT,
DEFAULT_MAX_LIMIT
);
uint256 limitFromBeforeOperate = 7 ether;
uint256 limit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, limitFromBeforeOperate);
assertEq(limit, DEFAULT_BASE_LIMIT_AFTER_BIG_MATH); // should be shrinked to base borrow limit instantly
}
}
contract LibraryLiquidityCalcsBorrowLimitTests is LibraryLiquidityCalcsBaseTest {
function test_calcBorrowLimit_CombinationSequence() public {
vm.warp(block.timestamp + 10_000); // skip ahead to not cause an underflow for last update timestamp
uint256 expandPercentage = 20 * 1e2; // 20%
uint256 baseLimit = 5 ether;
uint256 maxLimit = 7 ether;
uint256 expandDuration = 200 seconds;
console2.log("-------------------------------");
console2.log("Config expandPercentage 20%", expandPercentage);
console2.log("Config baseLimit 5 ether", baseLimit);
console2.log("Config maxLimit 7 ether", maxLimit);
console2.log("Config expandDuration 200 seconds", expandDuration);
uint256 previousLimit = 0;
uint256 lastUpdateTimestamp = 0;
console2.log(
"\n--------- Simulate 1. action: borrow of 4.18 ether, expands to 5.01 (above base limit) ---------"
);
uint256 userBorrow = 0;
uint256 userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, baseLimit, 1e16); // allow BigMath precision delta
userBorrow = 4.18 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, baseLimit, 1e16); // allow BigMath precision delta
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 200);
console2.log("--------- TIME WARP 200 seconds ---------");
console2.log("\n--------- Simulate 2. action: borrow of 0.82 ether to 5 ether total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertEq(previousLimit, 5.016 ether); // fully expanded from 4.18 to 5.016 ether (not base limit)
userBorrow += 0.82 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertEq(previousLimit, 5.016 ether); // fully expanded from 4.18 to 5.016 ether (not base limit)
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 97); // tiny bit less than half to get closest to 5.5 & make up for 0.016 already as last limit
console2.log("--------- TIME WARP 97 seconds (half expanded) ---------");
console2.log("\n--------- Simulate 3. action: borrow of 0.5 ether to 5.5 ether total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 5.5 ether, 1e16); // allow BigMath precision delta
userBorrow += 0.5 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 5.5 ether, 1e16); // allow BigMath precision delta
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 1);
console2.log("--------- TIME WARP 1 seconds ---------");
console2.log("\n--------- Simulate 4. action: payback 0.01 ether to total 5.49 ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 5.5 ether, 1e16); // allow BigMath precision delta
userBorrow -= 0.01 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 5.5 ether, 1e16); // allow BigMath precision delta right after still 5.5 ether
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 200);
console2.log("--------- TIME WARP 200 seconds (full expansion to 6.588 limit) ---------");
console2.log("\n--------- Simulate 5. action: borrow of 1.01 ether to 6.5 ether total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 6.588 ether, 1e16); // 5.49 * 1.2 -> 6,588
userBorrow += 1.01 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 6.588 ether, 1e16); // 5.49 * 1.2 -> 6,588
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 200);
console2.log(
"--------- TIME WARP 200 seconds (max expansion to 7.8 ether but max limit of 7 ether gets active) ---------"
);
console2.log("\n--------- Simulate 6. action: borrow 0.49 ether up to max limit of 7 total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 7 ether, 1e16); // max limit of 7 ether with BigMath imprecision
userBorrow += 0.49 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 7 ether, 1e16); // max limit of 7 ether with BigMath imprecision
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 200);
console2.log("--------- TIME WARP 200 seconds ---------");
console2.log(
"\n--------- Simulate 7. action: borrow 0.01 ether would fail even after expansion (above max limit) ---------"
);
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 7 ether, 1e16, "Limit is not ~7 ether");
console2.log("\n--------- Simulate 8. action: payback 1.49 ether down to 5.5 total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 7 ether, 1e16, "Limit is not ~7 ether");
userBorrow -= 1.49 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 6.6 ether, 1e16, "Limit is not ~6.6 ether"); // immediately shrinked to full expansion 5.5 * 1.2 = 6.6
lastUpdateTimestamp = block.timestamp;
vm.warp(block.timestamp + 1);
console2.log("--------- TIME WARP 1 seconds ---------");
console2.log("\n--------- Simulate 9. action: payback 5.5 ether down to 0 total ---------");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit", previousLimit);
assertApproxEqAbs(previousLimit, 6.6 ether, 1e16, "Limit is not ~6.6 ether"); // immediately shrinked to full expansion 5.5 * 1.2 = 6.6
userBorrow -= 5.5 ether;
console2.log("userBorrow", userBorrow);
assertEq(userBorrow < previousLimit, true, "USER BORROW IS > LIMIT");
userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitAfterOperate(userBorrowData, userBorrow, previousLimit);
console2.log("AFTER operate limit", previousLimit);
assertApproxEqAbs(previousLimit, baseLimit, 1e16, "Limit is not baseLimit"); // immediately shrinked to base limit
}
function test_calcBorrowLimit_CalcBorrowLimitFirstAction() public {
vm.warp(block.timestamp + 10_000); // skip ahead to not cause an underflow for last update timestamp
uint256 expandPercentage = 20 * 1e2; // 20%
uint256 baseLimit = 5 ether;
uint256 maxLimit = 7 ether;
uint256 expandDuration = 200 seconds;
uint256 previousLimit = 0; // previous limit is not set at config
uint256 lastUpdateTimestamp = 0; // last update timestamp is 0 at first interaction (not set at config)
uint256 userBorrow = 0; // user borrow amount at first action can only be 0 still
uint256 userBorrowData = _simulateUserBorrowDataFull(
1,
userBorrow,
previousLimit,
lastUpdateTimestamp,
expandPercentage,
expandDuration,
baseLimit,
maxLimit,
false
);
previousLimit = testHelper.calcBorrowLimitBeforeOperate(userBorrowData, userBorrow);
console2.log("BEFORE operate limit first action (should be ~5 ether base limit)", previousLimit);
assertApproxEqAbs(previousLimit, baseLimit, 1e16); // allow BigMath precision delta
}
}