Skip to content

Commit

Permalink
add new invariant check
Browse files Browse the repository at this point in the history
  • Loading branch information
kinrezC committed Apr 29, 2024
1 parent fbfb58d commit a263e59
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 109 deletions.
55 changes: 16 additions & 39 deletions src/SYCoveredCall/SYCoveredCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -218,45 +218,19 @@ contract SYCoveredCall is PairStrategy {
SYCoveredCallParams memory ccParams =
abi.decode(params, (SYCoveredCallParams));

uint256 computedL;
uint256 swapTimestamp;
(
tokenInIndex,
tokenOutIndex,
amountIn,
amountOut,
computedL,
swapTimestamp
) = abi.decode(
data, (uint256, uint256, uint256, uint256, uint256, uint256)
);

console2.log("swapTimestamp", swapTimestamp);
console2.log("block.timestamp", block.timestamp);
(tokenInIndex, tokenOutIndex, amountIn, amountOut) =
abi.decode(data, (uint256, uint256, uint256, uint256));

if (
swapTimestamp < internalParams[poolId].lastTimestamp
|| swapTimestamp < block.timestamp - T_EPSILON
|| swapTimestamp > block.timestamp + T_EPSILON
) {
revert InvalidTimestamp();
}
ccParams.lastTimestamp = block.timestamp;

// if timestamp is valid, append it to the poolParams for validation check
ccParams.lastTimestamp = swapTimestamp;

ccParams.mean =
computeKGivenLastPrice(pool.reserves[0], computedL, ccParams);

int256 computedInvariant =
tradingFunction(pool.reserves, computedL, abi.encode(ccParams));
console2.log("got here");
ccParams.mean = computeKGivenLastPrice(
pool.reserves[0], pool.totalLiquidity, ccParams
);

if (computedInvariant < 0 || computedInvariant > EPSILON) {
revert InvalidComputedLiquidity(computedInvariant);
}
int256 prevInvariant = tradingFunction(
pool.reserves, pool.totalLiquidity, abi.encode(ccParams)
);

console2.log("now we compute dl");
deltaLiquidity = _computeSwapDeltaLiquidity(
pool,
abi.encode(ccParams),
Expand All @@ -265,18 +239,21 @@ contract SYCoveredCall is PairStrategy {
amountIn,
amountOut
);
console2.log("deltaLiquidity", deltaLiquidity);

pool.reserves[tokenInIndex] += amountIn;
pool.reserves[tokenOutIndex] -= amountOut;

invariant = tradingFunction(
pool.reserves, computedL + deltaLiquidity, abi.encode(ccParams)
pool.reserves,
pool.totalLiquidity + deltaLiquidity,
abi.encode(ccParams)
);

params = abi.encode(ccParams);
valid = invariant >= 0;
//valid = invariant >= 0 && invariant <= EPSILON;
console2.log("prevInvariant: ", prevInvariant);
console2.log("invariant: ", invariant);
console2.log("deltaLiquidity: ", deltaLiquidity);
valid = prevInvariant <= invariant;
}

function postSwapHook(
Expand Down
60 changes: 40 additions & 20 deletions src/SYCoveredCall/SYCoveredCallMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -323,20 +323,28 @@ function computeDeallocationGivenDeltaY(

/// @dev This is a pure 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 as an argument to find the root of the trading function given the reserveXWad.
function findRootY(bytes memory data, uint256 ry) pure returns (int256) {
(uint256 rx, uint256 L, SYCoveredCallParams memory params) =
abi.decode(data, (uint256, uint256, SYCoveredCallParams));
return computeTradingFunction(rx, ry, L, params);
(
uint256 rx,
uint256 L,
int256 prevInvariant,
SYCoveredCallParams memory params
) = abi.decode(data, (uint256, uint256, int256, SYCoveredCallParams));
return computeTradingFunction(rx, ry, L, params) - (prevInvariant + 1);
}

/// @dev This is a pure 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 as an argument to find the root of the trading function given the reserveYWad.
function findRootX(bytes memory data, uint256 rx) pure returns (int256) {
(uint256 ry, uint256 L, SYCoveredCallParams memory params) =
abi.decode(data, (uint256, uint256, SYCoveredCallParams));
return computeTradingFunction(rx, ry, L, params);
(
uint256 ry,
uint256 L,
int256 prevInvariant,
SYCoveredCallParams memory params
) = abi.decode(data, (uint256, uint256, int256, SYCoveredCallParams));
return computeTradingFunction(rx, ry, L, params) - (prevInvariant + 1);
}

/// @dev This is a pure anonymous function defined at the file level, which allows
Expand Down Expand Up @@ -404,15 +412,16 @@ function computeNextRx(
uint256 rY,
uint256 L,
int256 invariant,
int256 prevInvariant,
uint256 approximatedRx,
SYCoveredCallParams memory params
) pure returns (uint256 rX) {
uint256 upper = approximatedRx;
uint256 lower = approximatedRx;
int256 computedInvariant = invariant;
if (computedInvariant < 0) {
while (computedInvariant < 0) {
upper = upper.mulDivUp(1001, 1000);
if (computedInvariant - prevInvariant < 0) {
while (computedInvariant - prevInvariant < 0) {
upper = upper.mulDivUp(101, 100);
upper = upper > L ? L : upper;
computedInvariant = computeTradingFunction({
rX: upper,
Expand All @@ -422,8 +431,8 @@ function computeNextRx(
});
}
} else {
while (computedInvariant > 0) {
lower = lower.mulDivDown(999, 1000);
while (computedInvariant - prevInvariant > 0) {
lower = lower.mulDivDown(99, 100);
lower = lower > L ? L : lower;
computedInvariant = computeTradingFunction({
rX: lower,
Expand All @@ -434,7 +443,12 @@ function computeNextRx(
}
}
(uint256 rootInput, uint256 upperInput,) = bisection(
abi.encode(rY, L, params), lower, upper, 0, MAX_ITER, findRootX
abi.encode(rY, L, prevInvariant, params),
lower,
upper,
0,
MAX_ITER,
findRootX
);
// `upperInput` should be positive, so if root is < 0 return upperInput instead
if (
Expand All @@ -451,15 +465,16 @@ function computeNextRy(
uint256 rX,
uint256 L,
int256 invariant,
int256 prevInvariant,
uint256 approximatedRy,
SYCoveredCallParams memory params
) pure returns (uint256 rY) {
uint256 upper = approximatedRy;
uint256 lower = approximatedRy;
int256 computedInvariant = invariant;
if (computedInvariant < 0) {
while (computedInvariant < 0) {
upper = upper.mulDivUp(1001, 1000);
if (computedInvariant - prevInvariant < 0) {
while (computedInvariant - prevInvariant < 0) {
upper = upper.mulDivUp(101, 100);
computedInvariant = computeTradingFunction({
rX: rX,
rY: upper,
Expand All @@ -468,8 +483,8 @@ function computeNextRy(
});
}
} else {
while (computedInvariant > 0) {
lower = lower.mulDivDown(999, 1000);
while (computedInvariant - prevInvariant > 0) {
lower = lower.mulDivDown(99, 100);
computedInvariant = computeTradingFunction({
rX: rX,
rY: lower,
Expand All @@ -479,7 +494,12 @@ function computeNextRy(
}
}
(uint256 rootInput, uint256 upperInput,) = bisection(
abi.encode(rX, L, params), lower, upper, 0, MAX_ITER, findRootY
abi.encode(rX, L, prevInvariant, params),
lower,
upper,
0,
MAX_ITER,
findRootY
);
// `upperInput` should be positive, so if root is < 0 return upperInput instead
if (
Expand Down
107 changes: 64 additions & 43 deletions src/SYCoveredCall/SYCoveredCallSolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import {
computeDeltaYGivenDeltaL,
computeDeltaXGivenDeltaL
} from "src/lib/StrategyLib.sol";
import "forge-std/console2.sol";

contract SYCoveredCallSolver is ISolver {
using FixedPointMathLib for uint256;
Expand Down Expand Up @@ -184,7 +185,6 @@ contract SYCoveredCallSolver is ISolver {
uint256 amountOut;
uint256 deltaLiquidity;
uint256 fees;
uint256 timestamp;
}

function prepareSwap(
Expand All @@ -200,15 +200,17 @@ contract SYCoveredCallSolver is ISolver {
getPoolParamsCustomTimestamp(poolId, block.timestamp);

SimulateSwapState memory state;
state.timestamp = block.timestamp;

uint256 startComputedL = getNextLiquidity(
poolId, preReserves[0], preReserves[1], preTotalLiquidity
);
poolParams.mean = computeKGivenLastPrice(
preReserves[0], preTotalLiquidity, poolParams
);

int256 prevInvariant = computeTradingFunction(
preReserves[0], preReserves[1], preTotalLiquidity, poolParams
);

console2.log("prevInvariant", prevInvariant);

uint256 poolId = poolId;
uint256 swapAmountIn = amountIn;
{
Expand All @@ -222,11 +224,17 @@ contract SYCoveredCallSolver is ISolver {
);

endReserves.rx = preReserves[0] + swapAmountIn;
endReserves.L = startComputedL + state.deltaLiquidity;
uint256 approxPrice = getEstimatedPrice(poolId, 0, 1);
endReserves.L = preTotalLiquidity + state.deltaLiquidity;
uint256 approxPrice = getIntermediatePrice(
endReserves.rx, endReserves.L, 0, 1, poolParams
);

endReserves.ry = getNextReserveY(
poolId, endReserves.rx, endReserves.L, approxPrice
endReserves.rx,
endReserves.L,
approxPrice,
prevInvariant,
poolParams
);

require(
Expand All @@ -244,11 +252,17 @@ contract SYCoveredCallSolver is ISolver {
);

endReserves.ry = preReserves[1] + swapAmountIn;
endReserves.L = startComputedL + state.deltaLiquidity;
uint256 approxPrice = getEstimatedPrice(poolId, 1, 0);
endReserves.L = preTotalLiquidity + state.deltaLiquidity;
uint256 approxPrice = getIntermediatePrice(
endReserves.ry, endReserves.L, 1, 0, poolParams
);

endReserves.rx = getNextReserveX(
poolId, endReserves.ry, endReserves.L, approxPrice
endReserves.ry,
endReserves.L,
approxPrice,
prevInvariant,
poolParams
);

require(
Expand All @@ -266,23 +280,9 @@ contract SYCoveredCallSolver is ISolver {
bytes memory swapData;

if (tokenInIndex == 0) {
swapData = abi.encode(
0,
1,
swapAmountIn,
state.amountOut,
startComputedL,
state.timestamp
);
swapData = abi.encode(0, 1, swapAmountIn, state.amountOut);
} else {
swapData = abi.encode(
1,
0,
swapAmountIn,
state.amountOut,
startComputedL,
state.timestamp
);
swapData = abi.encode(1, 0, swapAmountIn, state.amountOut);
}

(bool valid,,,,,,,) = IStrategy(strategy).validateSwap(
Expand All @@ -292,6 +292,25 @@ contract SYCoveredCallSolver is ISolver {
return (valid, state.amountOut, swapData);
}

function getIntermediatePrice(
uint256 independentReserve,
uint256 L,
uint256 tokenInIndex,
uint256 tokenOutIndex,
SYCoveredCallParams memory params
) public view returns (uint256 price) {
if (
tokenInIndex > 1 || tokenOutIndex > 1
|| tokenInIndex == tokenOutIndex
) revert InvalidTokenIndex();

if (tokenInIndex == 0) {
price = computePriceGivenX(independentReserve, L, params);
} else {
price = computePriceGivenY(independentReserve, L, params);
}
}

/// @inheritdoc ISolver
function getEstimatedPrice(
uint256 poolId,
Expand Down Expand Up @@ -344,30 +363,32 @@ contract SYCoveredCallSolver is ISolver {
}

function getNextReserveX(
uint256 poolId,
uint256 ry,
uint256 L,
uint256 S
uint256 S,
int256 prevInvariant,
SYCoveredCallParams memory params
) public view returns (uint256) {
SYCoveredCallParams memory poolParams =
getPoolParamsCustomTimestamp(poolId, block.timestamp);
uint256 approximatedRx = computeXGivenL(L, S, poolParams);
int256 invariant =
computeTradingFunction(approximatedRx, ry, L, poolParams);
return computeNextRx(ry, L, invariant, approximatedRx, poolParams);
uint256 approximatedRx = computeXGivenL(L, S, params);
int256 invariant = computeTradingFunction(approximatedRx, ry, L, params);
console2.log("intermediate invariant", invariant);
return computeNextRx(
ry, L, invariant, prevInvariant, approximatedRx, params
);
}

function getNextReserveY(
uint256 poolId,
uint256 rx,
uint256 L,
uint256 S
uint256 S,
int256 prevInvariant,
SYCoveredCallParams memory params
) public view returns (uint256) {
SYCoveredCallParams memory poolParams =
getPoolParamsCustomTimestamp(poolId, block.timestamp);
uint256 approximatedRy = computeYGivenL(L, S, poolParams);
int256 invariant =
computeTradingFunction(rx, approximatedRy, L, poolParams);
return computeNextRy(rx, L, invariant, approximatedRy, poolParams);
uint256 approximatedRy = computeYGivenL(L, S, params);
int256 invariant = computeTradingFunction(rx, approximatedRy, L, params);
console2.log("intermediate invariant", invariant);
return computeNextRy(
rx, L, invariant, prevInvariant, approximatedRy, params
);
}
}
2 changes: 0 additions & 2 deletions test/SYCoveredCall/unit/SetUp.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ contract SYCoveredCallSetUp is SetUp {
uint256 public constant FEE = 0.00001 ether;

uint256 defaultReserveX = 100 ether;
uint256 defaultReserveXMil = 1_000_000 ether;
uint256 defaultReserveXDeep = ONE * 10_000_000;

uint256 defaultPrice = ONE;
uint256 defaultPricePoint9Rate = 0.84167999326 ether;
Expand Down
Loading

0 comments on commit a263e59

Please sign in to comment.