Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add digit to nSigma so it's more governable #153

Merged
merged 14 commits into from
Sep 11, 2023
Merged
26 changes: 13 additions & 13 deletions core/.gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
BorrowerGasTest:test_addMargin() (gas: 16203)
BorrowerGasTest:test_borrow() (gas: 110565)
BorrowerGasTest:test_borrow() (gas: 110577)
BorrowerGasTest:test_getUniswapPositions() (gas: 5219)
BorrowerGasTest:test_modify() (gas: 82162)
BorrowerGasTest:test_modifyWithAnte() (gas: 88617)
BorrowerGasTest:test_repay() (gas: 111924)
BorrowerGasTest:test_uniswapDepositInBorrower() (gas: 257777)
BorrowerGasTest:test_modify() (gas: 82174)
BorrowerGasTest:test_modifyWithAnte() (gas: 88629)
BorrowerGasTest:test_repay() (gas: 111936)
BorrowerGasTest:test_uniswapDepositInBorrower() (gas: 257789)
BorrowerGasTest:test_uniswapDepositStandard() (gas: 167558)
BorrowerGasTest:test_uniswapWithdraw() (gas: 147664)
BorrowerGasTest:test_withdraw() (gas: 105682)
BorrowerGasTest:test_uniswapWithdraw() (gas: 147673)
BorrowerGasTest:test_withdraw() (gas: 105694)
FactoryGasTest:test_createBorrower() (gas: 156519)
FactoryGasTest:test_createMarket() (gas: 3903606)
FactoryGasTest:test_createMarket() (gas: 3904606)
LenderGasTest:test_accrueInterest() (gas: 46070)
LenderGasTest:test_borrow() (gas: 40834)
LenderGasTest:test_deposit() (gas: 53422)
LenderGasTest:test_depositWithCourier() (gas: 53568)
LenderGasTest:test_redeem() (gas: 53114)
LenderGasTest:test_redeemWithCourier() (gas: 83506)
LenderGasTest:test_repay() (gas: 44774)
LiquidatorGasTest:test_noCallbackOneAsset() (gas: 51093)
LiquidatorGasTest:test_noCallbackTwoAssets() (gas: 59274)
LiquidatorGasTest:test_noCallbackTwoAssetsAndUniswapPosition() (gas: 95674)
LiquidatorGasTest:test_warn() (gas: 35077)
LiquidatorGasTest:test_withCallbackAndSwap() (gas: 130258)
LiquidatorGasTest:test_noCallbackOneAsset() (gas: 51105)
LiquidatorGasTest:test_noCallbackTwoAssets() (gas: 59286)
LiquidatorGasTest:test_noCallbackTwoAssetsAndUniswapPosition() (gas: 95684)
LiquidatorGasTest:test_warn() (gas: 35089)
LiquidatorGasTest:test_withCallbackAndSwap() (gas: 130270)
VolatilityGasTest:test_consult() (gas: 43075)
VolatilityGasTest:test_updateNoBinarySearch() (gas: 145902)
2 changes: 1 addition & 1 deletion core/src/libraries/BalanceSheet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ library BalanceSheet {
uint56 metric
) internal pure returns (uint160 a, uint160 b, bool isSus) {
unchecked {
iv = SoladyMath.clamp(nSigma * iv, PROBE_PERCENT_MIN, PROBE_PERCENT_MAX);
iv = SoladyMath.clamp((nSigma * iv) / 10, PROBE_PERCENT_MIN, PROBE_PERCENT_MAX);
isSus = metric > _manipulationThreshold(_effectiveCollateralFactor(iv));

a = uint160((sqrtMeanPriceX96 * SoladyMath.sqrt(1e12 - iv)) / 1e6);
Expand Down
1 change: 1 addition & 0 deletions core/src/libraries/Volatility.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ library Volatility {

/**
* @notice Estimates implied volatility using [this math](https://lambert-guillaume.medium.com/on-chain-volatility-and-uniswap-v3-d031b98143d1).
* @dev The return value can fit in uint128 if necessary
* @param metadata The pool's metadata (may be cached)
* @param data A summary of the pool's state from `pool.slot0` `pool.observe` and `pool.liquidity`
* @param a The pool's cumulative feeGrowthGlobals some time in the past
Expand Down
16 changes: 9 additions & 7 deletions core/src/libraries/constants/Constants.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@ uint256 constant MAX_RATE = 706354;
uint216 constant DEFAULT_ANTE = 0.01 ether;

/// @dev The default number of standard deviations of price movement used to determine probe prices for `Borrower`
/// solvency. The `Factory` can override this value on a per-market basis.
uint8 constant DEFAULT_N_SIGMA = 5;
/// solvency. The `Factory` can override this value on a per-market basis. Expressed x10, e.g. 50 → 5σ
uint8 constant DEFAULT_N_SIGMA = 50;

/// @dev The default portion of interest that will accrue to a `Lender`'s `RESERVE` address.
/// Expressed as a reciprocal, e.g. 16 → 6.25%
Expand All @@ -43,11 +43,13 @@ uint8 constant DEFAULT_RESERVE_FACTOR = 16;
GOVERNANCE CONSTRAINTS
//////////////////////////////////////////////////////////////*/

/// @dev The lowest number of standard deviations of price movement allowed for determining `Borrower` probe prices
uint8 constant CONSTRAINT_N_SIGMA_MIN = 4;
/// @dev The lowest number of standard deviations of price movement allowed for determining `Borrower` probe prices.
/// Expressed x10, e.g. 40 → 4σ
uint8 constant CONSTRAINT_N_SIGMA_MIN = 40;

/// @dev The highest number of standard deviations of price movement allowed for determining `Borrower` probe prices
uint8 constant CONSTRAINT_N_SIGMA_MAX = 5;
/// @dev The highest number of standard deviations of price movement allowed for determining `Borrower` probe prices.
/// Expressed x10, e.g. 80 → 8σ
uint8 constant CONSTRAINT_N_SIGMA_MAX = 80;

/// @dev The lower bound on what any `Lender`'s reserve factor can be. Expressed as reciprocal, e.g. 4 → 25%
uint8 constant CONSTRAINT_RESERVE_FACTOR_MIN = 4;
Expand Down Expand Up @@ -108,7 +110,7 @@ uint32 constant IV_SCALE = 24 hours;
/// @dev The initial value of implied volatility, used when `VolatilityOracle.prepare` is called for a new pool.
/// Expressed as a 1e12 percentage at `IV_SCALE`, e.g. {0.20e12, 24 hours} → 20% daily → 382% annual. Error on the
/// side of making this too large (resulting in low LTV).
uint128 constant IV_COLD_START = uint128(PROBE_PERCENT_MAX / DEFAULT_N_SIGMA);
uint128 constant IV_COLD_START = uint128((PROBE_PERCENT_MAX * 10) / CONSTRAINT_N_SIGMA_MIN);

/// @dev The maximum rate at which (reported) implied volatility can change. Raw samples in `VolatilityOracle.update`
/// are clamped (before being stored) so as not to exceed this rate.
Expand Down
96 changes: 50 additions & 46 deletions core/test/libraries/BalanceSheet.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,109 +23,109 @@ contract BalanceSheetTest is Test {
function test_spec_computeProbePrices() public {
bool isSus;

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.00e12, 5, 87);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.00e12, 50, 87);
assertFalse(isSus, "0.00 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.00e12, 5, 88);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.00e12, 50, 88);
assertTrue(isSus, "0.00 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.01e12, 5, 87);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.01e12, 50, 87);
assertFalse(isSus, "0.01 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.01e12, 5, 88);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.01e12, 50, 88);
assertTrue(isSus, "0.01 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.02e12, 5, 132);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.02e12, 50, 132);
assertFalse(isSus, "0.02 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.02e12, 5, 133);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.02e12, 50, 133);
assertTrue(isSus, "0.02 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.03e12, 5, 180);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.03e12, 50, 180);
assertFalse(isSus, "0.03 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.03e12, 5, 181);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.03e12, 50, 181);
assertTrue(isSus, "0.03 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.04e12, 5, 230);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.04e12, 50, 230);
assertFalse(isSus, "0.04 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.04e12, 5, 231);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.04e12, 50, 231);
assertTrue(isSus, "0.04 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.05e12, 5, 284);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.05e12, 50, 284);
assertFalse(isSus, "0.05 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.05e12, 5, 285);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.05e12, 50, 285);
assertTrue(isSus, "0.05 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.06e12, 5, 341);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.06e12, 50, 341);
assertFalse(isSus, "0.06 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.06e12, 5, 342);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.06e12, 50, 342);
assertTrue(isSus, "0.06 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.07e12, 5, 403);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.07e12, 50, 403);
assertFalse(isSus, "0.07 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.07e12, 5, 404);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.07e12, 50, 404);
assertTrue(isSus, "0.07 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.08e12, 5, 470);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.08e12, 50, 470);
assertFalse(isSus, "0.08 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.08e12, 5, 471);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.08e12, 50, 471);
assertTrue(isSus, "0.08 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.09e12, 5, 542);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.09e12, 50, 542);
assertFalse(isSus, "0.09 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.09e12, 5, 543);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.09e12, 50, 543);
assertTrue(isSus, "0.09 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.10e12, 5, 622);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.10e12, 50, 622);
assertFalse(isSus, "0.10 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.10e12, 5, 623);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.10e12, 50, 623);
assertTrue(isSus, "0.10 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.11e12, 5, 710);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.11e12, 50, 710);
assertFalse(isSus, "0.11 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.11e12, 5, 711);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.11e12, 50, 711);
assertTrue(isSus, "0.11 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.12e12, 5, 808);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.12e12, 50, 808);
assertFalse(isSus, "0.12 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.12e12, 5, 809);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.12e12, 50, 809);
assertTrue(isSus, "0.12 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.13e12, 5, 919);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.13e12, 50, 919);
assertFalse(isSus, "0.13 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.13e12, 5, 920);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.13e12, 50, 920);
assertTrue(isSus, "0.13 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.14e12, 5, 1048);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.14e12, 50, 1048);
assertFalse(isSus, "0.14 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.14e12, 5, 1049);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.14e12, 50, 1049);
assertTrue(isSus, "0.14 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.15e12, 5, 1199);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.15e12, 50, 1199);
assertFalse(isSus, "0.15 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.15e12, 5, 1200);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.15e12, 50, 1200);
assertTrue(isSus, "0.15 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.16e12, 5, 1385);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.16e12, 50, 1385);
assertFalse(isSus, "0.16 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.16e12, 5, 1386);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.16e12, 50, 1386);
assertTrue(isSus, "0.16 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.17e12, 5, 1625);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.17e12, 50, 1625);
assertFalse(isSus, "0.17 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.17e12, 5, 1626);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.17e12, 50, 1626);
assertTrue(isSus, "0.17 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.18e12, 5, 1918);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.18e12, 50, 1918);
assertFalse(isSus, "0.18 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.18e12, 5, 1919);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.18e12, 50, 1919);
assertTrue(isSus, "0.18 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, 0.19e12, 5, 1918);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.19e12, 50, 1918);
assertFalse(isSus, "0.19 false");
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.19e12, 5, 1919);
(, , isSus) = BalanceSheet.computeProbePrices(0, 0.19e12, 50, 1919);
assertTrue(isSus, "0.19 true");

(, , isSus) = BalanceSheet.computeProbePrices(0, IV_COLD_START, 5, 1918);
(, , isSus) = BalanceSheet.computeProbePrices(0, IV_COLD_START, 50, 1918);
assertFalse(isSus, "cold start false");
(, , isSus) = BalanceSheet.computeProbePrices(0, IV_COLD_START, 5, 1919);
(, , isSus) = BalanceSheet.computeProbePrices(0, IV_COLD_START, 50, 1919);
assertTrue(isSus, "cold start true");
}

Expand All @@ -147,18 +147,22 @@ contract BalanceSheetTest is Test {
a = square(uint160(a));
b = square(uint160(b));

if (iv < PROBE_PERCENT_MIN / nSigma) iv = PROBE_PERCENT_MIN / nSigma;
else if (iv > PROBE_PERCENT_MAX / nSigma) iv = PROBE_PERCENT_MAX / nSigma;
if (iv < PROBE_PERCENT_MIN * 10 / nSigma) iv = PROBE_PERCENT_MIN * 10 / nSigma;
else if (iv > PROBE_PERCENT_MAX * 10 / nSigma) iv = PROBE_PERCENT_MAX * 10 / nSigma;

assertApproxEqRel(a, SoladyMath.fullMulDiv(price, 1e12 - nSigma * iv, 1e12), 0.0001e18);
assertApproxEqRel(b, SoladyMath.fullMulDiv(price, 1e12 + nSigma * iv, 1e12), 0.0001e18);
assertApproxEqRel(a, SoladyMath.fullMulDiv(price, 1e12 - (nSigma * iv) / 10, 1e12), 0.0001e18);
assertApproxEqRel(b, SoladyMath.fullMulDiv(price, 1e12 + (nSigma * iv) / 10, 1e12), 0.0001e18);
}

function test_constants() public {
// Just checking that things are reasonable
assertEqDecimal(PROBE_PERCENT_MIN, 50500000000, 12);
assertEqDecimal(PROBE_PERCENT_MAX, 894500000000, 12);

// Necessary for iv scaling not to overflow
assertLt(PROBE_PERCENT_MIN, 1 << 128);
assertLt(PROBE_PERCENT_MAX, 1 << 128);

// Necessary for collateral factor computation to work
assertGt(LTV_MIN, TickMath.MIN_SQRT_RATIO);
}
Expand Down