Skip to content

Commit

Permalink
Merge pull request #49 from primitivefinance/feat/remove-liq-data-cla…
Browse files Browse the repository at this point in the history
…sses

Feat/remove liq data classes
  • Loading branch information
Alexangelj authored Aug 15, 2022
2 parents cf62e7c + 7549dea commit 893fcc9
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 23 deletions.
11 changes: 11 additions & 0 deletions contracts/libraries/Instructions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ library Instructions {
data = abi.encodePacked(ADD_LIQUIDITY, useMax, poolId, loTick, hiTick, power, amount);
}

function encodeRemoveLiquidity(
uint8 useMax,
uint48 poolId,
int24 loTick,
int24 hiTick,
uint8 power,
uint8 amount
) internal pure returns (bytes memory data) {
data = abi.encodePacked(REMOVE_LIQUIDITY, useMax, poolId, loTick, hiTick, power, amount);
}

/// @dev Expects the standard instruction with two trailing run-length encoded amounts.
/// @param data Maximum 8 + 3 + 3 + 16 + 16 = 46 bytes.
/// | 0x | 1 packed byte useMax Flag - enigma code | 6 byte poolId | 3 byte loTick | 3 byte hiTick | 1 byte pointer to next power byte | 1 byte power | ...amount | 1 byte power | ...amount |
Expand Down
2 changes: 1 addition & 1 deletion contracts/prototype/EnigmaVirtualMachinePrototype.sol
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ abstract contract EnigmaVirtualMachinePrototype is IEnigma {

// --- State --- //
/// @dev Tick index -> Info of the price slot.
mapping(int24 => HyperSlot) internal _slots;
mapping(uint48 => mapping(int24 => HyperSlot)) internal _slots;
/// @dev Pool id -> Pair of a Pool.
mapping(uint16 => Pair) internal _pairs;
/// @dev Pool id -> HyperPool Data Structure.
Expand Down
33 changes: 18 additions & 15 deletions contracts/prototype/HyperPrototype.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
if (!_doesPoolExist(poolId_)) revert NonExistentPool(poolId_);
// Compute amounts of tokens for the real reserves.
Curve memory curve = _curves[uint32(poolId_)];
HyperSlot memory slot = _slots[loTick];
HyperSlot memory slot = _slots[poolId_][loTick];
HyperPool memory pool = _pools[poolId_];
uint256 timestamp = _blockTimestamp();

Expand Down Expand Up @@ -81,14 +81,14 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
)
{
(uint8 useMax, uint48 poolId_, uint16 pairId, int24 loTick, int24 hiTick, uint128 deltaLiquidity) = Instructions
.decodeRemoveLiquidity(data);
.decodeRemoveLiquidity(data[1:]); // Trims Enigma Instruction Code.

if (deltaLiquidity == 0) revert ZeroLiquidityError();
if (!_doesPoolExist(poolId_)) revert NonExistentPool(poolId_);

// Compute amounts of tokens for the real reserves.
Curve memory curve = _curves[uint32(poolId_)];
HyperSlot memory slot = _slots[loTick];
HyperSlot memory slot = _slots[poolId_][loTick];
HyperPool memory pool = _pools[poolId_];
uint256 timestamp = _blockTimestamp();
uint256 price = _computePriceGivenTickIndex(loTick);
Expand All @@ -98,16 +98,18 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
curve.sigma,
curve.maturity - timestamp
);

uint256 deltaR2 = HyperSwapLib.computeR2WithPrice(price, curve.strike, curve.sigma, curve.maturity - timestamp); // todo: I don't think this is right since its (1 - (x / x(P_a)))
deltaR2 = currentR2.divWadDown(deltaR2);
if (deltaR2 == 0) deltaR2 = currentR2;
else deltaR2 = currentR2.divWadDown(deltaR2);

uint256 deltaR1 = computeR1GivenR2(deltaR2, curve.strike, curve.sigma, curve.maturity, price); // todo: fix with using the hiTick.
deltaR1 = deltaR1.mulWadDown(deltaLiquidity);
deltaR2 = deltaR2.mulWadDown(deltaLiquidity);

// Decrease amount of liquidity in each tick.
_decreaseSlotLiquidity(loTick, deltaLiquidity, false);
_decreaseSlotLiquidity(hiTick, deltaLiquidity, true);
_decreaseSlotLiquidity(poolId_, loTick, deltaLiquidity, false);
_decreaseSlotLiquidity(poolId_, hiTick, deltaLiquidity, true);

// Todo: delete any slots if uninstantiated.

Expand All @@ -117,8 +119,8 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {

// note: Global reserves are referenced at end of processing to determine amounts of token to transfer.
Pair memory pair = _pairs[pairId];
_decreaseGlobal(pair.tokenBase, deltaR1);
_decreaseGlobal(pair.tokenQuote, deltaR2);
_decreaseGlobal(pair.tokenBase, deltaR2);
_decreaseGlobal(pair.tokenQuote, deltaR1);

emit RemoveLiquidity(poolId_, pairId, deltaR1, deltaR2, deltaLiquidity);
}
Expand All @@ -134,8 +136,8 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
if (deltaLiquidity == 0) revert ZeroLiquidityError();

// Update the slots.
_increaseSlotLiquidity(loTick, deltaLiquidity, false);
_increaseSlotLiquidity(hiTick, deltaLiquidity, true);
_increaseSlotLiquidity(poolId, loTick, deltaLiquidity, false);
_increaseSlotLiquidity(poolId, hiTick, deltaLiquidity, true);

// Todo: update bitmap of instantiated slots.

Expand Down Expand Up @@ -164,7 +166,6 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
) internal view returns (uint256 R1) {
uint256 tau = maturity - _blockTimestamp();
R1 = Invariant.getY(R2, strike, sigma, tau, 0); // todo: add non-zero invariant
console.log(R2, strike, tau, R1);
// todo: add hiTick range
//R1 = R1.mulWadDown(price); // Multiplies price to calibrate to the price specified.
}
Expand All @@ -173,11 +174,12 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
* @notice Updates the liquidity of a slot, and returns a bool to reflect whether its instantiation state was changed.
*/
function _increaseSlotLiquidity(
uint48 poolId,
int24 tickIndex,
uint256 deltaLiquidity,
bool hi
) internal returns (bool alterState) {
HyperSlot storage slot = _slots[tickIndex];
HyperSlot storage slot = _slots[poolId][tickIndex];

uint256 prevLiquidity = slot.totalLiquidity;
uint256 nextLiquidity = slot.totalLiquidity + deltaLiquidity;
Expand All @@ -193,16 +195,17 @@ abstract contract HyperPrototype is EnigmaVirtualMachinePrototype {
* @notice Updates the liquidity of a slot, and returns a bool to reflect whether its instantiation state was changed.
*/
function _decreaseSlotLiquidity(
uint48 poolId,
int24 tickIndex,
uint256 deltaLiquidity,
bool hi
) internal returns (bool alterState) {
HyperSlot storage slot = _slots[tickIndex];
HyperSlot storage slot = _slots[poolId][tickIndex];

uint256 prevLiquidity = slot.totalLiquidity;
uint256 nextLiquidity = slot.totalLiquidity + deltaLiquidity;
uint256 nextLiquidity = slot.totalLiquidity - deltaLiquidity;

alterState = !((prevLiquidity != 0) && (nextLiquidity == 0)); // If there was liquidity previously and all of it was removed.
alterState = (prevLiquidity == 0 && nextLiquidity != 0) || (prevLiquidity != 0 && nextLiquidity == 0); // If there was liquidity previously and all of it was removed.

slot.totalLiquidity = nextLiquidity;
if (alterState) slot.instantiated = !slot.instantiated;
Expand Down
136 changes: 129 additions & 7 deletions test/foundry/HyperPrototype.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ contract TestHyperPrototype is HyperPrototype, BaseTest {
// --- Helpers --- //

function getTickLiquidity(int24 tick) public view returns (uint256) {
return _slots[tick].totalLiquidity;
return _slots[__poolId][tick].totalLiquidity;
}

// --- Add liquidity --- //
Expand Down Expand Up @@ -199,31 +199,31 @@ contract TestHyperPrototype is HyperPrototype, BaseTest {

function testA_LLowTickInstantiatedChange() public {
int24 tick = DEFAULT_TICK;
bool instantiated = _slots[tick].instantiated;
bool instantiated = _slots[__poolId][tick].instantiated;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, tick, tick + 2, power, amount);
bool success = forwarder.pass(data);
assertTrue(success, "forwarder call failed");

bool change = _slots[tick].instantiated;
bool change = _slots[__poolId][tick].instantiated;
assertTrue(instantiated != change);
}

function testA_LHighTickInstantiatedChange() public {
int24 tick = DEFAULT_TICK;
bool instantiated = _slots[tick].instantiated;
bool instantiated = _slots[__poolId][tick].instantiated;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, tick - 2, tick, power, amount);
bool success = forwarder.pass(data);
assertTrue(success, "forwarder call failed");

bool change = _slots[tick].instantiated;
bool change = _slots[__poolId][tick].instantiated;
assertTrue(instantiated != change);
}

function testA_LGlobalAssetIncrease() public {
function testA_LGlobalAssetIncreases() public {
uint256 prevGlobal = _globalReserves[address(asset)];
int24 loTick = DEFAULT_TICK;
int24 hiTick = DEFAULT_TICK + 2;
Expand All @@ -238,7 +238,7 @@ contract TestHyperPrototype is HyperPrototype, BaseTest {
assertTrue(nextGlobal > prevGlobal, "reserves did not change");
}

function testA_LGlobalQuoteIncrease() public {
function testA_LGlobalQuoteIncreases() public {
uint256 prevGlobal = _globalReserves[address(quote)];
int24 loTick = DEFAULT_TICK - 256; // Enough below to have quote.
int24 hiTick = DEFAULT_TICK + 2;
Expand All @@ -253,6 +253,128 @@ contract TestHyperPrototype is HyperPrototype, BaseTest {
assertTrue(nextGlobal > prevGlobal, "reserves did not change");
}

// --- Remove Liquidity --- //

function testFailR_LZeroLiquidityReverts() public {
bytes memory data = Instructions.encodeRemoveLiquidity(0, __poolId, 1, 1, 0x00, 0x00);
bool success = forwarder.pass(data);
assertTrue(!success);
}

function testFailR_LNonExistentPoolReverts() public {
bytes memory data = Instructions.encodeRemoveLiquidity(0, 42, 1, 1, 0x01, 0x01);
bool success = forwarder.pass(data);
assertTrue(!success);
}

function testR_LLowTickLiquidityDecreases() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

uint256 prev = _slots[__poolId][lo].totalLiquidity;

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

uint256 next = _slots[__poolId][lo].totalLiquidity;
assertTrue(next < prev);
}

function testR_LHighTickLiquidityDecreases() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

uint256 prev = _slots[__poolId][hi].totalLiquidity;

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

uint256 next = _slots[__poolId][hi].totalLiquidity;
assertTrue(next < prev);
}

function testR_LLowTickInstantiatedChanges() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

bool prev = _slots[__poolId][lo].instantiated;

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

bool next = _slots[__poolId][lo].instantiated;
assertTrue(next != prev);
}

function testR_LHighTickInstantiatedChanges() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

bool prev = _slots[__poolId][hi].instantiated;

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

bool next = _slots[__poolId][hi].instantiated;
assertTrue(next != prev);
}

function testR_LGlobalAssetDecreases() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

uint256 prev = _globalReserves[address(asset)];

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

uint256 next = _globalReserves[address(asset)];
assertTrue(next < prev, "reserves did not change");
}

function testR_LGlobalQuoteDecreases() public {
int24 lo = DEFAULT_TICK - 256;
int24 hi = DEFAULT_TICK;
uint8 amount = 0x01;
uint8 power = 0x01;
bytes memory data = Instructions.encodeAddLiquidity(0, __poolId, lo, hi, power, amount);
bool success = forwarder.pass(data);
assertTrue(success);

uint256 prev = _globalReserves[address(quote)];

data = Instructions.encodeRemoveLiquidity(0, __poolId, lo, hi, power, amount);
success = forwarder.pass(data);

uint256 next = _globalReserves[address(quote)];
assertTrue(next < prev, "reserves did not change");
}

// --- Create Pair --- //

function testFailC_PrSameTokensReverts() public {
Expand Down

0 comments on commit 893fcc9

Please sign in to comment.