Skip to content

Commit

Permalink
Feat/delta params (#19)
Browse files Browse the repository at this point in the history
* fix: stack too deep issue in GeometricMean strategy

* feat: revamp G3MExtendedLib to solve stack too deep issues

* feat: remove deltaL from allocate and deallocate

* chore: add terminology to README

* feat: allocate and deallocate are using delta params

* feat: add extra functions to compute deltas

* feat: allocate / deallocate validation funcs are now using deltaLiquidity

* test: update MockStrategy to new IStrategy interface

* feat: IStrategy now passes pool struct, splits liquidity validation

* feat: updates ConstantSum to new IStrategy pattern

* feat: update ConstantSumSolver

* feat: updates LogNormal to new IStrategy pattern

* feat: solve stack too deep issues

* feat: update LogNormalSolver

* test: update ConstantSumTest

* test: fix G3M allocate test

* test: fix G3M init test

* feat: fix deltaLiquidity in G3M

* feat: add allocateGivenDeltaX in G3MSolver

* feat: rename returned variables in IStrategy

* feat: add allocateGivenDeltaY to G3MSolver

* test: fix G3M allocate tests

* feat: fix deallocate in G3M strategy

* build: remove strategies deployment for now

* test: fix G3M deallocate tests

* feat: update validateAllocate and validateDeallocate in LogNormal strategy

* feat: add allocateGivenDeltaX and DeltaY to LogNormalSolver

* test: update LogNormal allocate tests

* test: check multiple allocations in a row in a G3M pool

* test: add revert check when maxDeltaX is reached in G3M strategy

* test: add revert check when maxDeltaY is reached in G3M strategy

* feat: rename returned variables of MockStrategy

* feat: add new functions to compute deltas

* feat: simplify G3M allocate and deallocate calculations

* feat: simplify LogNormal allocate and deallocate calculations

* feat: small trick to fix stack too deep

* test: simplify allocate LogNormal test

* feat: fix stack too deep errors

* build: no more --via-ir, hell yeah

* test: fix LogNormal allocate test

* feat: add computeDeltaLGivenDeltaY

* test: update deallocate LogNormal tests

* chore: use named imports in DFMM contract

* chore: use named imports in ScalingLib

* chore: use named imports in DynamicParamLib

* feat: add InvalidTransfer error to IDFMM

* test: add deallocate scenario to MockStrategy

* feat: move token balance checks after transfers into dedicated function

* chore: use named import for LPToken in DFMM

* test: add slippage to deallocate in LogNormal tests

* chore: wip contribution guidelines

* test: add token X and Y to DFMM setup

* test: update MockStrategy

* test: update deallocate tests in DFMM

* test: update init tests for DFMM

* test: remove duplicate test tokens

* test: update DFMM general setup

* test: update DFMM setup

* test: fix some DFMM init tests

* test: fix DFMM init reverts when not valid test

* test: fix DFMM init event check

* test: fix DFMM init reserves check

* forge install: forge-std

v1.7.6

* build: remove useless Forge remapping

* test: skip broken test in ConstantSum tests

* chore: fix conflicts merge error

* test: add WETH, fix wrong constructor param for DFMM

* test: add WETH address check in DFMM

* test: add skipped test for DFMM allocate

* fix: cleanup arb logic

* fix optimalArbRaise signature

* fmt

* test: add DFMM receive revert check

* test: add DFMM receive ETH from WETH check

* chore: add missing NatSpec for weth function

* test: change test tokens metadata

* test: add LPToken metadata check

* test: add DFMM lp tokens mint in init

---------

Co-authored-by: kinrezc <matt.czernik@gmail.com>
  • Loading branch information
clemlak and kinrezC authored Feb 28, 2024
1 parent d28dc9c commit 5a9295a
Show file tree
Hide file tree
Showing 35 changed files with 1,269 additions and 583 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
url = https://github.com/primitivefinance/solstat
branch = "update-forge-std"

[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
# Contribution guidelines

### General

A few rules:
1. Follow our [style guide](#style-guide).
2. Format on save using Foundry formatter.

## Style Guide


### Imports

1. Use named imports as much as possible.
2. Import external dependencies first (libs, etc).

### Tests

1. Name the tests accordingly using the following format:
`test_{name of the contract}_{name of the function}_{what should happen}`

For example:
`test_DFMM_init_IncrementsPoolId`.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,25 @@ The DFMM protocol is currently deployed on the following networks:
| `G3M` | Optimism Sepolia | `0xB5C2c5a4000FB230b289bB54f8b48F4dd8075F3D` |
| `LogNormal` | Optimism Sepolia | `0x6A74a571c638dDDF13ae52F48A37D1019B916520` |

## Terminology

These terms are used to name specific variables throuhout the codebase and the documentation. They are noted here for clarity and are expected to be reused in the future by strategy developers or contributors.

- The prefix `delta` is used to denote the amount of tokens or liquidity to be added or removed from a pool: `deltaX`, `deltaY`, `deltaLiquidity`.
- The prefix `adjusted` is used to denote the amount of tokens or liquidity after the addition or removal of liquidity: `adjustedReserveX`, `adjustedReserveY`, `adjustedLiquidity`.

| Term | Definition |
|---|---|
| `reserveX` | The amount of token X held by a pool |
| `reserveY` | The amount of token Y held by a pool |
| `liquidity` | The amount of liquidity held by a pool |
| `deltaX` | The amount of token X to be added or removed from a pool |
| `deltaY` | The amount of token Y to be added or removed from a pool |
| `deltaLiquidity` | The amount of liquidity to be added or removed from a pool |
| `adjustedReserveX` | The amount of token X after the addition or removal of liquidity |
| `adjustedReserveY` | The amount of token Y after the addition or removal of liquidity |
| `adjustedLiquidity` | The amount of liquidity after the addition or removal of liquidity |

## Contributing

Contributions are welcome! Check out our [guidelines](./CONTRIBUTING.md).
Expand Down
3 changes: 1 addition & 2 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
remappings = [
"solmate/=lib/solstat/lib/solmate/src/",
"solstat/=lib/solstat/src/",
"forge-std/=lib/solstat/lib/forge-std/src/",
]
solc_version = "0.8.22"

# these are defaults. explicitly setting them here for clarity.
libs = ["lib"]
out = "out/"
via-ir = true
# via-ir = true

[fmt]
bracket_spacing = true
Expand Down
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at ae570f
63 changes: 47 additions & 16 deletions src/ConstantSum/ConstantSum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ contract ConstantSum is IStrategy {
function init(
address,
uint256 poolId,
IDFMM.Pool calldata pool,
bytes calldata data
)
public
Expand Down Expand Up @@ -73,6 +74,7 @@ contract ConstantSum is IStrategy {
function validateSwap(
address,
uint256 poolId,
IDFMM.Pool calldata pool,
bytes calldata data
)
external
Expand All @@ -89,27 +91,24 @@ contract ConstantSum is IStrategy {
ConstantSumParams memory params =
abi.decode(getPoolParams(poolId), (ConstantSumParams));

(uint256 startRx, uint256 startRy, uint256 startL) =
IDFMM(dfmm).getReservesAndLiquidity(poolId);

(nextRx, nextRy, nextL) = abi.decode(data, (uint256, uint256, uint256));

uint256 minLiquidityDelta;
uint256 amountIn;
uint256 fees;
if (nextRx > startRy) {
amountIn = nextRx - startRx;
if (nextRx > pool.reserveX) {
amountIn = nextRx - pool.reserveX;
fees = amountIn.mulWadUp(params.swapFee);
minLiquidityDelta += fees;
} else if (nextRy > startRy) {
amountIn = nextRy - startRy;
} else if (nextRy > pool.reserveY) {
amountIn = nextRy - pool.reserveY;
fees = amountIn.mulWadUp(params.swapFee);
minLiquidityDelta += fees.divWadUp(params.price);
} else {
revert("invalid swap: inputs x and y have the same sign!");
}

liquidityDelta = int256(nextL) - int256(startL);
liquidityDelta = int256(nextL) - int256(pool.totalLiquidity);
assert(liquidityDelta >= int256(minLiquidityDelta));

invariant =
Expand All @@ -133,28 +132,59 @@ contract ConstantSum is IStrategy {
}

// This should literally always work lol
function validateAllocateOrDeallocate(
function validateAllocate(
address,
uint256 poolId,
IDFMM.Pool calldata pool,
bytes calldata data
)
external
view
returns (
bool valid,
int256 invariant,
uint256 reserveX,
uint256 reserveY,
uint256 totalLiquidity
uint256 deltaX,
uint256 deltaY,
uint256 deltaLiquidity
)
{
(reserveX, reserveY, totalLiquidity) =
(deltaX, deltaY, deltaLiquidity) =
abi.decode(data, (uint256, uint256, uint256));

invariant = ConstantSumLib.tradingFunction(
reserveX,
reserveY,
totalLiquidity,
pool.reserveX + deltaX,
pool.reserveX + deltaY,
pool.totalLiquidity + deltaLiquidity,
abi.decode(getPoolParams(poolId), (ConstantSumParams)).price
);

valid = -EPSILON < invariant && invariant < EPSILON;
}

// This should literally always work lol
function validateDeallocate(
address,
uint256 poolId,
IDFMM.Pool calldata pool,
bytes calldata data
)
external
view
returns (
bool valid,
int256 invariant,
uint256 deltaX,
uint256 deltaY,
uint256 deltaLiquidity
)
{
(deltaX, deltaY, deltaLiquidity) =
abi.decode(data, (uint256, uint256, uint256));

invariant = ConstantSumLib.tradingFunction(
pool.reserveX - deltaX,
pool.reserveY - deltaY,
pool.totalLiquidity - deltaLiquidity,
abi.decode(getPoolParams(poolId), (ConstantSumParams)).price
);

Expand All @@ -164,6 +194,7 @@ contract ConstantSum is IStrategy {
function update(
address sender,
uint256 poolId,
IDFMM.Pool calldata pool,
bytes calldata data
) external onlyDFMM {
if (sender != internalParams[poolId].controller) revert InvalidSender();
Expand Down
81 changes: 60 additions & 21 deletions src/ConstantSum/ConstantSumSolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,20 @@ contract ConstantSumSolver {
endReserves.rx = startReserves.rx - amountOut;
}

IDFMM.Pool memory pool;
pool.reserveX = startReserves.rx;
pool.reserveY = startReserves.rx;
pool.totalLiquidity = startReserves.L;

bytes memory swapData = abi.encode(endReserves);
(bool valid,,,,,) =
IStrategy(strategy).validateSwap(address(this), poolId, swapData);
(bool valid,,,,,) = IStrategy(strategy).validateSwap(
address(this), poolId, pool, swapData
);
return (valid, amountOut, swapData);
}

function simulateAllocateOrDeallocate(
function simulateAllocate(
uint256 poolId,
bool IsAllocate,
uint256 amountX,
uint256 amountY
) public view returns (bool, bytes memory) {
Expand All @@ -102,28 +107,62 @@ contract ConstantSumSolver {
(ConstantSum.ConstantSumParams)
);

if (IsAllocate) {
endReserves.rx = startReserves.rx + amountX;
endReserves.ry = startReserves.ry + amountY;
endReserves.L =
endReserves.rx + endReserves.ry.divWadUp(poolParams.price);
} else {
if (startReserves.rx < amountX || startReserves.ry < amountY) {
revert NotEnoughLiquidity();
}
endReserves.rx = startReserves.rx - amountX;
endReserves.ry = startReserves.ry - amountY;
endReserves.L =
endReserves.rx + endReserves.ry.divWadUp(poolParams.price);
}
endReserves.rx = startReserves.rx + amountX;
endReserves.ry = startReserves.ry + amountY;
endReserves.L =
endReserves.rx + endReserves.ry.divWadUp(poolParams.price);

IDFMM.Pool memory pool;
pool.reserveX = startReserves.rx;
pool.reserveY = startReserves.ry;
pool.totalLiquidity = startReserves.L;

bytes memory allocateData = abi.encode(endReserves);
(bool valid,,,,) = IStrategy(strategy).validateAllocateOrDeallocate(
address(this), poolId, allocateData
(bool valid,,,,) = IStrategy(strategy).validateAllocate(
address(this), poolId, pool, allocateData
);
return (valid, allocateData);
}
function preparePriceUpdate(uint256 newPrice) public pure returns (bytes memory) {

function simulateDeallocate(
uint256 poolId,
uint256 amountX,
uint256 amountY
) public view returns (bool, bytes memory) {
Reserves memory startReserves;
Reserves memory endReserves;
(startReserves.rx, startReserves.ry, startReserves.L) =
IDFMM(IStrategy(strategy).dfmm()).getReservesAndLiquidity(poolId);
ConstantSum.ConstantSumParams memory poolParams = abi.decode(
IStrategy(strategy).getPoolParams(poolId),
(ConstantSum.ConstantSumParams)
);

if (startReserves.rx < amountX || startReserves.ry < amountY) {
revert NotEnoughLiquidity();
}
endReserves.rx = startReserves.rx - amountX;
endReserves.ry = startReserves.ry - amountY;
endReserves.L =
endReserves.rx + endReserves.ry.divWadUp(poolParams.price);

IDFMM.Pool memory pool;
pool.reserveX = startReserves.rx;
pool.reserveY = startReserves.ry;
pool.totalLiquidity = startReserves.L;

bytes memory deallocateData = abi.encode(endReserves);
(bool valid,,,,) = IStrategy(strategy).validateDeallocate(
address(this), poolId, pool, deallocateData
);
return (valid, deallocateData);
}

function preparePriceUpdate(uint256 newPrice)
public
pure
returns (bytes memory)
{
return ConstantSumLib.encodePriceUpdate(newPrice);
}
}
Loading

0 comments on commit 5a9295a

Please sign in to comment.