Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/delta params #19

Merged
merged 79 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
2c44aef
fix: stack too deep issue in GeometricMean strategy
clemlak Feb 22, 2024
5229d6b
feat: revamp G3MExtendedLib to solve stack too deep issues
clemlak Feb 22, 2024
c353233
feat: remove deltaL from allocate and deallocate
clemlak Feb 23, 2024
73a307f
chore: add terminology to README
clemlak Feb 23, 2024
20aaf26
feat: allocate and deallocate are using delta params
clemlak Feb 23, 2024
a62ec59
feat: add extra functions to compute deltas
clemlak Feb 23, 2024
ee29260
feat: allocate / deallocate validation funcs are now using deltaLiqui…
clemlak Feb 23, 2024
349ff7e
test: update MockStrategy to new IStrategy interface
clemlak Feb 23, 2024
63ceb04
feat: IStrategy now passes pool struct, splits liquidity validation
clemlak Feb 23, 2024
3378309
feat: updates ConstantSum to new IStrategy pattern
clemlak Feb 23, 2024
b96e20f
feat: update ConstantSumSolver
clemlak Feb 23, 2024
3fde14c
feat: updates LogNormal to new IStrategy pattern
clemlak Feb 23, 2024
d6739bf
feat: solve stack too deep issues
clemlak Feb 23, 2024
2431874
feat: update LogNormalSolver
clemlak Feb 23, 2024
e160d83
test: update ConstantSumTest
clemlak Feb 23, 2024
c7225b0
test: fix G3M allocate test
clemlak Feb 23, 2024
fbed68c
test: fix G3M init test
clemlak Feb 23, 2024
af27a1b
feat: fix deltaLiquidity in G3M
clemlak Feb 23, 2024
f14a4d2
feat: add allocateGivenDeltaX in G3MSolver
clemlak Feb 23, 2024
5114cc0
feat: rename returned variables in IStrategy
clemlak Feb 23, 2024
fbf5acd
feat: add allocateGivenDeltaY to G3MSolver
clemlak Feb 23, 2024
16b9c1a
test: fix G3M allocate tests
clemlak Feb 23, 2024
b8b034f
feat: fix deallocate in G3M strategy
clemlak Feb 23, 2024
94206c0
build: remove strategies deployment for now
clemlak Feb 23, 2024
8896e8a
test: fix G3M deallocate tests
clemlak Feb 23, 2024
f2e9205
feat: update validateAllocate and validateDeallocate in LogNormal str…
clemlak Feb 23, 2024
a5d0b0b
feat: add allocateGivenDeltaX and DeltaY to LogNormalSolver
clemlak Feb 23, 2024
fb6de61
test: update LogNormal allocate tests
clemlak Feb 23, 2024
2b715d1
test: check multiple allocations in a row in a G3M pool
clemlak Feb 23, 2024
ee8dc25
test: add revert check when maxDeltaX is reached in G3M strategy
clemlak Feb 23, 2024
0d3c003
test: add revert check when maxDeltaY is reached in G3M strategy
clemlak Feb 23, 2024
e15dec7
feat: rename returned variables of MockStrategy
clemlak Feb 23, 2024
85c97b4
feat: add new functions to compute deltas
clemlak Feb 23, 2024
bfd4244
feat: simplify G3M allocate and deallocate calculations
clemlak Feb 23, 2024
e34275e
feat: simplify LogNormal allocate and deallocate calculations
clemlak Feb 23, 2024
f1b309c
feat: small trick to fix stack too deep
clemlak Feb 23, 2024
78a4392
test: simplify allocate LogNormal test
clemlak Feb 23, 2024
e63cea8
feat: fix stack too deep errors
clemlak Feb 23, 2024
103208f
build: no more --via-ir, hell yeah
clemlak Feb 23, 2024
d0c6544
test: fix LogNormal allocate test
clemlak Feb 23, 2024
29b0aa8
feat: add computeDeltaLGivenDeltaY
clemlak Feb 23, 2024
e64e222
test: update deallocate LogNormal tests
clemlak Feb 23, 2024
de67775
chore: use named imports in DFMM contract
clemlak Feb 26, 2024
581403c
chore: use named imports in ScalingLib
clemlak Feb 26, 2024
a9522ce
chore: use named imports in DynamicParamLib
clemlak Feb 26, 2024
b74b536
feat: add InvalidTransfer error to IDFMM
clemlak Feb 26, 2024
3a74921
test: add deallocate scenario to MockStrategy
clemlak Feb 26, 2024
76a7e16
feat: move token balance checks after transfers into dedicated function
clemlak Feb 26, 2024
5d69908
chore: use named import for LPToken in DFMM
clemlak Feb 26, 2024
6d6a431
test: add slippage to deallocate in LogNormal tests
clemlak Feb 26, 2024
7c07c2f
chore: wip contribution guidelines
clemlak Feb 26, 2024
5013430
test: add token X and Y to DFMM setup
clemlak Feb 26, 2024
f69deac
test: update MockStrategy
clemlak Feb 26, 2024
8f5e4f3
test: update deallocate tests in DFMM
clemlak Feb 26, 2024
ea99cb0
test: update init tests for DFMM
clemlak Feb 26, 2024
7349792
test: remove duplicate test tokens
clemlak Feb 26, 2024
67a3cb0
test: update DFMM general setup
clemlak Feb 27, 2024
52a02c0
test: update DFMM setup
clemlak Feb 27, 2024
641fa4d
test: fix some DFMM init tests
clemlak Feb 27, 2024
fe27670
test: fix DFMM init reverts when not valid test
clemlak Feb 27, 2024
ad52810
test: fix DFMM init event check
clemlak Feb 27, 2024
3d8f5d0
test: fix DFMM init reserves check
clemlak Feb 27, 2024
9060465
forge install: forge-std
clemlak Feb 27, 2024
42e2a04
build: remove useless Forge remapping
clemlak Feb 27, 2024
fe2158b
test: skip broken test in ConstantSum tests
clemlak Feb 27, 2024
634bd15
Merge branch 'main' into feat/delta-params
clemlak Feb 27, 2024
8004319
chore: fix conflicts merge error
clemlak Feb 27, 2024
7c62c99
test: add WETH, fix wrong constructor param for DFMM
clemlak Feb 27, 2024
ef054f0
test: add WETH address check in DFMM
clemlak Feb 27, 2024
9672182
test: add skipped test for DFMM allocate
clemlak Feb 27, 2024
79141cf
fix: cleanup arb logic
kinrezC Feb 27, 2024
886a62c
fix optimalArbRaise signature
kinrezC Feb 27, 2024
c27b6bc
fmt
kinrezC Feb 27, 2024
9a98eb9
test: add DFMM receive revert check
clemlak Feb 28, 2024
fb38c0f
test: add DFMM receive ETH from WETH check
clemlak Feb 28, 2024
49fa6be
chore: add missing NatSpec for weth function
clemlak Feb 28, 2024
2f3cdb5
test: change test tokens metadata
clemlak Feb 28, 2024
6d4bc54
test: add LPToken metadata check
clemlak Feb 28, 2024
4480404
test: add DFMM lp tokens mint in init
clemlak Feb 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this for?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, it's not entirely clear to me why we're passing the pool in init, I checked the implementations in all of the strategies and none of them seem to use this argument.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I explained this on Discord or during a standup, there are two main reasons:

  1. Most of the other functions in IStrategy are receiving the pool parameter, so I added it for the sake of conformity
  2. This extends the possibilities of the strategies, for example restricting the pool creation to stable tokens only, etc...

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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are really deltaRx, deltaRy and deltaL right?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like these are still returned to DFMM as next reserves and liquidity

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kinrezC is right, in this case the parameters are still the reserves, I think it's easier because we don't have to think about if the swap is x for y or vice versa.
@Autoparallel I think this is why you were talking about using int256 yesterday? I understand how they could be useful here but I still think they are annoying to deal with.

Anyway, ff we want to use delta we can add a isXForY parameter.


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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait, if we're passing deltas into this method then why are we getting the liquidityDelta here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We're still passing the reserves (see my reply above).

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) =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see. Only the allocations have the deltas. Perhaps we could also do this with swaps?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it would be better to have all the functions expecting the same kind of parameters.

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
Loading