Skip to content

Commit

Permalink
Merge branch 'main' into root-license
Browse files Browse the repository at this point in the history
  • Loading branch information
0xJepsen authored Apr 23, 2024
2 parents 38554c6 + 4e207a7 commit 722f88d
Show file tree
Hide file tree
Showing 58 changed files with 1,947 additions and 983 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/solidity.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ jobs:
uses: foundry-rs/foundry-toolchain@v1

- name: run tests
run: forge test --via-ir -vvv
run: forge test -vvv
env:
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}

- name: run snapshot
run: forge snapshot --via-ir
run: forge snapshot
env:
MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }}
145 changes: 134 additions & 11 deletions src/ConstantSum/ConstantSum.sol
Original file line number Diff line number Diff line change
@@ -1,9 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.13;

import "./ConstantSumMath.sol";
import "./ConstantSumUtils.sol";
pragma solidity 0.8.22;

import {
FixedPointMathLib,
computeTradingFunction,
computeSwapDeltaLiquidity,
computeDeltaLiquidityRoundDown,
computeDeltaLiquidityRoundUp
} from "./ConstantSumMath.sol";
import {
decodePriceUpdate,
decodeFeeUpdate,
decodeControllerUpdate
} from "./ConstantSumUtils.sol";
import { PairStrategy, IStrategy, Pool } from "src/PairStrategy.sol";
import { EPSILON } from "src/lib/StrategyLib.sol";

struct InternalParams {
uint256 price;
Expand All @@ -27,17 +38,22 @@ enum UpdateCode {
contract ConstantSum is PairStrategy {
using FixedPointMathLib for uint256;

/// @notice Thrown when the expected liquidity is not met.
error InvalidDeltaLiquidity();

/// @inheritdoc IStrategy
string public constant name = "ConstantSum";

mapping(uint256 => InternalParams) public internalParams;

/// @param dfmm_ Address of the DFMM contract.
constructor(address dfmm_) PairStrategy(dfmm_) { }

/// @inheritdoc IStrategy
function init(
address,
uint256 poolId,
Pool calldata,
Pool calldata pool,
bytes calldata data
)
public
Expand All @@ -50,21 +66,109 @@ contract ConstantSum is PairStrategy {
)
{
ConstantSumParams memory params;
(reserves, totalLiquidity, params) =
abi.decode(data, (uint256[], uint256, ConstantSumParams));

(reserves, params) = abi.decode(data, (uint256[], ConstantSumParams));
totalLiquidity = computeDeltaLiquidityRoundDown(
reserves[0], reserves[1], params.price
);

if (pool.reserves.length != 2 || reserves.length != 2) {
revert InvalidReservesLength();
}

internalParams[poolId].price = params.price;
internalParams[poolId].swapFee = params.swapFee;
internalParams[poolId].controller = params.controller;

// Get the trading function and check this is valid
invariant =
tradingFunction(reserves, totalLiquidity, abi.encode(params));

valid = -EPSILON < invariant && invariant < EPSILON;
valid = invariant >= 0 && invariant <= EPSILON;

return (valid, invariant, reserves, totalLiquidity);
}

function validateAllocate(
address,
uint256 poolId,
Pool memory pool,
bytes calldata data
)
external
view
override
returns (
bool valid,
int256 invariant,
uint256[] memory deltas,
uint256 deltaLiquidity
)
{
(uint256 deltaX, uint256 deltaY, uint256 minDeltaL) =
abi.decode(data, (uint256, uint256, uint256));

deltaLiquidity = computeDeltaLiquidityRoundDown(
deltaX, deltaY, internalParams[poolId].price
);
if (deltaLiquidity < minDeltaL) revert InvalidDeltaLiquidity();

deltas = new uint256[](2);
deltas[0] = deltaX;
deltas[1] = deltaY;

pool.reserves[0] += deltaX;
pool.reserves[1] += deltaY;

invariant = tradingFunction(
pool.reserves,
pool.totalLiquidity + deltaLiquidity,
getPoolParams(poolId)
);

valid = invariant >= 0;
}

function validateDeallocate(
address,
uint256 poolId,
Pool memory pool,
bytes calldata data
)
external
view
override
returns (
bool valid,
int256 invariant,
uint256[] memory deltas,
uint256 deltaLiquidity
)
{
(uint256 deltaX, uint256 deltaY, uint256 maxDeltaL) =
abi.decode(data, (uint256, uint256, uint256));

deltaLiquidity = computeDeltaLiquidityRoundUp(
deltaX, deltaY, internalParams[poolId].price
);
if (deltaLiquidity > maxDeltaL) revert InvalidDeltaLiquidity();

deltas = new uint256[](2);
deltas[0] = deltaX;
deltas[1] = deltaY;

pool.reserves[0] -= deltaX;
pool.reserves[1] -= deltaY;

invariant = tradingFunction(
pool.reserves,
pool.totalLiquidity - deltaLiquidity,
getPoolParams(poolId)
);

valid = invariant >= 0;
}

/// @inheritdoc IStrategy
function update(
address sender,
Expand All @@ -76,7 +180,7 @@ contract ConstantSum is PairStrategy {
UpdateCode updateCode = abi.decode(data, (UpdateCode));

if (updateCode == UpdateCode.Price) {
(internalParams[poolId].price,) = decodePriceUpdate(data);
(internalParams[poolId].price) = decodePriceUpdate(data);
} else if (updateCode == UpdateCode.SwapFee) {
internalParams[poolId].swapFee = decodeFeeUpdate(data);
} else if (updateCode == UpdateCode.Controller) {
Expand All @@ -86,6 +190,7 @@ contract ConstantSum is PairStrategy {
}
}

/// @inheritdoc IStrategy
function getPoolParams(uint256 poolId)
public
view
Expand All @@ -96,10 +201,12 @@ contract ConstantSum is PairStrategy {

params.price = internalParams[poolId].price;
params.swapFee = internalParams[poolId].swapFee;
params.controller = internalParams[poolId].controller;

return abi.encode(params);
}

/// @inheritdoc IStrategy
function tradingFunction(
uint256[] memory reserves,
uint256 totalLiquidity,
Expand All @@ -112,19 +219,35 @@ contract ConstantSum is PairStrategy {
);
}

/// @inheritdoc PairStrategy
function _computeAllocateDeltasGivenDeltaL(
uint256,
Pool memory,
bytes memory
) internal pure override returns (uint256[] memory) {
return new uint256[](0);
return new uint256[](2);
}

/// @inheritdoc PairStrategy
function _computeDeallocateDeltasGivenDeltaL(
uint256,
Pool memory,
bytes memory
) internal pure override returns (uint256[] memory) {
return new uint256[](0);
return new uint256[](2);
}

/// @inheritdoc PairStrategy
function _computeSwapDeltaLiquidity(
Pool memory,
bytes memory params,
uint256 tokenInIndex,
uint256,
uint256 amountIn,
uint256
) internal pure override returns (uint256) {
return computeSwapDeltaLiquidity(
amountIn, abi.decode(params, (ConstantSumParams)), tokenInIndex == 0
);
}
}
85 changes: 22 additions & 63 deletions src/ConstantSum/ConstantSumMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@ pragma solidity ^0.8.13;

import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";
import { ConstantSumParams } from "src/ConstantSum/ConstantSum.sol";

uint256 constant ONE = 1 ether;
import { ONE } from "src/lib/StrategyLib.sol";

using FixedPointMathLib for uint256;
using FixedPointMathLib for int256;

function computeTradingFunction(
uint256[] memory reserves,
uint256 totalLiquidity,
uint256 price
) pure returns (int256) {
return int256(reserves[0].divWadUp(totalLiquidity))
+ int256(reserves[1].divWadUp(totalLiquidity.mulWadUp(price))) - int256(ONE);
return int256(
price.mulWadUp(reserves[0].divWadUp(totalLiquidity))
+ reserves[1].divWadUp(totalLiquidity)
) - int256(ONE);
}

function computeInitialPoolData(
Expand All @@ -25,77 +25,36 @@ function computeInitialPoolData(
) pure returns (bytes memory) {
// The pool can be initialized with any non-negative amount of rx, and ry.
// so we have to allow a user to pass an amount of both even if one is zero.
uint256 L = rx + ry.divWadUp(params.price);
uint256[] memory reserves = new uint256[](2);
reserves[0] = rx;
reserves[1] = ry;
return abi.encode(reserves, L, params);
return abi.encode(reserves, params);
}

function computeDeallocateGivenDeltaX(
function computeDeltaLiquidityRoundUp(
uint256 deltaX,
uint256 rX,
uint256 rY,
uint256 totalLiquidity
) pure returns (uint256 deltaY, uint256 deltaL) {
uint256 a = deltaX.divWadDown(rX);
if (rY > 0) {
deltaY = a.mulWadDown(rY);
}
deltaL = a.mulWadDown(totalLiquidity);
}

function computeDeallocateGivenDeltaY(
uint256 deltaY,
uint256 rX,
uint256 rY,
uint256 totalLiquidity
) pure returns (uint256 deltaX, uint256 deltaL) {
uint256 a = deltaY.divWadDown(rY);
if (rX > 0) {
deltaY = a.mulWadDown(rX);
}
deltaL = a.mulWadDown(totalLiquidity);
uint256 price
) pure returns (uint256) {
return price.mulWadUp(deltaX) + deltaY;
}

function computeAllocateGivenDeltaX(
function computeDeltaLiquidityRoundDown(
uint256 deltaX,
uint256 rX,
uint256 rY,
uint256 totalLiquidity
) pure returns (uint256 deltaY, uint256 deltaL) {
uint256 a = deltaX.divWadUp(rX);
if (rY > 0) {
deltaY = a.mulWadUp(rY);
}
deltaL = a.mulWadUp(totalLiquidity);
}

function computeAllocateGivenDeltaY(
uint256 deltaY,
uint256 rX,
uint256 rY,
uint256 totalLiquidity
) pure returns (uint256 deltaX, uint256 deltaL) {
uint256 a = deltaY.divWadUp(rY);
if (rX > 0) {
deltaX = a.mulWadUp(rX);
}
deltaL = a.mulWadUp(totalLiquidity);
}

function computeDeltaGivenDeltaLRoundUp(
uint256 reserve,
uint256 deltaLiquidity,
uint256 totalLiquidity
uint256 price
) pure returns (uint256) {
return reserve.mulWadUp(deltaLiquidity.divWadUp(totalLiquidity));
return price.mulWadDown(deltaX) + deltaY;
}

function computeDeltaGivenDeltaLRoundDown(
uint256 reserve,
uint256 deltaLiquidity,
uint256 totalLiquidity
function computeSwapDeltaLiquidity(
uint256 delta,
ConstantSumParams memory params,
bool isSwapXForY
) pure returns (uint256) {
return reserve.mulWadDown(deltaLiquidity.divWadDown(totalLiquidity));
if (isSwapXForY) {
return params.swapFee.mulWadUp(delta.mulWadUp(params.price));
} else {
return params.swapFee.mulWadUp(delta);
}
}
Loading

0 comments on commit 722f88d

Please sign in to comment.