diff --git a/foundry.toml b/foundry.toml index 11cd564e..745c98f3 100644 --- a/foundry.toml +++ b/foundry.toml @@ -9,7 +9,7 @@ solc_version = "0.8.22" # these are defaults. explicitly setting them here for clarity. libs = ["lib"] out = "out/" -via-ir = true +via-ir = false [fmt] bracket_spacing = true diff --git a/src/GeometricMean/G3MExtendedLib.sol b/src/GeometricMean/G3MExtendedLib.sol index 5c7ae879..416a9b78 100644 --- a/src/GeometricMean/G3MExtendedLib.sol +++ b/src/GeometricMean/G3MExtendedLib.sol @@ -17,7 +17,7 @@ function computeLGivenX( uint256 x, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { int256 a = int256(params.wY.divWadUp(params.wX).mulWadUp(S)); int256 b = a.powWad(int256(params.wY)); return x.mulWadUp(uint256(b)); @@ -27,7 +27,7 @@ function computeLGivenY( uint256 y, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { return y.mulWadUp(params.wX).divWadUp(params.wY.mulWadUp(S)); } @@ -35,7 +35,7 @@ function computeXGivenL( uint256 L, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { return params.wX.mulWadUp(L).divWadUp(params.wY.mulWadUp(S)); } @@ -43,7 +43,7 @@ function computeYGivenL( uint256 L, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { return params.wY.mulWadUp(L).divWadUp(params.wX.mulWadUp(S)); } @@ -51,7 +51,7 @@ function computeY( uint256 x, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { return params.wY.divWadDown(params.wX).mulWadDown(S).mulWadDown(x); } @@ -59,7 +59,7 @@ function computeX( uint256 y, uint256 S, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { return params.wX.divWadDown(params.wY.mulWadDown(S)).mulWadDown(y); } @@ -67,7 +67,7 @@ function computeL( uint256 x, uint256 y, GeometricMeanParams memory params -) pure returns (uint256) { +) view returns (uint256) { uint256 a = uint256(int256(x).powWad(int256(params.wX))); uint256 b = uint256(int256(y).powWad(int256(params.wY))); @@ -78,7 +78,7 @@ function computeInitialPoolData( uint256 amountX, uint256 initialPrice, GeometricMeanParams memory params -) pure returns (bytes memory) { +) view returns (bytes memory) { uint256 rY = computeY(amountX, initialPrice, params); uint256 L = computeL(amountX, rY, params); @@ -100,7 +100,7 @@ function computeNextRy( uint256 rX, uint256 liquidity, GeometricMeanParams memory params -) pure returns (uint256 rY) { +) view returns (uint256 rY) { rY = uint256( int256( liquidity.divWadUp(uint256(int256(rX).powWad(int256(params.wX)))) @@ -113,7 +113,7 @@ function computeNextRx( uint256 rY, uint256 liquidity, GeometricMeanParams memory params -) pure returns (uint256 rX) { +) view returns (uint256 rX) { rX = uint256( int256( liquidity.divWadUp(uint256(int256(rY).powWad(int256(params.wY)))) @@ -126,19 +126,19 @@ function computePrice( uint256 rX, uint256 rY, GeometricMeanParams memory params -) pure returns (uint256 price) { +) view returns (uint256 price) { uint256 n = rY.divWadDown(params.wY); uint256 d = rX.divWadDown(params.wX); price = n.divWadDown(d); } -/// @dev This is a pure anonymous function defined at the file level, which allows +/// @dev This is a view anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this /// function as an argument to find the root of the trading function given the liquidity. function findRootLiquidity( bytes memory data, uint256 L -) pure returns (int256) { +) view returns (int256) { (uint256 rX, uint256 rY,, GeometricMeanParams memory params) = abi.decode(data, (uint256, uint256, int256, GeometricMeanParams)); return GeometricMeanLib.tradingFunction({ @@ -149,7 +149,7 @@ function findRootLiquidity( }); } -function findRootLower(bytes memory data, uint256 v) pure returns (int256) { +function findRootLower(bytes memory data, uint256 v) view returns (int256) { ( uint256 S, uint256 rX, @@ -162,7 +162,7 @@ function findRootLower(bytes memory data, uint256 v) pure returns (int256) { return diffLower({ S: S, rX: rX, rY: rY, L: L, v: v, params: params }); } -function findRootRaise(bytes memory data, uint256 v) pure returns (int256) { +function findRootRaise(bytes memory data, uint256 v) view returns (int256) { ( uint256 S, uint256 rX, @@ -175,6 +175,80 @@ function findRootRaise(bytes memory data, uint256 v) pure returns (int256) { return diffRaise({ S: S, rX: rX, rY: rY, L: L, v: v, params: params }); } +struct DiffLowerStruct { + uint256 wX; + uint256 rX; + uint256 rY; + uint256 L; + uint256 v; + uint256 yOverXPowWx; + uint256 yOverXPowWy; + uint256 gamma; +} + +function computeDiffLowerNumerator(DiffLowerStruct memory params) + view + returns (uint256) +{ + uint256 first = params.L.mulWadDown(params.wX).mulWadDown(params.rX) + .mulWadDown(params.yOverXPowWx); + uint256 second = (params.v - params.v.mulWadDown(params.wX) + params.rX) + .mulWadDown(params.rY).mulWadDown(ONE - params.gamma); + uint256 third = + uint256(int256(params.v + params.rX).powWad(-int256(params.wX))); + uint256 fourth = params.L + + params.v.mulWadDown(params.yOverXPowWy).mulWadDown(ONE - params.gamma); + return (first - second).mulWadDown( + uint256( + int256(third.mulWadDown(fourth)).powWad( + int256(ONE.divWadDown(ONE - params.wX)) + ) + ) + ); +} + +function computeDiffLowerDenominator(DiffLowerStruct memory params) + view + returns (uint256) +{ + uint256 dFirst = ONE - params.wX; + uint256 dSecond = params.v + params.rX; + uint256 dThird = + params.L.mulWadDown(params.rX).mulWadDown(uint256(params.yOverXPowWx)); + uint256 dFourth = + params.v.mulWadDown(params.rY).mulWadDown(ONE - params.gamma); + return dFirst.mulWadDown(dSecond).mulWadDown(dThird + dFourth); +} + +function computeDiffLowerResult( + uint256 wX, + uint256 wY, + uint256 rX, + uint256 rY, + uint256 S, + uint256 L, + uint256 v, + uint256 gamma +) view returns (int256) { + int256 yOverX = int256(rY.divWadDown(rX)); + + DiffLowerStruct memory params = DiffLowerStruct({ + wX: wX, + rX: rX, + rY: rY, + L: L, + v: v, + yOverXPowWx: uint256(yOverX.powWad(int256(wX))), + yOverXPowWy: uint256(yOverX.powWad(int256(wY))), + gamma: gamma + }); + + uint256 numerator = computeDiffLowerNumerator(params); + uint256 denominator = computeDiffLowerDenominator(params); + + return -int256(S) + int256(numerator.divWadDown(denominator)); +} + // todo(matt): refactor this to only use int256 function diffLower( uint256 S, @@ -183,41 +257,68 @@ function diffLower( uint256 L, uint256 v, GeometricMeanParams memory params -) pure returns (int256) { - (int256 wx, int256 wy) = (int256(params.wX), int256(params.wY)); +) view returns (int256) { uint256 gamma = ONE - params.swapFee; - int256 yOverX = int256(rY.divWadDown(rX)); - uint256 yOverXPowWx = uint256(yOverX.powWad(wx)); - uint256 yOverXPowWy = uint256(yOverX.powWad(wy)); + return computeDiffLowerResult(params.wX, params.wY, rX, rY, S, L, v, gamma); +} - uint256 numerator; - { - uint256 first = - L.mulWadDown(params.wX).mulWadDown(rX).mulWadDown(yOverXPowWx); - uint256 second = (v - v.mulWadDown(params.wX) + rX).mulWadDown(rY) - .mulWadDown(ONE - gamma); - uint256 third = uint256(int256(v + rX).powWad(-wx)); - uint256 fourth = L + v.mulWadDown(yOverXPowWy).mulWadDown(ONE - gamma); - numerator = (first - second).mulWadDown( - uint256( - int256(third.mulWadDown(fourth)).powWad( - int256(ONE.divWadDown(ONE - params.wX)) - ) - ) - ); - } +function computeDiffRaiseNumerator(DiffRaiseStruct memory params) + view + returns (int256) +{ + int256 first = + int256(params.wX).wadMul(int256(params.v) + int256(params.rY)); + int256 third = (params.vPlusYPow.wadMul(params.lMinusVTimesXOverYPowWx)) + .powWad(I_ONE.wadDiv(int256(params.wX))); + int256 fourth = int256(params.L).wadMul(-I_ONE + int256(params.wX)); + int256 fifth = params.xOverYPowWx.wadMul( + int256(params.v).wadMul(int256(params.wX)) + int256(params.rY) + ).wadMul(-I_ONE + params.gamma); + return first.wadMul(params.lMinusVTimesXOverYPowWx) + + int256(params.S).wadMul(third).wadMul(fourth - fifth); +} - uint256 denominator; - { - uint256 dFirst = ONE - params.wX; - uint256 dSecond = v + rX; - uint256 dThird = L.mulWadDown(rX).mulWadDown(uint256(yOverXPowWx)); - uint256 dFourth = v.mulWadDown(rY).mulWadDown(ONE - gamma); - denominator = dFirst.mulWadDown(dSecond).mulWadDown(dThird + dFourth); - } +struct DiffRaiseStruct { + uint256 wX; + uint256 v; + uint256 rY; + int256 lMinusVTimesXOverYPowWx; + int256 vPlusYPow; + uint256 L; + int256 xOverYPowWx; + int256 gamma; + uint256 S; + int256 vTimesXOverYPowWx; +} + +function getDiffRaiseStruct( + uint256 wX, + uint256 rX, + uint256 v, + uint256 rY, + uint256 L, + uint256 S, + uint256 swapFee +) view returns (DiffRaiseStruct memory) { + int256 vPlusYPow = (int256(v) + int256(rY)).powWad(-I_ONE + int256(wX)); + int256 xOverYPowWx = (int256(rX).wadDiv(int256(rY))).powWad(int256(wX)); + int256 vTimesXOverYPowWx = int256(v).wadMul(xOverYPowWx); + int256 gamma = I_ONE - int256(swapFee); + int256 lMinusVTimesXOverYPowWx = + int256(L) - vTimesXOverYPowWx.wadMul(-I_ONE + gamma); - int256 result = -int256(S) + int256(numerator.divWadDown(denominator)); - return result; + return DiffRaiseStruct({ + wX: wX, + v: v, + rY: rY, + lMinusVTimesXOverYPowWx: lMinusVTimesXOverYPowWx, + vPlusYPow: vPlusYPow, + L: L, + xOverYPowWx: xOverYPowWx, + gamma: gamma, + S: S, + vTimesXOverYPowWx: vTimesXOverYPowWx + }); } function diffRaise( @@ -227,41 +328,19 @@ function diffRaise( uint256 L, uint256 v, GeometricMeanParams memory params -) pure returns (int256) { - (int256 wx, int256 wy, int256 swapFee) = - (int256(params.wX), int256(params.wY), int256(params.swapFee)); - int256 I_ONE = int256(ONE); - int256 iS = int256(S); - int256 iX = int256(rX); - int256 iY = int256(rY); - int256 iL = int256(L); - int256 iV = int256(v); - int256 gamma = I_ONE - swapFee; - - int256 vPlusYPow = (iV + iY).powWad(-I_ONE + wx); - - int256 xOverYPowWx = (iX.wadDiv(iY)).powWad(wx); - int256 vTimesXOverYPowWx = iV.wadMul(xOverYPowWx); - int256 lMinusVTimesXOverYPowWx = - iL - vTimesXOverYPowWx.wadMul(-I_ONE + gamma); +) view returns (int256) { + DiffRaiseStruct memory diffRaiseParams = + getDiffRaiseStruct(params.wX, rX, v, rY, L, S, params.swapFee); - int256 numerator; - { - int256 first = wx.wadMul(iV + iY); - int256 second = lMinusVTimesXOverYPowWx; - int256 third = - (vPlusYPow.wadMul(lMinusVTimesXOverYPowWx)).powWad(I_ONE.wadDiv(wx)); - int256 fourth = iL.wadMul(-I_ONE + wx); - int256 fifth = - xOverYPowWx.wadMul(iV.wadMul(wx) + iY).wadMul(-I_ONE + gamma); - numerator = - first.wadMul(second) + iS.wadMul(third).wadMul(fourth - fifth); - } + int256 numerator = computeDiffRaiseNumerator(diffRaiseParams); int256 denominator; { - int256 first = wx.wadMul(iV + iY); - int256 second = -iL + vTimesXOverYPowWx.wadMul(-I_ONE + gamma); + int256 first = int256(params.wX).wadMul(int256(v) + int256(rY)); + int256 second = -int256(L) + + diffRaiseParams.vTimesXOverYPowWx.wadMul( + -I_ONE + diffRaiseParams.gamma + ); denominator = first.wadMul(second); } @@ -275,7 +354,7 @@ function computeOptimalLower( uint256 L, uint256 vUpper, GeometricMeanParams memory params -) pure returns (uint256 v) { +) view returns (uint256 v) { uint256 upper = vUpper; uint256 lower = 1000; int256 lowerBoundOutput = diffLower(S, rX, rY, L, lower, params); @@ -299,7 +378,7 @@ function computeOptimalRaise( uint256 L, uint256 vUpper, GeometricMeanParams memory params -) pure returns (uint256 v) { +) view returns (uint256 v) { uint256 upper = vUpper; uint256 lower = 1000; int256 lowerBoundOutput = diffRaise(S, rX, rY, L, lower, params); @@ -322,7 +401,7 @@ function computeNextLiquidity( int256 invariant, uint256 approximatedL, GeometricMeanParams memory params -) pure returns (uint256 L) { +) view returns (uint256 L) { uint256 upper = approximatedL; uint256 lower = approximatedL; int256 computedInvariant = invariant; diff --git a/src/GeometricMean/GeometricMean.sol b/src/GeometricMean/GeometricMean.sol index 38e2423b..3414f85e 100644 --- a/src/GeometricMean/GeometricMean.sol +++ b/src/GeometricMean/GeometricMean.sol @@ -178,6 +178,7 @@ contract GeometricMean is IStrategy { revert("invalid swap: inputs x and y have the same sign!"); } + /* liquidityDelta = int256(nextL) - int256( GeometricMeanLib.computeNextLiquidity( @@ -186,6 +187,7 @@ contract GeometricMean is IStrategy { abi.decode(getPoolParams(poolId), (GeometricMeanParams)) ) ); + */ invariant = GeometricMeanLib.tradingFunction(nextRx, nextRy, nextL, params); diff --git a/src/GeometricMean/GeometricMeanSolver.sol b/src/GeometricMean/GeometricMeanSolver.sol index fe2a9c9f..988a3549 100644 --- a/src/GeometricMean/GeometricMeanSolver.sol +++ b/src/GeometricMean/GeometricMeanSolver.sol @@ -69,7 +69,7 @@ contract GeometricMeanSolver { uint256 rx, uint256 S, GeometricMeanParams memory params - ) public pure returns (bytes memory) { + ) public view returns (bytes memory) { return computeInitialPoolData(rx, S, params); } diff --git a/src/LogNormal/LogNormal.sol b/src/LogNormal/LogNormal.sol index 836a977d..d4783c62 100644 --- a/src/LogNormal/LogNormal.sol +++ b/src/LogNormal/LogNormal.sol @@ -6,6 +6,7 @@ import "src/interfaces/IDFMM.sol"; import "src/interfaces/IStrategy.sol"; import "src/lib/DynamicParamLib.sol"; import "src/lib/StrategyLib.sol"; +import "forge-std/console2.sol"; /// @notice Log Normal has three variable parameters: /// K - strike price @@ -172,19 +173,21 @@ contract LogNormal is IStrategy { if (nextRx > startRx) { amountIn = nextRx - startRx; fees = amountIn.mulWadUp(params.swapFee); - minLiquidityDelta += fees.mulWadUp(startL).divWadUp(startRx); + minLiquidityDelta += fees.mulWadUp(startL).divWadUp(startRx).mulWadUp(0.5 ether); } else if (nextRy > startRy) { // δl = δx * L / X, where δx = delta x * fee amountIn = nextRy - startRy; fees = amountIn.mulWadUp(params.swapFee); - minLiquidityDelta += fees.mulWadUp(startL).divWadUp(startRy); + minLiquidityDelta += fees.mulWadUp(startL).divWadUp(startRy).mulWadUp(0.5 ether); } else { // should never get here! revert("invalid swap: inputs x and y have the same sign!"); } liquidityDelta = int256(nextL) - int256(startL); - + console2.log("nextRx", nextRx); + console2.log("nextL", nextL); + console2.log("x gt L", nextRx > nextL); invariant = LogNormalLib.tradingFunction(nextRx, nextRy, nextL, params); valid = -(EPSILON) < invariant && invariant < EPSILON; } diff --git a/src/LogNormal/LogNormalExtendedLib.sol b/src/LogNormal/LogNormalExtendedLib.sol index 883bdba3..278355a4 100644 --- a/src/LogNormal/LogNormalExtendedLib.sol +++ b/src/LogNormal/LogNormalExtendedLib.sol @@ -8,6 +8,7 @@ import "./LogNormalLib.sol"; import "./LogNormal.sol"; import "solmate/tokens/ERC20.sol"; import "solstat/Gaussian.sol"; +import "forge-std/console2.sol"; using FixedPointMathLib for uint256; using FixedPointMathLib for int256; @@ -24,7 +25,7 @@ function computeLGivenX( uint256 rx, uint256 S, LogNormal.LogNormalParams memory params -) pure returns (uint256 L) { +) view returns (uint256 L) { int256 d1 = computeD1({ S: S, params: params }); int256 cdf = Gaussian.cdf(d1); uint256 unsignedCdf = LogNormalLib.toUint(cdf); @@ -38,7 +39,7 @@ function computeYGivenL( uint256 L, uint256 S, LogNormal.LogNormalParams memory params -) pure returns (uint256 ry) { +) view returns (uint256 ry) { int256 d2 = computeD2(S, params); int256 cdf = Gaussian.cdf(d2); uint256 unsignedCdf = LogNormalLib.toUint(cdf); @@ -52,7 +53,7 @@ function computeXGivenL( uint256 L, uint256 S, LogNormal.LogNormalParams memory params -) pure returns (uint256 rx) { +) view returns (uint256 rx) { int256 d1 = computeD1(S, params); int256 cdf = Gaussian.cdf(d1); uint256 unsignedCdf = LogNormalLib.toUint(cdf); @@ -66,7 +67,7 @@ function computeXGivenL( function computeD1( uint256 S, LogNormal.LogNormalParams memory params -) pure returns (int256 d1) { +) view returns (int256 d1) { (uint256 K, uint256 sigma, uint256 tau) = (params.strike, params.sigma, params.tau); uint256 sigmaSqrtTau = computeSigmaSqrtTau(sigma, tau); @@ -83,7 +84,7 @@ function computeD1( function computeD2( uint256 S, LogNormal.LogNormalParams memory params -) pure returns (int256 d2) { +) view returns (int256 d2) { (uint256 K, uint256 sigma, uint256 tau) = (params.strike, params.sigma, params.tau); uint256 sigmaSqrtTau = computeSigmaSqrtTau(sigma, tau); @@ -93,33 +94,33 @@ function computeD2( d2 = (lnSDivK - int256(halfSigmaPowTwoTau)) * 1e18 / int256(sigmaSqrtTau); } -/// @dev This is a pure anonymous function defined at the file level, which allows +/// @dev This is a view anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this /// function as an argument to find the root of the trading function given the reserveYWad. -function findRootY(bytes memory data, uint256 ry) pure returns (int256) { +function findRootY(bytes memory data, uint256 ry) view returns (int256) { (uint256 rx, uint256 L,, LogNormal.LogNormalParams memory params) = abi.decode(data, (uint256, uint256, int256, LogNormal.LogNormalParams)); return LogNormalLib.tradingFunction({ rx: rx, ry: ry, L: L, params: params }); } -/// @dev This is a pure anonymous function defined at the file level, which allows +/// @dev This is a view anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this /// function as an argument to find the root of the trading function given the reserveXWad. -function findRootX(bytes memory data, uint256 rx) pure returns (int256) { +function findRootX(bytes memory data, uint256 rx) view returns (int256) { (uint256 ry, uint256 L,, LogNormal.LogNormalParams memory params) = abi.decode(data, (uint256, uint256, int256, LogNormal.LogNormalParams)); return LogNormalLib.tradingFunction({ rx: rx, ry: ry, L: L, params: params }); } -/// @dev This is a pure anonymous function defined at the file level, which allows +/// @dev This is a view anonymous function defined at the file level, which allows /// it to be passed as an argument to another function. BisectionLib.sol takes this /// function as an argument to find the root of the trading function given the liquidity. function findRootLiquidity( bytes memory data, uint256 L -) pure returns (int256) { +) view returns (int256) { (uint256 rx, uint256 ry,, LogNormal.LogNormalParams memory params) = abi.decode(data, (uint256, uint256, int256, LogNormal.LogNormalParams)); return @@ -131,7 +132,7 @@ function computeInitialPoolData( uint256 amountX, uint256 initialPrice, LogNormal.LogNormalParams memory params -) pure returns (bytes memory) { +) view returns (bytes memory) { uint256 L = computeLGivenX(amountX, initialPrice, params); uint256 ry = computeYGivenL(L, initialPrice, params); int256 invariant = LogNormalLib.tradingFunction({ @@ -150,13 +151,15 @@ function computeNextLiquidity( int256 invariant, uint256 approximatedL, LogNormal.LogNormalParams memory params -) pure returns (uint256 L) { +) view returns (uint256 L) { uint256 upper = approximatedL; uint256 lower = approximatedL; int256 computedInvariant = invariant; if (computedInvariant < 0) { while (computedInvariant < 0) { lower = lower.mulDivDown(999, 1000); + uint256 min = rx > ry.divWadDown(params.strike) ? rx + 1000 : ry.divWadDown(params.strike) + 1000; + lower = lower < rx ? min : lower; computedInvariant = LogNormalLib.tradingFunction({ rx: rx, ry: ry, @@ -191,13 +194,17 @@ function computeNextRx( int256 invariant, uint256 approximatedRx, LogNormal.LogNormalParams memory params -) pure returns (uint256 rx) { +) view returns (uint256 rx) { uint256 upper = approximatedRx; uint256 lower = approximatedRx; + console2.log("approximatedRx", approximatedRx); int256 computedInvariant = invariant; + console2.log("computedInvariant", computedInvariant); if (computedInvariant < 0) { while (computedInvariant < 0) { upper = upper.mulDivUp(1001, 1000); + console2.log("rx upper", upper); + upper = upper > L ? L : upper; computedInvariant = LogNormalLib.tradingFunction({ rx: upper, ry: ry, @@ -208,6 +215,8 @@ function computeNextRx( } else { while (computedInvariant > 0) { lower = lower.mulDivDown(999, 1000); + console2.log("rx lower", lower); + lower = lower > L ? L : lower; computedInvariant = LogNormalLib.tradingFunction({ rx: lower, ry: ry, @@ -232,7 +241,7 @@ function computeNextRy( int256 invariant, uint256 approximatedRy, LogNormal.LogNormalParams memory params -) pure returns (uint256 ry) { +) view returns (uint256 ry) { uint256 upper = approximatedRy; uint256 lower = approximatedRy; int256 computedInvariant = invariant; @@ -267,152 +276,226 @@ function computeNextRy( ); } -function findRootLower(bytes memory data, uint256 v) pure returns (int256) { - ( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, - LogNormal.LogNormalParams memory params - ) = abi.decode( - data, (uint256, uint256, uint256, uint256, LogNormal.LogNormalParams) - ); - return diffLower({ S: S, rX: rX, rY: rY, L: L, v: v, params: params }); +function findRootLower(bytes memory data, uint256 v) view returns (int256) { + (uint256 S, uint256 rX, uint256 L, LogNormal.LogNormalParams memory params) + = abi.decode(data, (uint256, uint256, uint256, LogNormal.LogNormalParams)); + return diffLower({ + S: int256(S), + rX: int256(rX), + L: int256(L), + v: int256(v), + params: params + }); } -function findRootRaise(bytes memory data, uint256 v) pure returns (int256) { - ( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, - LogNormal.LogNormalParams memory params - ) = abi.decode( - data, (uint256, uint256, uint256, uint256, LogNormal.LogNormalParams) - ); - return diffRaise({ S: S, rX: rX, rY: rY, L: L, v: v, params: params }); +function findRootRaise(bytes memory data, uint256 v) view returns (int256) { + (uint256 S, uint256 rY, uint256 L, LogNormal.LogNormalParams memory params) + = abi.decode(data, (uint256, uint256, uint256, LogNormal.LogNormalParams)); + return diffRaise({ + S: int256(S), + rY: int256(rY), + L: int256(L), + v: int256(v), + params: params + }); } -function diffLower( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, - uint256 v, +struct DiffLowerStruct { + int256 ierfcResult; + int256 strike; + int256 sigma; + int256 tau; + int256 gamma; + int256 rX; + int256 L; + int256 v; + int256 S; + int256 sqrtTau; + int256 sqrtTwo; +} + +function createDiffLowerStruct( + int256 S, + int256 rx, + int256 L, + int256 gamma, + int256 v, LogNormal.LogNormalParams memory params -) pure returns (int256) { - (int256 strike, int256 sigma, int256 tau, int256 swapFee) = ( - int256(params.strike), - int256(params.sigma), - int256(params.tau), - int256(params.swapFee) - ); +) view returns (DiffLowerStruct memory) { + int256 a = I_TWO.wadMul(v + rx); + int256 b = L + v - v.wadMul(gamma); + int256 ierfcRes = Gaussian.ierfc(a.wadDiv(b)); + int256 sqrtTwo = int256(FixedPointMathLib.sqrt(TWO) * 1e9); int256 sqrtTau = int256(FixedPointMathLib.sqrt(params.tau) * 1e9); - int256 iS = int256(S); - int256 iX = int256(rX); - int256 iL = int256(L); - int256 iV = int256(v); - int256 gamma = I_ONE - swapFee; - - int256 ierfcNum = I_TWO.wadMul(iV + iX); - int256 ierfcDen = iL + iV - iV.wadMul(gamma); - int256 ierfcRes = Gaussian.ierfc(ierfcNum.wadDiv(ierfcDen)); - - int256 a; - { - int256 firstExp = -(sigma.wadMul(sigma).wadMul(tau).wadDiv(I_TWO)); - int256 secondExp = - sqrtTwo.wadMul(sigma).wadMul(sqrtTau).wadMul(ierfcRes); - - int256 first = FixedPointMathLib.expWad(firstExp + secondExp); - int256 second = strike.wadMul(iL + iX.wadMul(-I_ONE + gamma)); - - int256 firstNum = first.wadMul(second); - int256 firstDen = iL + iV - iV.wadMul(gamma); - a = firstNum.wadDiv(firstDen); - } - int256 b; - { - int256 first = I_HALF.wadMul(strike).wadMul(-I_ONE + gamma); - int256 erfcFirst = sigma.wadMul(sqrtTau).wadDiv(sqrtTwo); - int256 erfcSecond = ierfcRes; - b = first.wadMul(Gaussian.erfc(erfcFirst - erfcSecond)); - } + DiffLowerStruct memory ints = DiffLowerStruct({ + ierfcResult: ierfcRes, + strike: int256(params.strike), + sigma: int256(params.sigma), + tau: int256(params.tau), + gamma: gamma, + rX: rx, + L: L, + v: v, + S: S, + sqrtTwo: sqrtTwo, + sqrtTau: sqrtTau + }); - return -iS + a + b; + return ints; } -function diffRaise( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, - uint256 v, +function computeLowerA(DiffLowerStruct memory params) view returns (int256) { + int256 firstExp = + -(params.sigma.wadMul(params.sigma).wadMul(params.tau).wadDiv(I_TWO)); + int256 secondExp = params.sqrtTwo.wadMul(params.sigma).wadMul( + params.sqrtTau + ).wadMul(params.ierfcResult); + + int256 first = FixedPointMathLib.expWad(firstExp + secondExp); + int256 second = + params.strike.wadMul(params.L + params.rX.wadMul(-I_ONE + params.gamma)); + + int256 firstNum = first.wadMul(second); + int256 firstDen = params.L + params.v - params.v.wadMul(params.gamma); + return firstNum.wadDiv(firstDen); +} + +function computeLowerB(DiffLowerStruct memory params) view returns (int256) { + int256 a = I_HALF.wadMul(params.strike).wadMul(-I_ONE + params.gamma); + int256 b = params.sigma.wadMul(params.sqrtTau).wadDiv(params.sqrtTwo); + return a.wadMul(Gaussian.erfc(b - params.ierfcResult)); +} + +function diffLower( + int256 S, + int256 rX, + int256 L, + int256 v, LogNormal.LogNormalParams memory params -) pure returns (int256) { - (int256 strike, int256 sigma, int256 tau, int256 swapFee) = ( - int256(params.strike), - int256(params.sigma), - int256(params.tau), - int256(params.swapFee) - ); +) view returns (int256) { + int256 gamma = I_ONE - int256(params.swapFee); + DiffLowerStruct memory ints = + createDiffLowerStruct(S, rX, L, gamma, v, params); + int256 a = computeLowerA(ints); + int256 b = computeLowerB(ints); + + return -ints.S + a + b; +} + +struct DiffRaiseStruct { + int256 ierfcResult; + int256 strike; + int256 sigma; + int256 tau; + int256 gamma; + int256 rY; + int256 L; + int256 v; + int256 S; + int256 sqrtTwo; + int256 sqrtTau; +} + +function createDiffRaiseStruct( + int256 S, + int256 ry, + int256 L, + int256 gamma, + int256 v, + LogNormal.LogNormalParams memory params +) view returns (DiffRaiseStruct memory) { + int256 a = I_TWO.wadMul(v + ry); + int256 b = int256(params.strike).wadMul(L) + v - v.wadMul(gamma); + int256 intermediate = a.wadDiv(b); + console2.log("intermediate", intermediate); + int256 ierfcRes = Gaussian.ierfc(a.wadDiv(b)); + console2.log("ierfc res", ierfcRes); + int256 sqrtTwo = int256(FixedPointMathLib.sqrt(TWO) * 1e9); int256 sqrtTau = int256(FixedPointMathLib.sqrt(params.tau) * 1e9); - int256 iS = int256(S); - int256 iY = int256(rY); - int256 iL = int256(L); - int256 iV = int256(v); - int256 gamma = I_ONE - swapFee; - - int256 ierfcNum = I_TWO.wadMul(iV + iY); - int256 ierfcDen = strike.wadMul(iL) + iV - iV.wadMul(gamma); - int256 ierfcRes = Gaussian.ierfc(ierfcNum.wadDiv(ierfcDen)); - - int256 a; - { - int256 firstExp = -(sigma.wadMul(sigma).wadMul(tau).wadDiv(I_TWO)); - int256 secondExp = - sqrtTwo.wadMul(sigma).wadMul(sqrtTau).wadMul(ierfcRes); - int256 first = FixedPointMathLib.expWad(firstExp + secondExp); - int256 second = iS.wadMul(strike.wadMul(iL) + iY.wadMul(-I_ONE + gamma)); - - int256 num = first.wadMul(second); - int256 den = strike.wadMul(strike.wadMul(iL) + iV - iV.wadMul(gamma)); - a = num.wadDiv(den); - } - int256 b; - { - int256 first = iS.wadMul(-I_ONE + gamma); - int256 erfcFirst = sigma.wadMul(sqrtTau).wadDiv(sqrtTwo); - int256 erfcSecond = ierfcRes; - int256 num = first.wadMul(Gaussian.erfc(erfcFirst - erfcSecond)); - int256 den = I_TWO.wadMul(strike); + DiffRaiseStruct memory ints = DiffRaiseStruct({ + ierfcResult: ierfcRes, + strike: int256(params.strike), + sigma: int256(params.sigma), + tau: int256(params.tau), + gamma: gamma, + rY: ry, + L: L, + S: S, + v: v, + sqrtTwo: sqrtTwo, + sqrtTau: sqrtTau + }); + + return ints; +} - b = num.wadDiv(den); - } +function testIerfc(int256 x) view returns (int256) { + return Gaussian.ierfc(x); +} + +function computeRaiseA(DiffRaiseStruct memory params) view returns (int256) { + int256 firstExp = + -(params.sigma.wadMul(params.sigma).wadMul(params.tau).wadDiv(I_TWO)); + int256 secondExp = params.sqrtTwo.wadMul(params.sigma).wadMul( + params.sqrtTau + ).wadMul(params.ierfcResult); + int256 first = FixedPointMathLib.expWad(firstExp + secondExp); + int256 second = params.S.wadMul( + params.strike.wadMul(params.L) + params.rY.wadMul(-I_ONE + params.gamma) + ); + + int256 num = first.wadMul(second); + int256 den = params.strike.wadMul( + params.strike.wadMul(params.L) + params.v + - params.v.wadMul(params.gamma) + ); + return num.wadDiv(den); +} + +function computeRaiseB(DiffRaiseStruct memory params) view returns (int256) { + int256 first = params.S.wadMul(-I_ONE + params.gamma); + int256 erfcFirst = + params.sigma.wadMul(params.sqrtTau).wadDiv(params.sqrtTwo); + int256 num = first.wadMul(Gaussian.erfc(erfcFirst - params.ierfcResult)); + int256 den = I_TWO.wadMul(params.strike); + return num.wadDiv(den); +} + +function diffRaise( + int256 S, + int256 rY, + int256 L, + int256 v, + LogNormal.LogNormalParams memory params +) view returns (int256) { + int256 gamma = I_ONE - int256(params.swapFee); + DiffRaiseStruct memory ints = + createDiffRaiseStruct(S, rY, L, gamma, v, params); + int256 a = computeRaiseA(ints); + int256 b = computeRaiseB(ints); return -I_ONE + a + b; } function computeOptimalLower( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, + int256 S, + int256 rX, + int256 L, uint256 vUpper, LogNormal.LogNormalParams memory params -) pure returns (uint256 v) { +) view returns (uint256 v) { uint256 upper = vUpper; - uint256 lower = 1000; - int256 lowerBoundOutput = diffLower(S, rX, rY, L, lower, params); + uint256 lower = 1; + int256 lowerBoundOutput = diffLower(S, rX, L, int256(lower), params); if (lowerBoundOutput < 0) { return 0; } v = bisection( - abi.encode(S, rX, rY, L, params), + abi.encode(S, rX, L, params), lower, upper, uint256(1), @@ -422,21 +505,20 @@ function computeOptimalLower( } function computeOptimalRaise( - uint256 S, - uint256 rX, - uint256 rY, - uint256 L, + int256 S, + int256 rY, + int256 L, uint256 vUpper, LogNormal.LogNormalParams memory params -) pure returns (uint256 v) { +) view returns (uint256 v) { uint256 upper = vUpper; - uint256 lower = 1000; - int256 lowerBoundOutput = diffRaise(S, rX, rY, L, lower, params); + uint256 lower = 1; + int256 lowerBoundOutput = diffRaise(S, rY, L, int256(lower), params); if (lowerBoundOutput < 0) { return 0; } v = bisection( - abi.encode(S, rX, rY, L, params), + abi.encode(S, rY, L, params), lower, upper, uint256(1), @@ -444,3 +526,35 @@ function computeOptimalRaise( findRootRaise ); } + +function computeDy(int256 S, int256 rY, int256 L, LogNormal.LogNormalParams memory params) view returns (int256 dy) { + int256 gamma = I_ONE - int256(params.swapFee); + int256 strike = int256(params.strike); + int256 sigma = int256(params.sigma); + + int256 lnSDivK = computeLnSDivK(uint256(S), params.strike); + int256 a = lnSDivK.wadDiv(sigma) - sigma.wadDiv(I_TWO); + int256 cdfA = Gaussian.cdf(a); + + int256 delta = L.wadMul(strike).wadMul(cdfA); + dy = delta - rY; + + //dy = (delta - rY).wadDiv((gamma - I_ONE).wadMul(cdfA).wadDiv(rY.wadDiv(strike.wadMul(L))) + I_ONE); +} + +function computeDx(int256 S, int256 rX, int256 L, LogNormal.LogNormalParams memory params) view returns (int256 dx) { + int256 gamma = I_ONE - int256(params.swapFee); + int256 sigma = int256(params.sigma); + // lnSDivK + int256 lnSDivK = computeLnSDivK(uint256(S), params.strike); + // cdf((lnSDivK / sigma) + (sigma / 2)) + int256 a = Gaussian.cdf(lnSDivK.wadDiv(sigma) + sigma.wadDiv(I_TWO)); + // L * (1 - cdf) + int256 delta = L.wadMul(I_ONE - a); + dx = delta - rX; + + //dx = (delta - rX).wadDiv((gamma - I_ONE).wadMul(I_ONE - a).wadDiv(rX.wadDiv(L)) + I_ONE); +} + + + diff --git a/src/LogNormal/LogNormalSolver.sol b/src/LogNormal/LogNormalSolver.sol index 7997ddac..75138ed1 100644 --- a/src/LogNormal/LogNormalSolver.sol +++ b/src/LogNormal/LogNormalSolver.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-3.0-or-later +// SPDX-License-Identifier: GPL-3.0-or-latersolver pragma solidity ^0.8.13; import "./LogNormalExtendedLib.sol"; @@ -17,6 +17,7 @@ contract LogNormalSolver { uint256 rx; uint256 ry; uint256 L; + uint256 approxPrice; } uint256 public constant BISECTION_EPSILON = 0; @@ -88,7 +89,7 @@ contract LogNormalSolver { uint256 rx, uint256 S, LogNormal.LogNormalParams memory params - ) public pure returns (bytes memory) { + ) public view returns (bytes memory) { return computeInitialPoolData(rx, S, params); } @@ -151,7 +152,11 @@ contract LogNormalSolver { uint256 L ) public view returns (uint256) { bytes memory data = abi.encode(rx, ry, L); + console2.log("rx in nextL", rx); + console2.log("L in nextL", L); + int256 invariant = IStrategy(strategy).computeSwapConstant(poolId, data); + console2.log("invariant", invariant); return computeNextLiquidity(rx, ry, invariant, L, fetchPoolParams(poolId)); } @@ -163,6 +168,7 @@ contract LogNormalSolver { uint256 S ) public view returns (uint256) { uint256 approximatedRx = computeXGivenL(L, S, fetchPoolParams(poolId)); + console2.log("approximated rx given price", approximatedRx); bytes memory data = abi.encode(approximatedRx, ry, L); int256 invariant = IStrategy(strategy).computeSwapConstant(poolId, data); return computeNextRx( @@ -184,6 +190,42 @@ contract LogNormalSolver { ); } + function computeDeltaL( + uint256 amountIn, + uint256 swapFee, + uint256 L, + uint256 reserve + ) public view returns (uint256 deltaL) { + deltaL = amountIn.mulWadUp(swapFee).mulWadUp(L).divWadUp(reserve).mulWadUp(HALF); + } + + function computeDeltaLXIn( + uint256 poolId, + uint256 amountIn, + uint256 rx, + uint256 ry, + uint256 L, + LogNormal.LogNormalParams memory params + ) public view returns (uint256 deltaL) { + uint256 fees = params.swapFee.mulWadUp(amountIn); + uint256 px = getPriceGivenXL(poolId, rx, L); + deltaL = fees.divWadDown(px.mulWadDown(rx) + ry); + } + + function computeDeltaLYIn( + uint256 poolId, + uint256 amountIn, + uint256 rx, + uint256 ry, + uint256 L, + LogNormal.LogNormalParams memory params + ) public view returns (uint256 deltaL) { + uint256 fees = params.swapFee.mulWadUp(amountIn); + uint256 py = getPriceGivenYL(poolId, ry, L); + deltaL = fees.divWadDown(py.mulWadDown(rx) + ry); + } + + /// @dev Estimates a swap's reserves and adjustments and returns its validity. function simulateSwap( uint256 poolId, @@ -198,22 +240,26 @@ contract LogNormalSolver { uint256 amountOut; { + console2.log("start rx", startReserves.rx); + console2.log("start ry", startReserves.ry); + console2.log("start L", startReserves.L); + console2.log("computing next L"); uint256 startComputedL = getNextLiquidity( poolId, startReserves.rx, startReserves.ry, startReserves.L ); + console2.log("computed next L"); if (swapXIn) { - uint256 deltaL = amountIn.mulWadUp(poolParams.swapFee); - endReserves.rx = startReserves.rx + amountIn; - endReserves.L = startComputedL + deltaL; - uint256 approxPrice = + endReserves.L = startComputedL + computeDeltaLXIn(poolId, amountIn, startReserves.rx, startReserves.ry, startReserves.L, poolParams); + endReserves.approxPrice = getPriceGivenXL(poolId, endReserves.rx, endReserves.L); + console2.log("computing next Y"); endReserves.ry = getNextReserveY( - poolId, endReserves.rx, endReserves.L, approxPrice + poolId, endReserves.rx, endReserves.L, endReserves.approxPrice ); - endReserves.ry += 1; + console2.log("computed next Y"); require( endReserves.ry < startReserves.ry, @@ -221,19 +267,20 @@ contract LogNormalSolver { ); amountOut = startReserves.ry - endReserves.ry; } else { - uint256 deltaL = amountIn.mulWadUp(poolParams.swapFee).divWadUp( - poolParams.strike - ); - endReserves.ry = startReserves.ry + amountIn; - endReserves.L = startComputedL + deltaL; - uint256 approxPrice = + endReserves.L = startComputedL + computeDeltaLYIn(poolId, amountIn, startReserves.rx, startReserves.ry, startReserves.L, poolParams); + console2.log("end ry", endReserves.ry); + console2.log("end L", endReserves.L); + endReserves.approxPrice = getPriceGivenYL(poolId, endReserves.ry, endReserves.L); + console2.log("computing next X"); endReserves.rx = getNextReserveX( - poolId, endReserves.ry, endReserves.L, approxPrice + poolId, endReserves.ry, endReserves.L, endReserves.approxPrice ); - endReserves.rx += 1; + console2.log("computed next X"); + console2.log("next x:", endReserves.rx); + console2.log("start x:", startReserves.rx); require( endReserves.rx < startReserves.rx, @@ -291,8 +338,8 @@ contract LogNormalSolver { uint256 v ) public view returns (int256) { LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); - (uint256 rx, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); - return diffLower(S, rx, ry, L, v, params); + (uint256 rx,, uint256 L) = getReservesAndLiquidity(poolId); + return diffLower(int256(S), int256(rx), int256(L), int256(v), params); } function calculateDiffRaise( @@ -301,8 +348,8 @@ contract LogNormalSolver { uint256 v ) public view returns (int256) { LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); - (uint256 rx, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); - return diffRaise(S, rx, ry, L, v, params); + (, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); + return diffRaise(int256(S), int256(ry), int256(L), int256(v), params); } function computeOptimalArbLowerPrice( @@ -311,8 +358,8 @@ contract LogNormalSolver { uint256 vUpper ) public view returns (uint256) { LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); - (uint256 rx, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); - return computeOptimalLower(S, rx, ry, L, vUpper, params); + (uint256 rx,, uint256 L) = getReservesAndLiquidity(poolId); + return computeOptimalLower(int256(S), int256(rx), int256(L), vUpper, params); } function computeOptimalArbRaisePrice( @@ -321,7 +368,75 @@ contract LogNormalSolver { uint256 vUpper ) public view returns (uint256) { LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); - (uint256 rx, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); - return computeOptimalRaise(S, rx, ry, L, vUpper, params); + (, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); + return computeOptimalRaise(int256(S), int256(ry), int256(L), vUpper, params); + } + + function getDyGivenS( + uint256 poolId, + uint256 S + ) public view returns (int256) { + LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); + (, uint256 ry, uint256 L) = getReservesAndLiquidity(poolId); + int256 dy = computeDy(int256(S), int256(ry), int256(L), params); + console2.log("====computed dy=====", dy); + return dy; + } + + function getDxGivenS( + uint256 poolId, + uint256 S + ) public view returns (int256) { + LogNormal.LogNormalParams memory params = fetchPoolParams(poolId); + (uint256 rx,, uint256 L) = getReservesAndLiquidity(poolId); + return computeDx(int256(S), int256(rx), int256(L), params); + } + + function callIerfc(int256 x) public view returns (int256) { + return testIerfc(x); + } + + function solverTradingFunction( + uint256 rx, + uint256 ry, + uint256 L, + LogNormal.LogNormalParams memory params + ) public view returns (int256) { + require(rx < L, "tradingFunction: invalid x"); + + int256 AAAAA; + int256 BBBBB; + if (FixedPointMathLib.divWadDown(rx, L) >= ONE) { + AAAAA = int256(2 ** 255 - 1); + } else { + AAAAA = Gaussian.ppf(int256(FixedPointMathLib.divWadDown(rx, L))); + console2.log("a", AAAAA); + } + if ( + FixedPointMathLib.divWadDown( + ry, FixedPointMathLib.mulWadDown(params.strike, L) + ) >= ONE + ) { + console2.log("in max int branch"); + BBBBB = int256(2 ** 255 - 1); + console2.log("b", BBBBB); + } else { + BBBBB = Gaussian.ppf( + int256( + FixedPointMathLib.divWadDown( + ry, FixedPointMathLib.mulWadDown(params.strike, L) + ) + ) + ); + } + + int256 CCCCC = int256(computeSigmaSqrtTau(params.sigma, params.tau)); + console2.log("c", CCCCC); + + console2.log("a + c", AAAAA + CCCCC); + console2.log("a + b + c", AAAAA + BBBBB + CCCCC); + + return AAAAA + BBBBB + CCCCC; } + } diff --git a/src/lib/BisectionLib.sol b/src/lib/BisectionLib.sol index fec3c7fc..8289aa8f 100644 --- a/src/lib/BisectionLib.sol +++ b/src/lib/BisectionLib.sol @@ -28,8 +28,8 @@ function bisection( uint256 upper, uint256 epsilon, uint256 maxIterations, - function (bytes memory,uint256) pure returns (int256) fx -) pure returns (uint256 root) { + function (bytes memory,uint256) view returns (int256) fx +) view returns (uint256 root) { if (lower > upper) revert BisectionLib_InvalidBounds(lower, upper); // Passes the lower and upper bounds to the optimized function. // Reverts if the optimized function `fx` returns both negative or both positive values. diff --git a/src/test/LogNormal/LogNormalTest.t.sol b/src/test/LogNormal/LogNormalTest.t.sol index 16bc02e5..28717de9 100644 --- a/src/test/LogNormal/LogNormalTest.t.sol +++ b/src/test/LogNormal/LogNormalTest.t.sol @@ -6,10 +6,12 @@ import "solmate/test/utils/mocks/MockERC20.sol"; import "src/DFMM.sol"; import "src/LogNormal/LogNormal.sol"; import "src/LogNormal/LogNormalSolver.sol"; +import "../helpers/AtomicV2.sol"; import "../helpers/Lex.sol"; contract LogNormalTest is Test { using stdStorage for StdStorage; + using FixedPointMathLib for uint256; DFMM dfmm; LogNormal logNormal; @@ -17,21 +19,25 @@ contract LogNormalTest is Test { address tokenX; address tokenY; Lex lex; + AtomicV2 atomic; uint256 public constant TEST_SWAP_FEE = 0.003 ether; function setUp() public { tokenX = address(new MockERC20("tokenX", "X", 18)); tokenY = address(new MockERC20("tokenY", "Y", 18)); - MockERC20(tokenX).mint(address(this), 100_000_000 ether); - MockERC20(tokenY).mint(address(this), 100_000_000 ether); + MockERC20(tokenX).mint(address(this), 100_000_000_000 ether); + MockERC20(tokenY).mint(address(this), 100_000_000_000 ether); lex = new Lex(tokenX, tokenY, ONE); dfmm = new DFMM(address(0)); logNormal = new LogNormal(address(dfmm)); solver = new LogNormalSolver(address(logNormal)); + atomic = new AtomicV2(address(solver), address(dfmm), address(lex), tokenX, tokenY); MockERC20(tokenX).approve(address(dfmm), type(uint256).max); MockERC20(tokenY).approve(address(dfmm), type(uint256).max); + MockERC20(tokenX).approve(address(atomic), type(uint256).max); + MockERC20(tokenY).approve(address(atomic), type(uint256).max); } modifier realisticEth() { @@ -89,18 +95,101 @@ contract LogNormalTest is Test { _; } - modifier revert_scenario() { + modifier next_L() { vm.warp(0); LogNormal.LogNormalParams memory params = LogNormal.LogNormalParams({ - strike: 0.67323818941934077 ether, - sigma: ONE, + strike: ONE, + sigma: 0.0001 ether, + tau: ONE, + swapFee: 0.0001 ether, + controller: address(0) + }); + uint256 init_p = ONE; + uint256 init_x = ONE; + bytes memory initData = + solver.getInitialPoolData(init_x, init_p, params); + + IDFMM.InitParams memory initParams = IDFMM.InitParams({ + strategy: address(logNormal), + tokenX: tokenX, + tokenY: tokenY, + data: initData + }); + + dfmm.init(initParams); + + _; + } + + + modifier deep_liq() { + vm.warp(0); + + LogNormal.LogNormalParams memory params = LogNormal.LogNormalParams({ + strike: ONE, + sigma: 0.1 ether, tau: ONE, swapFee: TEST_SWAP_FEE, controller: address(0) }); - uint256 init_p = 1_329_956_352_651_532_999; - uint256 init_x = 70.658087306013359413 ether; + uint256 init_p = 1.1 ether; + uint256 init_x = ONE * 100_000_000; + bytes memory initData = + solver.getInitialPoolData(init_x, init_p, params); + + IDFMM.InitParams memory initParams = IDFMM.InitParams({ + strategy: address(logNormal), + tokenX: tokenX, + tokenY: tokenY, + data: initData + }); + + dfmm.init(initParams); + + _; + } + + modifier infinity_case() { + vm.warp(0); + + LogNormal.LogNormalParams memory params = LogNormal.LogNormalParams({ + strike: ONE, + sigma: 100000000000000, + tau: ONE, + swapFee: 100000000000000, + controller: address(0) + }); + uint256 init_p = ONE; + uint256 init_x = ONE * 100; + bytes memory initData = + solver.getInitialPoolData(init_x, init_p, params); + + IDFMM.InitParams memory initParams = IDFMM.InitParams({ + strategy: address(logNormal), + tokenX: tokenX, + tokenY: tokenY, + data: initData + }); + + dfmm.init(initParams); + + _; + } + + + modifier oob_scenario() { + vm.warp(0); + + LogNormal.LogNormalParams memory params = LogNormal.LogNormalParams({ + strike: ONE, + sigma: 100_000_000_000_000, + tau: ONE, + swapFee: 10000000000000, + controller: address(0) + }); + uint256 init_p = 1 ether; + uint256 init_x = 100 ether; bytes memory initData = solver.getInitialPoolData(init_x, init_p, params); @@ -144,6 +233,30 @@ contract LogNormalTest is Test { assertApproxEqAbs(priceGivenY, priceGivenX, 100); } + function test_oob_diff_raise() public oob_scenario { + uint256 poolId = dfmm.nonce() - 1; + int256 diffRaised = solver.calculateDiffRaise( + poolId, 868046956976567012, 868046956976577012 + ); + + console2.log(diffRaised); + } + + function test_bisect_oob_error() public oob_scenario { + bool xIn = true; + uint256 poolId = dfmm.nonce() - 1; + uint256 amountIn = 97506664135616951612; + (,,, bytes memory swapData) = solver.simulateSwap(poolId, xIn, amountIn); + + dfmm.swap(poolId, swapData); + + (uint256 rx, uint256 ry, uint256 L) = solver.getReservesAndLiquidity(poolId); + console2.log(rx); + console2.log(ry); + uint256 internalPrice = solver.internalPrice(poolId); + console2.log(internalPrice); + } + function test_ln_diff_lower() public basic { uint256 poolId = dfmm.nonce() - 1; int256 diffLowered = solver.calculateDiffLower( @@ -180,83 +293,104 @@ contract LogNormalTest is Test { console2.log(optimalRaise); } - // function test_internal_price() public basic { - // uint256 internalPrice = solver.internalPrice(); + function test_atomic_events() public basic { + atomic.logData(dfmm.nonce() - 1); + } + + function test_complete_arb_flow_dy() public basic { + uint256 poolId = dfmm.nonce() - 1; + uint256 S = 2.1 ether; + int256 dy = solver.getDyGivenS(poolId, S); + + uint256 optimalRaise = solver.computeOptimalArbRaisePrice(poolId, S, uint256(dy)); + + console2.log(optimalRaise); + } + + function test_complete_arb_flow_dx() public basic { + uint256 poolId = dfmm.nonce() - 1; + uint256 S = 1.9 ether; + int256 dx = solver.getDxGivenS(poolId, S); + + uint256 optimalLower = solver.computeOptimalArbLowerPrice(poolId, S, uint256(dx)); + + console2.log(optimalLower); + } + - // console2.log(internalPrice); - // } + function test_compute_dy() public basic { + uint256 poolId = dfmm.nonce() - 1; + int256 dy = solver.getDyGivenS(poolId, 2.1 ether); - // function test_internal_price_post_y_in() public basic { - // uint256 internalPrice = solver.internalPrice(); - // uint256 amountIn = 0.1 ether; - // bool swapXIn = false; + console2.log(dy); + } - // // Try doing simulate swap to see if we get a similar result. - // (bool valid,,, bytes memory payload) = - // solver.simulateSwap(swapXIn, amountIn); + function test_compute_dx() public basic { + uint256 poolId = dfmm.nonce() - 1; + int256 dx = solver.getDxGivenS(poolId, 1.9 ether); - // assertEq(valid, true); + console2.log(dx); + } - // dfmm.swap(payload); + function test_deep_liq_x_in() public deep_liq { + bool xIn = true; + uint256 amountIn = 1 ether; + uint256 poolId = dfmm.nonce() - 1; + (,,, bytes memory swapData) = solver.simulateSwap(poolId, xIn, amountIn); - // uint256 postSwapInternalPrice = solver.internalPrice(); + (uint256 input, uint256 output) = dfmm.swap(poolId, swapData); - // assertGt(postSwapInternalPrice, internalPrice); - // } + console2.log(input); + console2.log(output); + } - // function test_internal_price_post_x_in() public basic { - // uint256 internalPrice = solver.internalPrice(); - // uint256 amountIn = 0.1 ether; - // bool swapXIn = true; + function test_deep_liq_y_in() public deep_liq { + bool xIn = false; + uint256 amountIn = 1 ether; + uint256 poolId = dfmm.nonce() - 1; + (,,, bytes memory swapData) = solver.simulateSwap(poolId, xIn, amountIn); - // // Try doing simulate swap to see if we get a similar result. - // (bool valid,,, bytes memory payload) = - // solver.simulateSwap(swapXIn, amountIn); + (uint256 input, uint256 output) = dfmm.swap(poolId, swapData); - // assertEq(valid, true); + console2.log(input); + console2.log(output); + } - // dfmm.swap(payload); + function test_inf_scenario() public infinity_case { + uint256 poolId = dfmm.nonce() - 1; + bool xIn = true; + uint256 amountIn = 93184003566910952224; + (,,, bytes memory swapData) = solver.simulateSwap(poolId, xIn, amountIn); + dfmm.swap(poolId, swapData); - // uint256 postSwapInternalPrice = solver.internalPrice(); + (uint256 rx, uint256 ry, uint256 L) = solver.getReservesAndLiquidity(poolId); + console2.log(rx); + console2.log(ry); + console2.log(L); - // assertLt(postSwapInternalPrice, internalPrice); - // } + int256 raise = solver.calculateDiffRaise(poolId, 1000284890278334600, 193204839774776442304); - // function test_swap_eth_backtest() public realisticEth { - // uint256 amountIn = 0.1 ether; - // bool swapXIn = true; + console2.log(raise); + } - // // Try doing simulate swap to see if we get a similar result. - // (bool valid,,, bytes memory payload) = - // solver.simulateSwap(swapXIn, amountIn); + function test_ierfc_input() public basic { + int256 res = solver.callIerfc(1999999999999999999); + console2.log(res); + } - // assertEq(valid, true); + function test_solver_tf() public next_L { + uint256 rx = 58724093250934788978; + uint256 ry = 141290607496809311119; + //uint256 L = 200021587103761615868; - // dfmm.swap(payload); - // } + LogNormal.LogNormalParams memory params = solver.fetchPoolParams(0); - // function test_allocate_multiple_times() public basic { - // uint256 amountX = 0.1 ether; - // (uint256 rx, uint256 ry, uint256 L) = solver.allocateGivenX(amountX); + uint256 L = rx > ry.divWadDown(params.strike) ? rx + 1000 : ry.divWadDown(params.strike) + 1000; - // uint256 preBalance = dfmm.balanceOf(address(this)); - // uint256 deltaLiquidity = L - dfmm.totalLiquidity(); - // bytes memory data = abi.encode(rx, ry, L); - // dfmm.allocate(data); - // assertEq(preBalance + deltaLiquidity, dfmm.balanceOf(address(this))); + solver.solverTradingFunction(rx, ry, L, params); - // (rx, ry, L) = solver.allocateGivenX(amountX * 2); - // deltaLiquidity = L - dfmm.totalLiquidity(); - // data = abi.encode(rx, ry, L); - // MockERC20(tokenX).mint(address(0xbeef), rx); - // MockERC20(tokenY).mint(address(0xbeef), ry); + + } - // vm.startPrank(address(0xbeef)); - // MockERC20(tokenX).approve(address(dfmm), type(uint256).max); - // MockERC20(tokenY).approve(address(dfmm), type(uint256).max); - // dfmm.allocate(data); - // assertEq(deltaLiquidity, dfmm.balanceOf(address(0xbeef))); - // vm.stopPrank(); - // } } diff --git a/src/test/helpers/AtomicV2.sol b/src/test/helpers/AtomicV2.sol index 549463e9..4a820c10 100644 --- a/src/test/helpers/AtomicV2.sol +++ b/src/test/helpers/AtomicV2.sol @@ -6,13 +6,24 @@ import "solmate/utils/FixedPointMathLib.sol"; import "src/lib/BisectionLib.sol"; import "forge-std/console2.sol"; +struct LogNormalParams { + uint256 strike; + uint256 sigma; + uint256 tau; + uint256 swapFee; + address controller; +} + interface LiquidExchange { function swap(address token, uint256 amount) external; function price() external returns (uint256); } -interface StrategyLike { +interface DfmmLike { function swap(uint256 poolId, bytes calldata data) external; +} + +interface SolverLike { function simulateSwap( uint256 poolId, bool swapXIn, @@ -31,8 +42,18 @@ interface StrategyLike { external view returns (uint256, uint256, uint256); + function strategy() external view returns (address); +} + +interface LogNormalStrategyLike { + function fetchPoolParams(uint256 poolId) external view returns (LogNormalParams memory); +} + +interface StrategyLike { + function name() external view returns (string memory); } + interface TokenLike { function transferFrom(address, address, uint256) external; function transfer(address, uint256) external; @@ -69,6 +90,9 @@ contract AtomicV2 { address public solver; address public asset; address public quote; + address public strategy; + + string public strategyName; /// @dev Since token x is transferred inside the arbitrage loop, this stores that value in the last arb loop. uint256 public intermediateTokenXBalance; @@ -90,16 +114,39 @@ contract AtomicV2 { liquidExchange = liquidExchangeAddress; asset = assetAddress; quote = quoteAddress; + strategy = SolverLike(solver).strategy(); + strategyName = StrategyLike(strategy).name(); } bool public XTOY = true; bool public YTOX = false; error AttemptedProfit(int256 profit); + event LogDfmmData(uint256 price, uint256 timestamp, uint256 rx, uint256 ry, uint256 liq, uint256 strike, uint256 sigma, uint256 tau); + event LogAssetData(uint256 lexPrice, uint256 timestamp, uint256 rx, uint256 ry); + event LogArbData(uint256 xBalance, uint256 yBalance, uint256 timestamp); + + function logData(uint256 poolId) external { + + if (keccak256(abi.encode(strategyName)) == keccak256(abi.encode("LogNormal"))) { + (uint256 rx, uint256 ry, uint256 L) = SolverLike(solver).getReservesAndLiquidity(poolId); + uint256 price = SolverLike(solver).internalPrice(poolId); + LogNormalParams memory params = LogNormalStrategyLike(solver).fetchPoolParams(poolId); + emit LogDfmmData(price, block.timestamp, rx, ry, L, params.strike, params.sigma, params.tau); + } + + uint256 lexPrice = LiquidExchange(liquidExchange).price(); + uint256 lexBalanceX = TokenLike(asset).balanceOf(liquidExchange); + uint256 lexBalanceY = TokenLike(quote).balanceOf(liquidExchange); + emit LogAssetData(lexPrice, block.timestamp, lexBalanceX, lexBalanceY); + + uint256 arbBalanceX = TokenLike(asset).balanceOf(msg.sender); + uint256 arbBalanceY = TokenLike(quote).balanceOf(msg.sender); + emit LogArbData(arbBalanceX, arbBalanceY, block.timestamp); + + } function lower_exchange_price(uint256 poolId, uint256 input) external { - uint256 price = StrategyLike(solver).internalPrice(poolId); - emit Price(price, block.timestamp); // Arbitrageur Y -> AtomicV2 _invoice(input); @@ -114,8 +161,6 @@ contract AtomicV2 { } function raise_exchange_price(uint256 poolId, uint256 input) external { - uint256 price = StrategyLike(solver).internalPrice(poolId); - emit Price(price, block.timestamp); // Arbitrageur Y -> AtomicV2 _invoice(input); @@ -194,7 +239,7 @@ contract AtomicV2 { uint256 estimatedOut, uint256 estimatedPrice, bytes memory payload - ) = StrategyLike(solver).simulateSwap(poolId, swapXIn, amountIn); + ) = SolverLike(solver).simulateSwap(poolId, swapXIn, amountIn); if (!valid) { revert SimulatedSwapFailure( @@ -203,7 +248,7 @@ contract AtomicV2 { } // Execute the swap. - try StrategyLike(exchange).swap(poolId, payload) { + try DfmmLike(exchange).swap(poolId, payload) { // Swap succeeded, do nothing other than store the intermediary balance. if (swapXIn) { // If X -> Y @@ -262,7 +307,7 @@ contract AtomicV2 { bytes memory payload ) { - return StrategyLike(solver).simulateSwap(poolId, swapXIn, amountIn); + return SolverLike(solver).simulateSwap(poolId, swapXIn, amountIn); } function cdf(int256 input) public pure returns (int256 output) {