Skip to content

Commit

Permalink
Merge branch 'master' into max-repay-and-redeem
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenshively authored Sep 8, 2023
2 parents 7a5dd62 + 5ac00b9 commit 0b7e67b
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 81 deletions.
2 changes: 1 addition & 1 deletion core/.gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ LiquidatorGasTest:test_noCallbackTwoAssetsAndUniswapPosition() (gas: 95598)
LiquidatorGasTest:test_warn() (gas: 35026)
LiquidatorGasTest:test_withCallbackAndSwap() (gas: 130185)
VolatilityGasTest:test_consult() (gas: 43075)
VolatilityGasTest:test_updateNoBinarySearch() (gas: 146696)
VolatilityGasTest:test_updateNoBinarySearch() (gas: 145880)
110 changes: 54 additions & 56 deletions core/src/libraries/Volatility.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
pragma solidity 0.8.17;

import {Math} from "openzeppelin-contracts/contracts/utils/math/Math.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {FixedPointMathLib as SoladyMath} from "solady/utils/FixedPointMathLib.sol";

import {square, mulDiv96, mulDiv128, mulDiv224} from "./MulDiv.sol";
import {Oracle} from "./Oracle.sol";
Expand Down Expand Up @@ -46,37 +46,36 @@ library Volatility {
FeeGrowthGlobals memory b,
uint256 scale
) internal pure returns (uint256) {
unchecked {
if (data.secondsPerLiquidityX128 == 0 || b.timestamp - a.timestamp == 0) return 0;

uint128 revenue0Gamma1 = computeRevenueGamma(
a.feeGrowthGlobal0X128,
b.feeGrowthGlobal0X128,
data.secondsPerLiquidityX128,
data.oracleLookback,
metadata.gamma1
);
uint128 revenue1Gamma0 = computeRevenueGamma(
a.feeGrowthGlobal1X128,
b.feeGrowthGlobal1X128,
data.secondsPerLiquidityX128,
data.oracleLookback,
metadata.gamma0
);
// This is an approximation. Ideally the fees earned during each swap would be multiplied by the price
// *at that swap*. But for prices simulated with GBM and swap sizes either normally or uniformly distributed,
// the error you get from using geometric mean price is <1% even with high drift and volatility.
uint256 volumeGamma0Gamma1 = revenue1Gamma0 + amount0ToAmount1(revenue0Gamma1, data.sqrtMeanPriceX96);

// Fits in uint128
uint256 sqrtTickTVLX32 = FixedPointMathLib.sqrt(
computeTickTVLX64(metadata.tickSpacing, data.currentTick, data.sqrtPriceX96, data.tickLiquidity)
);
if (sqrtTickTVLX32 == 0) return 0;
uint256 tickTvl = computeTickTvl(metadata.tickSpacing, data.currentTick, data.sqrtPriceX96, data.tickLiquidity);

// Return early to avoid division by 0
if (data.secondsPerLiquidityX128 == 0 || b.timestamp - a.timestamp == 0 || tickTvl == 0) return 0;

uint256 revenue0Gamma1 = computeRevenueGamma(
a.feeGrowthGlobal0X128,
b.feeGrowthGlobal0X128,
data.secondsPerLiquidityX128,
data.oracleLookback,
metadata.gamma1
);
uint256 revenue1Gamma0 = computeRevenueGamma(
a.feeGrowthGlobal1X128,
b.feeGrowthGlobal1X128,
data.secondsPerLiquidityX128,
data.oracleLookback,
metadata.gamma0
);
// This is an approximation. Ideally the fees earned during each swap would be multiplied by the price
// *at that swap*. But for prices simulated with GBM and swap sizes either normally or uniformly distributed,
// the error you get from using geometric mean price is <1% even with high drift and volatility.
uint256 volumeGamma0Gamma1 = revenue1Gamma0 + amount0ToAmount1(revenue0Gamma1, data.sqrtMeanPriceX96);
// Clamp to prevent overflow later on
if (volumeGamma0Gamma1 > (1 << 128)) volumeGamma0Gamma1 = (1 << 128);

// Fits in uint48
uint256 timeAdjustmentX32 = FixedPointMathLib.sqrt((scale << 64) / (b.timestamp - a.timestamp));
return (uint256(2e18) * timeAdjustmentX32 * FixedPointMathLib.sqrt(volumeGamma0Gamma1)) / sqrtTickTVLX32;
unchecked {
// Scale volume to the target time frame
volumeGamma0Gamma1 = (volumeGamma0Gamma1 * scale) / (b.timestamp - a.timestamp);
return SoladyMath.sqrt((4e36 * volumeGamma0Gamma1) / tickTvl);
}
}

Expand All @@ -86,7 +85,7 @@ library Volatility {
* @param sqrtPriceX96 The sqrt(price) at which the conversion should hold true
* @return amount1 An equivalent amount of token1
*/
function amount0ToAmount1(uint128 amount0, uint160 sqrtPriceX96) internal pure returns (uint256 amount1) {
function amount0ToAmount1(uint256 amount0, uint160 sqrtPriceX96) internal pure returns (uint256 amount1) {
uint256 priceX128 = square(sqrtPriceX96);
amount1 = mulDiv128(amount0, priceX128);
}
Expand All @@ -106,20 +105,19 @@ library Volatility {
uint160 secondsPerLiquidityX128,
uint32 secondsAgo,
uint24 gamma
) internal pure returns (uint128) {
) internal pure returns (uint256) {
unchecked {
uint256 temp;
uint256 delta;

if (feeGrowthGlobalBX128 >= feeGrowthGlobalAX128) {
// feeGrowthGlobal has increased from time A to time B
temp = feeGrowthGlobalBX128 - feeGrowthGlobalAX128;
delta = feeGrowthGlobalBX128 - feeGrowthGlobalAX128;
} else {
// feeGrowthGlobal has overflowed between time A and time B
temp = type(uint256).max - feeGrowthGlobalAX128 + feeGrowthGlobalBX128;
delta = type(uint256).max - feeGrowthGlobalAX128 + feeGrowthGlobalBX128;
}

temp = Math.mulDiv(temp, secondsAgo * gamma, secondsPerLiquidityX128 * 1e6);
return temp > type(uint128).max ? type(uint128).max : uint128(temp);
return Math.mulDiv(delta, secondsAgo * uint256(gamma), secondsPerLiquidityX128 * uint256(1e6));
}
}

Expand All @@ -130,47 +128,47 @@ library Volatility {
* @param sqrtPriceX96 The current price (from pool.slot0())
* @param liquidity The liquidity depth at currentTick (from pool.liquidity())
*/
function computeTickTVLX64(
function computeTickTvl(
int24 tickSpacing,
int24 tick,
uint160 sqrtPriceX96,
uint128 liquidity
) internal pure returns (uint256 tickTVL) {
tick = TickMath.floor(tick, tickSpacing);

// both value0 and value1 fit in uint192
(uint256 value0, uint256 value1) = _getValueOfLiquidity(
sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tick),
TickMath.getSqrtRatioAtTick(tick + tickSpacing),
liquidity
);
tickTVL = (value0 + value1) << 64;
) internal pure returns (uint256 tickTvl) {
unchecked {
tick = TickMath.floor(tick, tickSpacing);

tickTvl = _getValueOfLiquidity(
sqrtPriceX96,
TickMath.getSqrtRatioAtTick(tick),
TickMath.getSqrtRatioAtTick(tick + tickSpacing),
liquidity
);
}
}

/**
* @notice Computes the value of the liquidity in terms of token1
* @dev Each return value can fit in a uint192 if necessary
* @dev The return value can fit in uint193 if necessary
* @param sqrtRatioX96 A sqrt price representing the current pool prices
* @param sqrtRatioAX96 A sqrt price representing the lower tick boundary
* @param sqrtRatioBX96 A sqrt price representing the upper tick boundary
* @param liquidity The liquidity being valued
* @return value0 The value of amount0 underlying `liquidity`, in terms of token1
* @return value1 The amount of token1
* @return value The total value of `liquidity`, in terms of token1
*/
function _getValueOfLiquidity(
uint160 sqrtRatioX96,
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity
) private pure returns (uint256 value0, uint256 value1) {
) private pure returns (uint256 value) {
assert(sqrtRatioAX96 <= sqrtRatioX96 && sqrtRatioX96 <= sqrtRatioBX96);

unchecked {
uint256 numerator = Math.mulDiv(uint256(liquidity) << 128, sqrtRatioX96, sqrtRatioBX96);

value0 = mulDiv224(numerator, sqrtRatioBX96 - sqrtRatioX96);
value1 = mulDiv96(liquidity, sqrtRatioX96 - sqrtRatioAX96);
value =
mulDiv224(numerator, sqrtRatioBX96 - sqrtRatioX96) +
mulDiv96(liquidity, sqrtRatioX96 - sqrtRatioAX96);
}
}
}
43 changes: 22 additions & 21 deletions core/test/libraries/Volatility.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ contract VolatilityTest is Test {
),
1 days
);
assertEq(dailyIV, 20405953567249984); // 2.041%
assertEq(dailyIV, 20405953715139097); // 2.041%

dailyIV = Volatility.estimate(
metadata,
Expand All @@ -52,7 +52,7 @@ contract VolatilityTest is Test {
),
1 hours
);
assertEq(dailyIV, 4165347829168579); // 0.417%
assertEq(dailyIV, 4165347859745125); // 0.417%

dailyIV = Volatility.estimate(
metadata,
Expand All @@ -65,7 +65,7 @@ contract VolatilityTest is Test {
),
1 days
);
assertEq(dailyIV, 6970260198990240); // 0.697%
assertEq(dailyIV, 6970260375825778); // 0.697%

dailyIV = Volatility.estimate(
metadata,
Expand Down Expand Up @@ -162,7 +162,7 @@ contract VolatilityTest is Test {
}

function test_spec_computeRevenueGamma() public {
uint128 revenueGamma = Volatility.computeRevenueGamma(11111111111, 222222222222, 3975297179, 5000, 100);
uint256 revenueGamma = Volatility.computeRevenueGamma(11111111111, 222222222222, 3975297179, 5000, 100);
assertEq(revenueGamma, 26);
}

Expand All @@ -175,19 +175,19 @@ contract VolatilityTest is Test {
Volatility.computeRevenueGamma(feeGrowthGlobalAX128, feeGrowthGlobalBX128, secondsPerLiquidityX128, 5000, 100);
}

function test_spec_computeTickTVL() public {
uint256 tickTVL;
tickTVL = Volatility.computeTickTVLX64(1, 19000, TickMath.getSqrtRatioAtTick(19000), 100000000000);
assertEq(tickTVL, 238460396558056720196173824);
tickTVL = Volatility.computeTickTVLX64(10, 19000, TickMath.getSqrtRatioAtTick(19000), 9763248618769789);
assertEq(tickTVL, 232762454487181009148555451957248);
tickTVL = Volatility.computeTickTVLX64(60, -19000, TickMath.getSqrtRatioAtTick(-19000), 100000000000);
assertEq(tickTVL, 2138446074761944812648136704);
tickTVL = Volatility.computeTickTVLX64(60, -3000, TickMath.getSqrtRatioAtTick(-3000), 999999999);
assertEq(tickTVL, 47558380999913911951032320);
function test_spec_computeTickTvl() public {
uint256 tickTvl;
tickTvl = Volatility.computeTickTvl(1, 19000, TickMath.getSqrtRatioAtTick(19000), 100000000000);
assertEq(tickTvl, 12926964);
tickTvl = Volatility.computeTickTvl(10, 19000, TickMath.getSqrtRatioAtTick(19000), 9763248618769789);
assertEq(tickTvl, 12618077941403);
tickTvl = Volatility.computeTickTvl(60, -19000, TickMath.getSqrtRatioAtTick(-19000), 100000000000);
assertEq(tickTvl, 115925394);
tickTvl = Volatility.computeTickTvl(60, -3000, TickMath.getSqrtRatioAtTick(-3000), 999999999);
assertEq(tickTvl, 2578145);
}

function test_computeTickTVL(
function test_computeTickTvl(
int24 currentTick,
uint8 tickSpacing,
uint128 tickLiquidity
Expand All @@ -200,15 +200,15 @@ contract VolatilityTest is Test {
uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(currentTick);

// Ensure it doesn't revert
uint256 tickTVL = Volatility.computeTickTVLX64(_tickSpacing, currentTick, sqrtPriceX96, tickLiquidity);
uint256 tickTvl = Volatility.computeTickTvl(_tickSpacing, currentTick, sqrtPriceX96, tickLiquidity);

// Check that it's non-zero in cases where we don't expect truncation
int24 lowerBound = TickMath.MIN_TICK / 2;
int24 upperBound = TickMath.MAX_TICK / 2;
if (tickLiquidity > 1_000_000 && currentTick < lowerBound && currentTick > upperBound) assertGt(tickTVL, 0);
if (tickLiquidity > 1_000_000 && currentTick < lowerBound && currentTick > upperBound) assertGt(tickTvl, 0);
}

function test_noRevert_computeTickTVLX64(
function test_noRevert_computeTickTvl(
uint8 tier,
int24 tick,
uint128 liquidity
Expand All @@ -225,7 +225,7 @@ contract VolatilityTest is Test {

uint160 sqrtPriceX96 = TickMath.getSqrtRatioAtTick(tick);

Volatility.computeTickTVLX64(
Volatility.computeTickTvl(
tickSpacing,
tick,
sqrtPriceX96,
Expand Down Expand Up @@ -263,8 +263,8 @@ contract VolatilityTest is Test {
uint32 feeGrowthSampleAge,
uint256 feeGrowthGlobalA0X128,
uint256 feeGrowthGlobalA1X128,
uint224 feeGrowthGlobalDelta0X128,
uint224 feeGrowthGlobalDelta1X128,
uint96 feeGrowthGlobalDelta0X128,
uint128 feeGrowthGlobalDelta1X128,
uint48 gammas,
int24 tick,
int24 arithmeticMeanTick,
Expand All @@ -275,6 +275,7 @@ contract VolatilityTest is Test {
) public view {
scale = uint32(bound(scale, 1 minutes, 2 days));
oracleLookback = uint32(bound(oracleLookback, 15 seconds, 1 days));
secondsPerLiquidityX128 = uint160(bound(secondsPerLiquidityX128, feeGrowthSampleAge, type(uint160).max));

Volatility.PoolMetadata memory metadata = Volatility.PoolMetadata(
uint24(gammas % (1 << 24)),
Expand Down
10 changes: 7 additions & 3 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
- [📘 BalanceSheet](core/libraries/BalanceSheet.sol/library.BalanceSheet.md)
- [📘 LiquidityAmounts](core/libraries/LiquidityAmounts.sol/library.LiquidityAmounts.md)
- [📘 LiquidityAmounts](core/libraries/LiquidityAmounts.sol/library.LiquidityAmounts.md)
- [⚛︎ mulDiv96](core/libraries/LiquidityAmounts.sol/function.mulDiv96.md)
- [📑 Log2](core/libraries/Log2.sol/Log2.md)
- [🗂️ Log2](core/libraries/Log2.sol/Log2.md)
- [⚛︎ msb](core/libraries/Log2.sol/function.msb.md)
- [⚛︎ log2](core/libraries/Log2.sol/function.log2.md)
- [⚛︎ log2Up](core/libraries/Log2.sol/function.log2Up.md)
- [⚛︎ exp2](core/libraries/Log2.sol/function.exp2.md)
- [🗂️ MulDiv](core/libraries/MulDiv.sol/MulDiv.md)
- [⚛︎ square](core/libraries/MulDiv.sol/function.square.md)
- [⚛︎ mulDiv96](core/libraries/MulDiv.sol/function.mulDiv96.md)
- [⚛︎ mulDiv128](core/libraries/MulDiv.sol/function.mulDiv128.md)
- [⚛︎ mulDiv224](core/libraries/MulDiv.sol/function.mulDiv224.md)
- [📘 Oracle](core/libraries/Oracle.sol/library.Oracle.md)
- [📘 Positions](core/libraries/Positions.sol/library.Positions.md)
- [📘 Positions](core/libraries/Positions.sol/library.Positions.md)
Expand Down Expand Up @@ -44,10 +48,10 @@
- [📃 LenderAccrualHelper](periphery/helpers/LenderAccrualHelper.sol/contract.LenderAccrualHelper.md)
- [📃 OracleUpdateHelper](periphery/helpers/OracleUpdateHelper.sol/contract.OracleUpdateHelper.md)
- [🗂️ managers](periphery/managers/README.md)
- [📃 BoostManager](periphery/managers/BoostManager.sol/contract.BoostManager.md)
- [📃 FrontendManager](periphery/managers/FrontendManager.sol/contract.FrontendManager.md)
- [📃 SimpleManager](periphery/managers/SimpleManager.sol/contract.SimpleManager.md)
- [📃 UniswapNFTManager](periphery/managers/UniswapNFTManager.sol/contract.UniswapNFTManager.md)
- [📃 WithdrawManager](periphery/managers/WithdrawManager.sol/contract.WithdrawManager.md)
- [🔎 BorrowerLens](periphery/BorrowerLens.sol/contract.BorrowerLens.md)
- [🔎 LenderLens](periphery/LenderLens.sol/contract.LenderLens.md)
- [📃 Router](periphery/Router.sol/contract.Router.md)
Empty file.

0 comments on commit 0b7e67b

Please sign in to comment.