diff --git a/src/ConstantSum/ConstantSum.sol b/src/ConstantSum/ConstantSum.sol index e3a5f0d3..52f431ec 100644 --- a/src/ConstantSum/ConstantSum.sol +++ b/src/ConstantSum/ConstantSum.sol @@ -13,7 +13,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 { @@ -35,7 +35,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. @@ -47,7 +47,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( @@ -219,25 +219,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 d749b62d..94c2b9cc 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 d8a86933..00000000 --- a/src/NTokenStrategy.sol +++ /dev/null @@ -1,224 +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 - ) - { - 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; - } - - function postSwapHook( - address, - uint256, - Pool memory, - bytes memory - ) external { } - - /// @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 55% rename from src/PairStrategy.sol rename to src/Strategy.sol index 18a44dfb..ea9f83eb 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) ); @@ -178,45 +176,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 b677ae7d..4d509670 100644 --- a/test/ConstantSum/unit/Allocate.t.sol +++ b/test/ConstantSum/unit/Allocate.t.sol @@ -12,6 +12,13 @@ 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)); diff --git a/test/G3M/unit/Allocate.t.sol b/test/G3M/unit/Allocate.t.sol index 0a1ef333..4d56c9fe 100644 --- a/test/G3M/unit/Allocate.t.sol +++ b/test/G3M/unit/Allocate.t.sol @@ -56,6 +56,7 @@ contract G3MAllocateTest is G3MSetUp { dfmm.allocate(POOL_ID, data); } + /// todo: need to replace this with proper min liquidity minted checks function test_G3M_allocate_RevertsIfMoreThanMaxDeltaX() public init { uint256[] memory deltas = new uint256[](2); deltas[0] = 1 ether; @@ -70,6 +71,7 @@ contract G3MAllocateTest is G3MSetUp { dfmm.allocate(POOL_ID, data); } + /// todo: need to replace this with proper min liquidity minted checks function test_G3M_allocate_RevertsIfMoreThanMaxDeltaY() public init { uint256[] memory deltas = new uint256[](2); deltas[0] = 1 ether; diff --git a/test/G3M/unit/G3M.t.sol b/test/G3M/unit/G3M.t.sol index b96853e5..cb81254b 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 747e4119..4b813d84 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,130 @@ 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); + + 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; + 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] + ); + 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 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; @@ -56,7 +184,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 +218,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 +247,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.getEstimatedPrice(POOL_ID, 0, 1); @@ -131,7 +271,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.getEstimatedPrice(POOL_ID, 0, 1); 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 c3add932..cc113511 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);