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);