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

Feat/new invariant check #144

Open
wants to merge 2 commits into
base: feat/v0.3.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading