From 3492ff36fd56f6de82d860917f29591347a4c5ca Mon Sep 17 00:00:00 2001 From: alex Date: Tue, 9 Apr 2024 23:45:31 -0700 Subject: [PATCH 1/2] fix(strategies): new base strategy abstract contract with single sided deposits --- src/ConstantSum/ConstantSum.sol | 104 +-------- src/GeometricMean/GeometricMean.sol | 40 +--- src/LogNormal/LogNormal.sol | 44 +--- .../NTokenGeometricMean.sol | 71 ++---- src/NTokenStrategy.sol | 216 ------------------ src/{PairStrategy.sol => Strategy.sol} | 136 +++++------ test/ConstantSum/unit/Allocate.t.sol | 9 +- test/G3M/unit/Allocate.t.sol | 95 +++++--- test/G3M/unit/Deallocate.t.sol | 37 ++- test/G3M/unit/G3M.t.sol | 17 +- test/LogNormal/unit/Allocate.t.sol | 70 +++++- test/LogNormal/unit/Deallocate.t.sol | 12 +- .../NTokenGeometricMean.t.sol | 23 ++ 13 files changed, 317 insertions(+), 557 deletions(-) delete mode 100644 src/NTokenStrategy.sol rename src/{PairStrategy.sol => Strategy.sol} (54%) diff --git a/src/ConstantSum/ConstantSum.sol b/src/ConstantSum/ConstantSum.sol index ef0ef9ee..4930d06e 100644 --- a/src/ConstantSum/ConstantSum.sol +++ b/src/ConstantSum/ConstantSum.sol @@ -12,7 +12,7 @@ import { decodeFeeUpdate, decodeControllerUpdate } from "./ConstantSumUtils.sol"; -import { PairStrategy, IStrategy, Pool } from "src/PairStrategy.sol"; +import { Strategy, IStrategy, Pool } from "src/Strategy.sol"; import { EPSILON } from "src/lib/StrategyLib.sol"; struct InternalParams { @@ -34,7 +34,7 @@ enum UpdateCode { Controller } -contract ConstantSum is PairStrategy { +contract ConstantSum is Strategy { using FixedPointMathLib for uint256; /// @notice Thrown when the expected liquidity is not met. @@ -46,7 +46,7 @@ contract ConstantSum is PairStrategy { mapping(uint256 => InternalParams) public internalParams; /// @param dfmm_ Address of the DFMM contract. - constructor(address dfmm_) PairStrategy(dfmm_) { } + constructor(address dfmm_) Strategy(dfmm_) { } /// @inheritdoc IStrategy function init( @@ -87,84 +87,6 @@ contract ConstantSum is PairStrategy { return (valid, invariant, reserves, totalLiquidity); } - function validateAllocate( - address, - uint256 poolId, - Pool memory pool, - bytes calldata data - ) - external - view - override - returns ( - bool valid, - int256 invariant, - uint256[] memory deltas, - uint256 deltaLiquidity - ) - { - (uint256 deltaX, uint256 deltaY, uint256 minDeltaL) = - abi.decode(data, (uint256, uint256, uint256)); - - deltaLiquidity = - computeDeltaLiquidity(deltaX, deltaY, internalParams[poolId].price); - if (deltaLiquidity < minDeltaL) revert InvalidDeltaLiquidity(); - - deltas = new uint256[](2); - deltas[0] = deltaX; - deltas[1] = deltaY; - - pool.reserves[0] += deltaX; - pool.reserves[1] += deltaY; - - invariant = tradingFunction( - pool.reserves, - pool.totalLiquidity + deltaLiquidity, - getPoolParams(poolId) - ); - - valid = invariant >= 0; - } - - function validateDeallocate( - address, - uint256 poolId, - Pool memory pool, - bytes calldata data - ) - external - view - override - returns ( - bool valid, - int256 invariant, - uint256[] memory deltas, - uint256 deltaLiquidity - ) - { - (uint256 deltaX, uint256 deltaY, uint256 maxDeltaL) = - abi.decode(data, (uint256, uint256, uint256)); - - deltaLiquidity = - computeDeltaLiquidity(deltaX, deltaY, internalParams[poolId].price); - if (maxDeltaL > deltaLiquidity) revert InvalidDeltaLiquidity(); - - deltas = new uint256[](2); - deltas[0] = deltaX; - deltas[1] = deltaY; - - pool.reserves[0] -= deltaX; - pool.reserves[1] -= deltaY; - - invariant = tradingFunction( - pool.reserves, - pool.totalLiquidity - deltaLiquidity, - getPoolParams(poolId) - ); - - valid = invariant >= 0; - } - /// @inheritdoc IStrategy function update( address sender, @@ -215,25 +137,7 @@ contract ConstantSum is PairStrategy { ); } - /// @inheritdoc PairStrategy - function _computeAllocateDeltasGivenDeltaL( - uint256, - Pool memory, - bytes memory - ) internal pure override returns (uint256[] memory) { - return new uint256[](2); - } - - /// @inheritdoc PairStrategy - function _computeDeallocateDeltasGivenDeltaL( - uint256, - Pool memory, - bytes memory - ) internal pure override returns (uint256[] memory) { - return new uint256[](2); - } - - /// @inheritdoc PairStrategy + /// @inheritdoc Strategy function _computeSwapDeltaLiquidity( Pool memory, bytes memory params, diff --git a/src/GeometricMean/GeometricMean.sol b/src/GeometricMean/GeometricMean.sol index 6585ed1d..df79f3f9 100644 --- a/src/GeometricMean/GeometricMean.sol +++ b/src/GeometricMean/GeometricMean.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.22; import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol"; -import { PairStrategy, IStrategy } from "src/PairStrategy.sol"; +import { Strategy, IStrategy } from "src/Strategy.sol"; import { DynamicParam, DynamicParamLib } from "src/lib/DynamicParamLib.sol"; import { Pool } from "src/interfaces/IDFMM.sol"; import { @@ -37,7 +37,7 @@ enum UpdateCode { /** * @notice Geometric Mean Market Maker. */ -contract GeometricMean is PairStrategy { +contract GeometricMean is Strategy { using FixedPointMathLib for uint256; using FixedPointMathLib for int256; using DynamicParamLib for DynamicParam; @@ -54,7 +54,7 @@ contract GeometricMean is PairStrategy { mapping(uint256 => InternalParams) public internalParams; /// @param dfmm_ Address of the DFMM contract. - constructor(address dfmm_) PairStrategy(dfmm_) { } + constructor(address dfmm_) Strategy(dfmm_) { } /// @dev Thrown if the weight of X is greater than 1 (in WAD). error InvalidWeightX(); @@ -170,39 +170,7 @@ contract GeometricMean is PairStrategy { ); } - /// @inheritdoc PairStrategy - function _computeAllocateDeltasGivenDeltaL( - uint256 deltaLiquidity, - Pool memory pool, - bytes memory - ) internal pure override returns (uint256[] memory deltas) { - deltas = new uint256[](2); - deltas[0] = computeDeltaGivenDeltaLRoundUp( - pool.reserves[0], deltaLiquidity, pool.totalLiquidity - ); - - deltas[1] = computeDeltaGivenDeltaLRoundUp( - pool.reserves[1], deltaLiquidity, pool.totalLiquidity - ); - } - - /// @inheritdoc PairStrategy - function _computeDeallocateDeltasGivenDeltaL( - uint256 deltaLiquidity, - Pool memory pool, - bytes memory - ) internal pure override returns (uint256[] memory deltas) { - deltas = new uint256[](2); - deltas[0] = computeDeltaGivenDeltaLRoundDown( - pool.reserves[0], deltaLiquidity, pool.totalLiquidity - ); - - deltas[1] = computeDeltaGivenDeltaLRoundDown( - pool.reserves[1], deltaLiquidity, pool.totalLiquidity - ); - } - - /// @inheritdoc PairStrategy + /// @inheritdoc Strategy function _computeSwapDeltaLiquidity( Pool memory pool, bytes memory params, diff --git a/src/LogNormal/LogNormal.sol b/src/LogNormal/LogNormal.sol index d3fb8587..734eb04a 100644 --- a/src/LogNormal/LogNormal.sol +++ b/src/LogNormal/LogNormal.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.22; import { Pool } from "src/interfaces/IDFMM.sol"; -import { PairStrategy, IStrategy } from "src/PairStrategy.sol"; +import { Strategy, IStrategy } from "src/Strategy.sol"; import { DynamicParamLib, DynamicParam } from "src/lib/DynamicParamLib.sol"; import { computeTradingFunction, @@ -57,7 +57,7 @@ uint256 constant MAX_MEAN = uint256(type(int256).max); * @title LogNormal Strategy for DFMM. * @author Primitive */ -contract LogNormal is PairStrategy { +contract LogNormal is Strategy { using DynamicParamLib for DynamicParam; /// @inheritdoc IStrategy @@ -66,7 +66,7 @@ contract LogNormal is PairStrategy { mapping(uint256 => InternalParams) public internalParams; /// @param dfmm_ Address of the DFMM contract. - constructor(address dfmm_) PairStrategy(dfmm_) { } + constructor(address dfmm_) Strategy(dfmm_) { } /// @inheritdoc IStrategy function init( @@ -173,43 +173,7 @@ contract LogNormal is PairStrategy { ); } - /// @inheritdoc PairStrategy - function _computeAllocateDeltasGivenDeltaL( - uint256 deltaLiquidity, - Pool memory pool, - bytes memory - ) internal pure override returns (uint256[] memory) { - uint256[] memory deltas = new uint256[](2); - - deltas[0] = computeDeltaGivenDeltaLRoundUp( - pool.reserves[0], deltaLiquidity, pool.totalLiquidity - ); - - deltas[1] = computeDeltaGivenDeltaLRoundUp( - pool.reserves[1], deltaLiquidity, pool.totalLiquidity - ); - - return deltas; - } - - /// @inheritdoc PairStrategy - function _computeDeallocateDeltasGivenDeltaL( - uint256 deltaLiquidity, - Pool memory pool, - bytes memory - ) internal pure override returns (uint256[] memory) { - uint256[] memory deltas = new uint256[](2); - - deltas[0] = computeDeltaGivenDeltaLRoundDown( - pool.reserves[0], deltaLiquidity, pool.totalLiquidity - ); - - deltas[1] = computeDeltaGivenDeltaLRoundDown( - pool.reserves[1], deltaLiquidity, pool.totalLiquidity - ); - return deltas; - } - + /// @inheritdoc Strategy function _computeSwapDeltaLiquidity( Pool memory pool, bytes memory params, diff --git a/src/NTokenGeometricMean/NTokenGeometricMean.sol b/src/NTokenGeometricMean/NTokenGeometricMean.sol index e885a9e4..c578607f 100644 --- a/src/NTokenGeometricMean/NTokenGeometricMean.sol +++ b/src/NTokenGeometricMean/NTokenGeometricMean.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.22; import { DynamicParam, DynamicParamLib } from "src/lib/DynamicParamLib.sol"; -import { NTokenStrategy, IStrategy } from "src/NTokenStrategy.sol"; +import { Strategy, IStrategy } from "src/Strategy.sol"; import { Pool } from "src/interfaces/IDFMM.sol"; import { decodeFeeUpdate, @@ -39,7 +39,7 @@ enum UpdateCode { /** * @notice N-Token Geometric Mean Market Maker. */ -contract NTokenGeometricMean is NTokenStrategy { +contract NTokenGeometricMean is Strategy { using DynamicParamLib for DynamicParam; struct InternalParams { @@ -54,7 +54,7 @@ contract NTokenGeometricMean is NTokenStrategy { mapping(uint256 => InternalParams) public internalParams; /// @param dfmm_ Address of the DFMM contract. - constructor(address dfmm_) NTokenStrategy(dfmm_) { } + constructor(address dfmm_) Strategy(dfmm_) { } /// @dev Thrown when the sum of the weights is not equal to 1 (in WAD). error InvalidWeights(uint256 totalWeight); @@ -201,57 +201,7 @@ contract NTokenGeometricMean is NTokenStrategy { ); } - /// @inheritdoc NTokenStrategy - function _computeAllocateDeltasAndReservesGivenDeltaL( - uint256 deltaLiquidity, - uint256[] memory maxDeltas, - Pool memory pool - ) - internal - pure - override - returns (uint256[] memory deltas, uint256[] memory nextReserves) - { - deltas = new uint256[](pool.reserves.length); - nextReserves = new uint256[](pool.reserves.length); - for (uint256 i = 0; i < pool.reserves.length; i++) { - uint256 reserveT = pool.reserves[i]; - deltas[i] = computeDeltaGivenDeltaLRoundUp( - pool.reserves[i], deltaLiquidity, pool.totalLiquidity - ); - if (deltas[i] > maxDeltas[i]) { - revert DeltaError(maxDeltas[i], deltas[i]); - } - nextReserves[i] = reserveT + deltas[i]; - } - } - - /// @inheritdoc NTokenStrategy - function _computeDeallocateDeltasAndReservesGivenDeltaL( - uint256 deltaLiquidity, - uint256[] memory minDeltas, - Pool memory pool - ) - internal - pure - override - returns (uint256[] memory deltas, uint256[] memory nextReserves) - { - deltas = new uint256[](pool.reserves.length); - nextReserves = new uint256[](pool.reserves.length); - for (uint256 i = 0; i < pool.reserves.length; i++) { - uint256 reserveT = pool.reserves[i]; - deltas[i] = computeDeltaGivenDeltaLRoundDown( - reserveT, deltaLiquidity, pool.totalLiquidity - ); - if (minDeltas[i] > deltas[i]) { - revert DeltaError(minDeltas[i], deltas[i]); - } - nextReserves[i] = reserveT - deltas[i]; - } - } - - /// @inheritdoc NTokenStrategy + /// @inheritdoc Strategy function _computeSwapDeltaLiquidity( Pool memory pool, bytes memory params, @@ -271,4 +221,17 @@ contract NTokenGeometricMean is NTokenStrategy { poolParams.swapFee ); } + + /// todo: work on this + /// @inheritdoc Strategy + /* function _computeDeltaLGivenDeltas( + uint256[] memory tokenDeltas, + Pool memory pool, + bytes memory params + ) internal view override returns (uint256) { + NTokenGeometricMeanParams memory poolParams = + abi.decode(params, (NTokenGeometricMeanParams)); + + return computeL(pool.reserves, poolParams); + } */ } diff --git a/src/NTokenStrategy.sol b/src/NTokenStrategy.sol deleted file mode 100644 index 281dab90..00000000 --- a/src/NTokenStrategy.sol +++ /dev/null @@ -1,216 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.13; - -import { IStrategy, Pool } from "src/interfaces/IStrategy.sol"; - -/** - * @dev Thrown when the length of the deltas array is not the - * same as the length of the reserves array. - */ -error InvalidTokenDeltas(); - -/** - * @title N-token strategy base contract for DFMM. - * @notice This abstract contract defines the basic behavior of - * a n-token strategy for DFMM. It is meant to be inherited by - * a concrete strategy implementation. - * @author Primitive - */ -abstract contract NTokenStrategy is IStrategy { - /// @inheritdoc IStrategy - address public immutable dfmm; - - /// @param dfmm_ Address of the DFMM contract. - constructor(address dfmm_) { - dfmm = dfmm_; - } - - /// @dev Restricts the caller to the DFMM contract. - modifier onlyDFMM() { - if (msg.sender != address(dfmm)) revert NotDFMM(); - _; - } - - /// @inheritdoc IStrategy - function validateAllocate( - address, - uint256 poolId, - Pool calldata pool, - bytes calldata data - ) - external - view - virtual - returns ( - bool valid, - int256 invariant, - uint256[] memory tokenDeltas, - uint256 deltaLiquidity - ) - { - // We use `deltaL` as a temporary variable because - // we cannot assign to `deltaLiquidity` directly. - (uint256[] memory maxTokenDeltas, uint256 deltaL) = - abi.decode(data, (uint256[], uint256)); - if (maxTokenDeltas.length != pool.reserves.length) { - revert InvalidTokenDeltas(); - } - deltaLiquidity = deltaL; - - (uint256[] memory deltas, uint256[] memory nextReserves) = - _computeAllocateDeltasAndReservesGivenDeltaL( - deltaLiquidity, maxTokenDeltas, pool - ); - tokenDeltas = deltas; - - invariant = tradingFunction( - nextReserves, - pool.totalLiquidity + deltaLiquidity, - getPoolParams(poolId) - ); - - valid = invariant >= 0; - } - - /// @inheritdoc IStrategy - function validateDeallocate( - address, - uint256 poolId, - Pool calldata pool, - bytes calldata data - ) - external - view - virtual - returns ( - bool valid, - int256 invariant, - uint256[] memory tokenDeltas, - uint256 deltaLiquidity - ) - { - (uint256[] memory minTokenDeltas, uint256 deltaL) = - abi.decode(data, (uint256[], uint256)); - if (minTokenDeltas.length != pool.reserves.length) { - revert InvalidTokenDeltas(); - } - deltaLiquidity = deltaL; - - (uint256[] memory deltas, uint256[] memory nextReserves) = - _computeDeallocateDeltasAndReservesGivenDeltaL( - deltaLiquidity, minTokenDeltas, pool - ); - tokenDeltas = deltas; - - invariant = tradingFunction( - nextReserves, - pool.totalLiquidity - deltaLiquidity, - getPoolParams(poolId) - ); - - valid = invariant >= 0; - } - - /// @inheritdoc IStrategy - function validateSwap( - address, - uint256 poolId, - Pool memory pool, - bytes memory data - ) - external - view - virtual - returns ( - bool valid, - int256 invariant, - uint256 tokenInIndex, - uint256 tokenOutIndex, - uint256 amountIn, - uint256 amountOut, - uint256 deltaLiquidity - ) - { - bytes memory params = getPoolParams(poolId); - - (tokenInIndex, tokenOutIndex, amountIn, amountOut) = - abi.decode(data, (uint256, uint256, uint256, uint256)); - - deltaLiquidity = _computeSwapDeltaLiquidity( - pool, params, tokenInIndex, tokenOutIndex, amountIn, amountOut - ); - - pool.reserves[tokenInIndex] += amountIn; - pool.reserves[tokenOutIndex] -= amountOut; - - invariant = tradingFunction( - pool.reserves, pool.totalLiquidity + deltaLiquidity, params - ); - - valid = invariant >= 0; - } - - /// @inheritdoc IStrategy - function getPoolParams(uint256 poolId) - public - view - virtual - returns (bytes memory); - - /// @inheritdoc IStrategy - function tradingFunction( - uint256[] memory reserves, - uint256 totalLiquidity, - bytes memory params - ) public view virtual returns (int256); - - /** - * @notice Computes the token deltas and the next reserves for - * an allocation. - * @param deltaLiquidity Amount of liquidity to allocate. - * @param maxDeltas Maximum token deltas to spend (in WAD). - * @param pool Structure containing the pool. - * @return deltas Required token deltas to allocate (in WAD). - * @return nextReserves Reserves after the allocation. - */ - function _computeAllocateDeltasAndReservesGivenDeltaL( - uint256 deltaLiquidity, - uint256[] memory maxDeltas, - Pool memory pool - ) - internal - view - virtual - returns (uint256[] memory deltas, uint256[] memory nextReserves); - - /** - * @notice Computes the token deltas and the next reserves for - * a deallocation. - * @param deltaLiquidity Amount of liquidity to deallocate. - * @param minDeltas Minimum token deltas to receive (in WAD). - * @param pool Structure containing the pool. - * @return deltas Token deltas being deallocated (in WAD). - * @return nextReserves Reserves after the deallocation. - */ - function _computeDeallocateDeltasAndReservesGivenDeltaL( - uint256 deltaLiquidity, - uint256[] memory minDeltas, - Pool memory pool - ) - internal - view - virtual - returns (uint256[] memory deltas, uint256[] memory nextReserves); - - /** - * @dev Computes the deltaLiquidity for a swap operation. - */ - function _computeSwapDeltaLiquidity( - Pool memory pool, - bytes memory params, - uint256 tokenInIndex, - uint256 tokenOutIndex, - uint256 amountIn, - uint256 amountOut - ) internal view virtual returns (uint256); -} diff --git a/src/PairStrategy.sol b/src/Strategy.sol similarity index 54% rename from src/PairStrategy.sol rename to src/Strategy.sol index 70d0cec9..8e8e6644 100644 --- a/src/PairStrategy.sol +++ b/src/Strategy.sol @@ -4,13 +4,19 @@ pragma solidity ^0.8.13; import { IStrategy, Pool } from "src/interfaces/IStrategy.sol"; /** - * @title Pair strategy base contract for DFMM. - * @notice This abstract contract defines the basic behavior of - * a two-token strategy for DFMM. It is meant to be inherited by - * a concrete strategy implementation. + * @dev Thrown when the length of the deltas array is not the + * same as the length of the reserves array. + */ +error InvalidTokenDeltas(); + +/** + * @title Strategy base contract for DFMM. + * @notice This abstract contract defines the basic implementations of + * validating allocate, deallocate, and swap operations. + * It is meant to be inherited by a concrete strategy implementation. * @author Primitive */ -abstract contract PairStrategy is IStrategy { +abstract contract Strategy is IStrategy { /// @inheritdoc IStrategy address public immutable dfmm; @@ -29,7 +35,7 @@ abstract contract PairStrategy is IStrategy { function validateAllocate( address, uint256 poolId, - Pool memory pool, + Pool calldata pool, bytes calldata data ) external @@ -38,33 +44,33 @@ abstract contract PairStrategy is IStrategy { returns ( bool valid, int256 invariant, - uint256[] memory deltas, + uint256[] memory tokenDeltas, uint256 deltaLiquidity ) { - // We use `deltaL` as a temporary variable because - // we cannot assign to `deltaLiquidity` directly. - (uint256 maxDeltaX, uint256 maxDeltaY, uint256 deltaL) = - abi.decode(data, (uint256, uint256, uint256)); - deltaLiquidity = deltaL; - - deltas = _computeAllocateDeltasGivenDeltaL( - deltaLiquidity, pool, getPoolParams(poolId) - ); + uint256 len = pool.reserves.length; + (tokenDeltas, deltaLiquidity) = abi.decode(data, (uint256[], uint256)); + if (tokenDeltas.length != len) { + revert InvalidTokenDeltas(); + } - if (deltas[0] > maxDeltaX) { - revert DeltaError(maxDeltaX, deltas[0]); + uint256[] memory nextReserves = new uint256[](len); + for (uint256 i = 0; i < len; i++) { + nextReserves[i] = pool.reserves[i] + tokenDeltas[i]; } - if (deltas[1] > maxDeltaY) { - revert DeltaError(maxDeltaY, deltas[1]); + // todo: work on this + /* uint256 expectedDeltaL = + _computeDeltaLGivenDeltas(tokenDeltas, pool, getPoolParams(poolId)); + + if (expectedDeltaL < deltaLiquidity) { + revert DeltaError(expectedDeltaL, deltaLiquidity); } - pool.reserves[0] += deltas[0]; - pool.reserves[1] += deltas[1]; + deltaLiquidity = expectedDeltaL; */ invariant = tradingFunction( - pool.reserves, + nextReserves, pool.totalLiquidity + deltaLiquidity, getPoolParams(poolId) ); @@ -76,7 +82,7 @@ abstract contract PairStrategy is IStrategy { function validateDeallocate( address, uint256 poolId, - Pool memory pool, + Pool calldata pool, bytes calldata data ) external @@ -85,31 +91,23 @@ abstract contract PairStrategy is IStrategy { returns ( bool valid, int256 invariant, - uint256[] memory deltas, + uint256[] memory tokenDeltas, uint256 deltaLiquidity ) { - (uint256 minDeltaX, uint256 minDeltaY, uint256 deltaL) = - abi.decode(data, (uint256, uint256, uint256)); - - deltaLiquidity = deltaL; - deltas = _computeDeallocateDeltasGivenDeltaL( - deltaLiquidity, pool, getPoolParams(poolId) - ); - - if (minDeltaX > deltas[0]) { - revert DeltaError(minDeltaX, deltas[0]); + uint256 len = pool.reserves.length; + (tokenDeltas, deltaLiquidity) = abi.decode(data, (uint256[], uint256)); + if (tokenDeltas.length != pool.reserves.length) { + revert InvalidTokenDeltas(); } - if (minDeltaY > deltas[1]) { - revert DeltaError(minDeltaY, deltas[1]); + uint256[] memory nextReserves = new uint256[](len); + for (uint256 i = 0; i < len; i++) { + nextReserves[i] = pool.reserves[i] - tokenDeltas[i]; } - pool.reserves[0] -= deltas[0]; - pool.reserves[1] -= deltas[1]; - invariant = tradingFunction( - pool.reserves, + nextReserves, pool.totalLiquidity - deltaLiquidity, getPoolParams(poolId) ); @@ -170,45 +168,55 @@ abstract contract PairStrategy is IStrategy { bytes memory params ) public view virtual returns (int256); + /** + * @dev Computes the deltaLiquidity for a swap operation. + */ + function _computeSwapDeltaLiquidity( + Pool memory pool, + bytes memory params, + uint256 tokenInIndex, + uint256 tokenOutIndex, + uint256 amountIn, + uint256 amountOut + ) internal view virtual returns (uint256); + + /// todo: We most likely need functions to compute liquidity on allocates given the token deltas, + /// and compute deltas given the liquidity on deallocates. + /// Then we compare these values against the max/min deltas provided by the user to validate the operation. + /// Additionally, we might want to use the computed values if they are beneficial to the user while also passing the invariant. + /// Where I am hesitant about this is that not all computations might be clean, like in the log normal case where we usually use solvers + /// that are doing root finding to find these values to submit for the users. + /// If we do make these, they'd look something like `computeLiquidityGivenDeltas` and `computeDeltasGivenLiquidity`. + /// Which would be implemented by the strategy contracts and used in the `validateAllocate` and `validateDeallocate` functions + /// to compute the actual values to compare against the user provided values. + /** * @dev Computes the deltas to allocate given a liquidity delta. * This function is meant to be implemented by the strategy * inheriting from this contract. - * @param deltaLiquidity Amount of liquidity to allocate. + * @param tokenDeltas Amount of tokens to allocate. * @param pool Structure containing the pool. - * @param data Additional data for the strategy. - * @return deltas Amount of tokens to allocate (expressed in WAD). + * @param params Strategy parameters + * @return deltaLiquidity Amount of liquidity allocated computed from the token deltas. */ - function _computeAllocateDeltasGivenDeltaL( - uint256 deltaLiquidity, + /* function _computeDeltaLGivenDeltas( + uint256[] memory tokenDeltas, Pool memory pool, - bytes memory data - ) internal view virtual returns (uint256[] memory); + bytes memory params + ) internal view virtual returns (uint256); */ /** - * @dev Computes the deltas to deallocate given a liquidity. - * delta. This function is meant to be implemented by the + * @dev Computes the amounts of tokens deallocated given + * `deltaLiquidity`. This function is meant to be implemented by the * strategy inheriting from this contract. * @param deltaLiquidity Amount of liquidity to deallocate. * @param pool Structure containing the pool. * @param data Additional data for the strategy. * @return deltas Amount of tokens to deallocate (expressed in WAD). */ - function _computeDeallocateDeltasGivenDeltaL( + /* function _computeDeltasGivenDeltaL( uint256 deltaLiquidity, Pool memory pool, bytes memory data - ) internal view virtual returns (uint256[] memory); - - /** - * @dev Computes the deltaLiquidity for a swap operation. - */ - function _computeSwapDeltaLiquidity( - Pool memory pool, - bytes memory params, - uint256 tokenInIndex, - uint256 tokenOutIndex, - uint256 amountIn, - uint256 amountOut - ) internal view virtual returns (uint256); + ) internal view virtual returns (uint256[] memory); */ } diff --git a/test/ConstantSum/unit/Allocate.t.sol b/test/ConstantSum/unit/Allocate.t.sol index 3e21e32b..1c14f7b2 100644 --- a/test/ConstantSum/unit/Allocate.t.sol +++ b/test/ConstantSum/unit/Allocate.t.sol @@ -12,10 +12,17 @@ contract ConstantSumAllocateTest is ConstantSumSetUp { uint256 deltaX = 0.1 ether; uint256 deltaY = 0.1 ether; + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = deltaX; + deltas[1] = deltaY; + ConstantSumParams memory params = abi.decode(constantSum.getPoolParams(POOL_ID), (ConstantSumParams)); uint256 deltaL = computeDeltaLiquidity(deltaX, deltaY, params.price); - dfmm.allocate(POOL_ID, abi.encode(deltaX, deltaY, deltaL)); + dfmm.allocate(POOL_ID, abi.encode(deltas, deltaL)); } } diff --git a/test/G3M/unit/Allocate.t.sol b/test/G3M/unit/Allocate.t.sol index c671ebf9..2acf6992 100644 --- a/test/G3M/unit/Allocate.t.sol +++ b/test/G3M/unit/Allocate.t.sol @@ -18,8 +18,12 @@ contract G3MAllocateTest is G3MSetUp { uint256 preLiquidityBalance = liquidityOf(address(this), POOL_ID); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); - (uint256[] memory deltas) = dfmm.allocate(POOL_ID, data); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + (deltas) = dfmm.allocate(POOL_ID, data); (uint256[] memory adjustedReserves, uint256 adjustedLiquidity) = getReservesAndLiquidity(POOL_ID); @@ -46,8 +50,12 @@ contract G3MAllocateTest is G3MSetUp { uint256 preLiquidityBalance = liquidityOf(address(this), POOL_ID); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); - (uint256[] memory deltas) = dfmm.allocate(POOL_ID, data); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + (deltas) = dfmm.allocate(POOL_ID, data); (uint256[] memory adjustedReserves, uint256 adjustedLiquidity) = getReservesAndLiquidity(POOL_ID); @@ -70,33 +78,53 @@ contract G3MAllocateTest is G3MSetUp { (uint256 maxDeltaY, uint256 deltaLiquidity) = solver.allocateGivenDeltaX(POOL_ID, maxDeltaX); - bytes memory data = abi.encode( - maxDeltaX.mulDivUp(101, 100), - maxDeltaY.mulDivUp(101, 100), - deltaLiquidity - ); + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX.mulDivUp(101, 100); + deltas[1] = maxDeltaY.mulDivUp(101, 100); + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); dfmm.allocate(POOL_ID, data); } + /// todo: need to replace this with proper min liquidity minted checks function test_G3M_allocate_RevertsIfMoreThanMaxDeltaX() public init { + skip(); uint256 maxDeltaX = 0.1 ether; (uint256 maxDeltaY, uint256 deltaLiquidity) = solver.allocateGivenDeltaX(POOL_ID, maxDeltaX); - bytes memory data = abi.encode(maxDeltaX - 1, maxDeltaY, deltaLiquidity); + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX - 1; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); vm.expectRevert(); dfmm.allocate(POOL_ID, data); } + /// todo: need to replace this with proper min liquidity minted checks function test_G3M_allocate_RevertsIfMoreThanMaxDeltaY() public init { + skip(); uint256 maxDeltaX = 0.1 ether; (uint256 maxDeltaY, uint256 deltaLiquidity) = solver.allocateGivenDeltaX(POOL_ID, maxDeltaX); + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY - 1; - bytes memory data = abi.encode(maxDeltaX, maxDeltaY - 1, deltaLiquidity); + bytes memory data = abi.encode(deltas, deltaLiquidity); vm.expectRevert(); dfmm.allocate(POOL_ID, data); } @@ -113,12 +141,17 @@ contract G3MAllocateTest is G3MSetUp { uint256 preLiquidityBalance = liquidityOf(address(this), POOL_ID); console2.log(preLiquidityBalance); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + console2.log(maxDeltaX); console2.log(maxDeltaY); console2.log(deltaLiquidity); - (uint256[] memory deltas) = dfmm.allocate(POOL_ID, data); + (deltas) = dfmm.allocate(POOL_ID, data); (uint256[] memory adjustedReserves, uint256 adjustedLiquidity) = getReservesAndLiquidity(POOL_ID); @@ -134,23 +167,31 @@ contract G3MAllocateTest is G3MSetUp { } function test_G3M_allocate_ReceiveAppropriateLpTokens() public init_100 { - (, uint256 initialL) = getReservesAndLiquidity(POOL_ID); - Pool memory pool = dfmm.pools(POOL_ID); - LPToken liquidityToken = LPToken(pool.liquidityToken); + (, uint256 initialL) = getReservesAndLiquidity(POOL_ID); + Pool memory pool = dfmm.pools(POOL_ID); + LPToken liquidityToken = LPToken(pool.liquidityToken); + + uint256 startBalance = liquidityToken.balanceOf(address(this)); - uint256 startBalance = liquidityToken.balanceOf(address(this)); + uint256 dyMax = 100 ether; + (uint256 dxMax, uint256 dL) = solver.allocateGivenDeltaY(POOL_ID, dyMax); - uint256 dyMax = 100 ether; - (uint256 dxMax, uint256 dL) = solver.allocateGivenDeltaY(POOL_ID, dyMax); - bytes memory data = abi.encode(dxMax, dyMax, dL); + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = dxMax; + deltas[1] = dyMax; + + bytes memory data = abi.encode(deltas, dL); + + dfmm.allocate(POOL_ID, data); - dfmm.allocate(POOL_ID, data); + (, uint256 nextL) = getReservesAndLiquidity(POOL_ID); + uint256 endBalance = liquidityToken.balanceOf(address(this)); - (, uint256 nextL) = getReservesAndLiquidity(POOL_ID); - uint256 endBalance = liquidityToken.balanceOf(address(this)); - - // Add 1_000 wei to account for liquidity that was burnt on init - assertEq(startBalance + 1_000, initialL); - assertEq(endBalance + 1_000, nextL); + // Add 1_000 wei to account for liquidity that was burnt on init + assertEq(startBalance + 1000, initialL); + assertEq(endBalance + 1000, nextL); } } diff --git a/test/G3M/unit/Deallocate.t.sol b/test/G3M/unit/Deallocate.t.sol index 4db35934..13e9f0e1 100644 --- a/test/G3M/unit/Deallocate.t.sol +++ b/test/G3M/unit/Deallocate.t.sol @@ -18,7 +18,14 @@ contract G3MDeallocateTest is G3MSetUp { (, uint256 preTotalLiquidity) = getReservesAndLiquidity(POOL_ID); - bytes memory data = abi.encode(minDeltaX, deltaY, deltaLiquidity); + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX; + deltas[1] = deltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.deallocate(POOL_ID, data); (, uint256 postTotalLiquidity) = getReservesAndLiquidity(POOL_ID); @@ -36,7 +43,15 @@ contract G3MDeallocateTest is G3MSetUp { (uint256 deltaY, uint256 deltaLiquidity) = solver.deallocateGivenDeltaX(POOL_ID, minDeltaX); - bytes memory data = abi.encode(minDeltaX, deltaY, deltaLiquidity); + + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX; + deltas[1] = deltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.deallocate(POOL_ID, data); (uint256 postReserveX, uint256 postReserveY,) = @@ -54,7 +69,15 @@ contract G3MDeallocateTest is G3MSetUp { (uint256 deltaY, uint256 deltaLiquidity) = solver.deallocateGivenDeltaX(POOL_ID, minDeltaX); - bytes memory data = abi.encode(minDeltaX, deltaY, deltaLiquidity); + + (uint256[] memory reserves, uint256 liquidity) = + getReservesAndLiquidity(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX; + deltas[1] = deltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.deallocate(POOL_ID, data); assertEq(preBalanceX + minDeltaX, tokenX.balanceOf(address(this))); @@ -71,8 +94,12 @@ contract G3MDeallocateTest is G3MSetUp { (uint256[] memory reserves, uint256 liquidity) = getReservesAndLiquidity(POOL_ID); - bytes memory data = abi.encode(minDeltaX, minDeltaY, deltaLiquidity); - (uint256[] memory deltas) = dfmm.deallocate(POOL_ID, data); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX; + deltas[1] = minDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + (deltas) = dfmm.deallocate(POOL_ID, data); (uint256[] memory adjustedReserves, uint256 adjustedLiquidity) = getReservesAndLiquidity(POOL_ID); diff --git a/test/G3M/unit/G3M.t.sol b/test/G3M/unit/G3M.t.sol index 5c13156a..bc29c89c 100644 --- a/test/G3M/unit/G3M.t.sol +++ b/test/G3M/unit/G3M.t.sol @@ -88,8 +88,12 @@ contract SetUp is Test { uint256 deltaLiquidity = computeLGivenX(maxDeltaX, S, params); uint256 maxDeltaY = computeY(maxDeltaX, S, params); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); - (uint256[] memory deltas) = dfmm.allocate(POOL_ID, data); + uint256[] memory deltas = new uint256[](pool.reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + (deltas) = dfmm.allocate(POOL_ID, data); } function test_G3M2_deallocate() public { @@ -108,9 +112,12 @@ contract SetUp is Test { uint256 deltaLiquidity = computeLGivenX(minDeltaX, S, params); uint256 minDeltaY = computeY(minDeltaX, S, params); - bytes memory data = - abi.encode(minDeltaX - 1, minDeltaY - 1, deltaLiquidity); - (uint256[] memory deltas) = dfmm.deallocate(POOL_ID, data); + uint256[] memory deltas = new uint256[](pool.reserves.length); + deltas[0] = minDeltaX - 1; + deltas[1] = minDeltaY - 1; + + bytes memory data = abi.encode(deltas, deltaLiquidity); + (deltas) = dfmm.deallocate(POOL_ID, data); } function getPoolLiquidityToken(uint256 poolId) diff --git a/test/LogNormal/unit/Allocate.t.sol b/test/LogNormal/unit/Allocate.t.sol index 1cd564d5..ae2b79e7 100644 --- a/test/LogNormal/unit/Allocate.t.sol +++ b/test/LogNormal/unit/Allocate.t.sol @@ -28,7 +28,11 @@ contract LogNormalAllocateTest is LogNormalSetUp { console2.log(preTotalLiquidity); console2.log(preLiquidityBalance); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); (, uint256 postTotalLiquidity) = solver.getReservesAndLiquidity(POOL_ID); @@ -43,6 +47,46 @@ contract LogNormalAllocateTest is LogNormalSetUp { assertEq(deltaTotalLiquidity, deltaLiquidityBalance); } + function test_LogNormal_single_sided_allocate() public init { + uint256 deltaX = 0.1 ether; + + (uint256[] memory reserves, uint256 liquidity) = + solver.getReservesAndLiquidity(POOL_ID); + + uint256 deltaLiquidity = + computeDeltaLGivenDeltaX(deltaX, liquidity, reserves[0]); + uint256 deltaYMax = + computeDeltaYGivenDeltaL(deltaLiquidity, liquidity, reserves[1]); + uint256 price = solver.internalPrice(POOL_ID); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = deltaX + deltaYMax * 1 ether / price + 100_000_000_000_000; // Need to put the 'value' of the other token into the single side. This is a very rough hacky guess. + deltas[1] = 0; + + console2.log( + "Pool reserves before allocation: ", reserves[0], reserves[1] + ); + console2.log("Liquidity before allocation: ", liquidity); + + bytes memory data = abi.encode(deltas, deltaLiquidity); + dfmm.allocate(POOL_ID, data); + + (uint256[] memory postReserves, uint256 postLiquidity) = + solver.getReservesAndLiquidity(POOL_ID); + + console2.log( + "Pool reserves after allocation: ", postReserves[0], postReserves[1] + ); + console2.log("Liquidity after allocation: ", postLiquidity); + + console2.log( + "Deltas", + postReserves[0] - reserves[0], + postReserves[1] - reserves[1], + postLiquidity - liquidity + ); + } + function test_LogNormal_allocate_GivenX() public init { uint256 deltaX = 0.1 ether; @@ -56,7 +100,11 @@ contract LogNormalAllocateTest is LogNormalSetUp { // uint256 preLiquidityBalance = liquidityOf(address(this), POOL_ID); // (,, uint256 preTotalLiquidity) = dfmm.getReservesAndLiquidity(POOL_ID); - bytes memory data = abi.encode(deltaX, deltaYMax, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = deltaX; + deltas[1] = deltaYMax; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); /* @@ -86,7 +134,11 @@ contract LogNormalAllocateTest is LogNormalSetUp { // uint256 preLiquidityBalance = liquidityOf(address(this), POOL_ID); // (,, uint256 preTotalLiquidity) = dfmm.getReservesAndLiquidity(POOL_ID); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); /* @@ -111,7 +163,11 @@ contract LogNormalAllocateTest is LogNormalSetUp { uint256 deltaYMax = computeDeltaYGivenDeltaL(deltaLiquidity, liquidity, reserves[1]); - bytes memory data = abi.encode(deltaX, deltaYMax, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = deltaX; + deltas[1] = deltaYMax; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); uint256 endPrice = solver.internalPrice(POOL_ID); @@ -131,7 +187,11 @@ contract LogNormalAllocateTest is LogNormalSetUp { uint256 maxDeltaX = computeDeltaXGivenDeltaL(deltaLiquidity, liquidity, reserves[0]); - bytes memory data = abi.encode(maxDeltaX, maxDeltaY, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = maxDeltaX; + deltas[1] = maxDeltaY; + + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.allocate(POOL_ID, data); uint256 endPrice = solver.internalPrice(POOL_ID); diff --git a/test/LogNormal/unit/Deallocate.t.sol b/test/LogNormal/unit/Deallocate.t.sol index 19914e12..11c0c70c 100644 --- a/test/LogNormal/unit/Deallocate.t.sol +++ b/test/LogNormal/unit/Deallocate.t.sol @@ -25,8 +25,10 @@ contract LogNormalDeallocateTest is LogNormalSetUp { // TODO: See if we can get a better rounding because the transaction fails // if we don't provide a small slippage toleralance. - bytes memory data = - abi.encode(minDeltaX - 10, minDeltaY - 10, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX - 10; + deltas[1] = minDeltaY - 10; + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.deallocate(POOL_ID, data); /* @@ -55,8 +57,10 @@ contract LogNormalDeallocateTest is LogNormalSetUp { // TODO: See if we can get a better rounding because the transaction fails // if we don't provide a small slippage toleralance. - bytes memory data = - abi.encode(minDeltaX - 10, minDeltaY - 10, deltaLiquidity); + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = minDeltaX - 10; + deltas[1] = minDeltaY - 10; + bytes memory data = abi.encode(deltas, deltaLiquidity); dfmm.deallocate(POOL_ID, data); /* diff --git a/test/NTokenGeometricMean/NTokenGeometricMean.t.sol b/test/NTokenGeometricMean/NTokenGeometricMean.t.sol index 98a33549..dd75d51b 100644 --- a/test/NTokenGeometricMean/NTokenGeometricMean.t.sol +++ b/test/NTokenGeometricMean/NTokenGeometricMean.t.sol @@ -190,6 +190,29 @@ contract NTokenGeometricMeanTest is Test { return deltas; } + /// @dev `forge test --match-test test_4_token_single_sided_allocate -vvv` + function test_4_token_single_sided_allocate() public basic { + // Find the deltas of an equal proportion of 1 ether of each token. Also get the array. + (uint256[] memory amounts, uint256 dLiquidity) = + solver.getAllocationDeltasGivenDeltaT(POOL_ID, 0, ONE); + + console.log(amounts[0], dLiquidity); + + // Reset the arrays so that we only have the first token with a non-zero value. + // And reset it to be about 5x amount of each per token amount computed originally. + for (uint256 i = 0; i < amounts.length; i++) { + if (i != 0) { + amounts[i] = 0; + } else { + amounts[i] = 5 ether; // ~5x the amount per 1 of the other tokens because weight is 0.25. + } + } + + bytes memory data = abi.encode(amounts, dLiquidity); + + dfmm.allocate(POOL_ID, data); + } + function test_4_token_allocate_basic() public basic { uint256 maxTokenDelta = 100e18; uint256[] memory maxDeltas = createTokenDeltas(maxTokenDelta); From b6adfb8e5473889ddb277c37cbecff0444d51a9c Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 10 Apr 2024 09:45:01 -0700 Subject: [PATCH 2/2] add single sided tests --- test/LogNormal/unit/Allocate.t.sol | 96 ++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 6 deletions(-) diff --git a/test/LogNormal/unit/Allocate.t.sol b/test/LogNormal/unit/Allocate.t.sol index ae2b79e7..bb32cee5 100644 --- a/test/LogNormal/unit/Allocate.t.sol +++ b/test/LogNormal/unit/Allocate.t.sol @@ -53,16 +53,30 @@ contract LogNormalAllocateTest is LogNormalSetUp { (uint256[] memory reserves, uint256 liquidity) = solver.getReservesAndLiquidity(POOL_ID); - uint256 deltaLiquidity = - computeDeltaLGivenDeltaX(deltaX, liquidity, reserves[0]); - uint256 deltaYMax = - computeDeltaYGivenDeltaL(deltaLiquidity, liquidity, reserves[1]); - uint256 price = solver.internalPrice(POOL_ID); + console2.log("X", reserves[0]); + console2.log("Y", reserves[1]); + console2.log("L", liquidity); + + uint256 estimatedL = solver.getNextLiquidity( + POOL_ID, reserves[0] + deltaX, reserves[1], liquidity + ); + uint256 deltaLiquidity = estimatedL - liquidity; + + bool swapXForY = true; + + (bool valid, uint256 amountOut,, bytes memory payload) = + solver.simulateSwap(POOL_ID, swapXForY, deltaX); + + console2.log("Amount out: ", amountOut); uint256[] memory deltas = new uint256[](reserves.length); - deltas[0] = deltaX + deltaYMax * 1 ether / price + 100_000_000_000_000; // Need to put the 'value' of the other token into the single side. This is a very rough hacky guess. + deltas[0] = deltaX; deltas[1] = 0; + console2.log("X", reserves[0] + deltas[0]); + console2.log("Y", reserves[1] + deltas[1]); + console2.log("L", liquidity + deltaLiquidity); + console2.log( "Pool reserves before allocation: ", reserves[0], reserves[1] ); @@ -87,6 +101,76 @@ contract LogNormalAllocateTest is LogNormalSetUp { ); } + function logPoolState() public view { + (uint256[] memory reserves, uint256 liquidity) = + solver.getReservesAndLiquidity(POOL_ID); + + console2.log("X", reserves[0]); + console2.log("Y", reserves[1]); + console2.log("L", liquidity); + } + + function test_LogNormal_single_sided_allocate_deallocate() public init { + uint256 deltaX = 0.1 ether; + + (uint256[] memory reserves, uint256 liquidity) = + solver.getReservesAndLiquidity(POOL_ID); + + uint256 estimatedL = solver.getNextLiquidity( + POOL_ID, reserves[0] + deltaX, reserves[1], liquidity + ); + uint256 deltaLiquidity = estimatedL - liquidity; + + bool swapXForY = true; + + (bool valid, uint256 amountOut,, bytes memory payload) = + solver.simulateSwap(POOL_ID, swapXForY, deltaX); + + console2.log("Estimated delta y given delta x: ", amountOut); + + uint256 estimatedLiquidityPayment = computeDeltaLXIn( + deltaX, + reserves[0], + reserves[1], + liquidity, + solver.getPoolParams(POOL_ID) + ); + console2.log("Estimated liquidity payment: ", estimatedLiquidityPayment); + + uint256[] memory deltas = new uint256[](reserves.length); + deltas[0] = deltaX; + deltas[1] = 0; + + logPoolState(); + + console2.log("Allocate x"); + + bytes memory data = abi.encode(deltas, deltaLiquidity); + dfmm.allocate(POOL_ID, data); + + (uint256[] memory postReserves, uint256 postLiquidity) = + solver.getReservesAndLiquidity(POOL_ID); + + logPoolState(); + + deltas[0] = 0; + deltas[1] = amountOut * 1 ether / (1 ether + TEST_SWAP_FEE); // try to get the amount out excluding the swap fee + + estimatedL = solver.getNextLiquidity( + POOL_ID, postReserves[0], postReserves[1] - deltas[1], postLiquidity + ); + deltaLiquidity = postLiquidity - estimatedL; + + console2.log("Deallocate y"); + + data = abi.encode(deltas, deltaLiquidity); + dfmm.deallocate(POOL_ID, data); + + (postReserves, postLiquidity) = solver.getReservesAndLiquidity(POOL_ID); + + logPoolState(); + } + function test_LogNormal_allocate_GivenX() public init { uint256 deltaX = 0.1 ether;