Skip to content

Commit

Permalink
Merge branch 'fix/spearbit-audit' into fix/g3m-init-struct
Browse files Browse the repository at this point in the history
  • Loading branch information
clemlak authored Mar 25, 2024
2 parents 6154ecd + 7033738 commit df79f5a
Show file tree
Hide file tree
Showing 21 changed files with 511 additions and 386 deletions.
97 changes: 93 additions & 4 deletions src/ConstantSum/ConstantSum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
pragma solidity 0.8.22;

import {
FixedPointMathLib, computeTradingFunction
FixedPointMathLib,
computeTradingFunction,
computeDeltaLiquidity
} from "./ConstantSumMath.sol";
import {
decodePriceUpdate,
Expand Down Expand Up @@ -33,6 +35,9 @@ 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";

Expand All @@ -45,7 +50,7 @@ contract ConstantSum is PairStrategy {
function init(
address,
uint256 poolId,
Pool calldata,
Pool calldata pool,
bytes calldata data
)
public
Expand All @@ -61,8 +66,13 @@ contract ConstantSum is PairStrategy {
(reserves, totalLiquidity, params) =
abi.decode(data, (uint256[], uint256, ConstantSumParams));

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 =
Expand All @@ -73,6 +83,84 @@ contract ConstantSum is PairStrategy {
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 =
computeDeltaLiquidity(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 =
computeDeltaLiquidity(deltaX, deltaY, internalParams[poolId].price);
if (maxDeltaL > deltaLiquidity) 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 Down Expand Up @@ -105,6 +193,7 @@ contract ConstantSum is PairStrategy {

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

return abi.encode(params);
}
Expand All @@ -128,7 +217,7 @@ contract ConstantSum is PairStrategy {
Pool memory,
bytes memory
) internal pure override returns (uint256[] memory) {
return new uint256[](0);
return new uint256[](2);
}

/// @inheritdoc PairStrategy
Expand All @@ -137,6 +226,6 @@ contract ConstantSum is PairStrategy {
Pool memory,
bytes memory
) internal pure override returns (uint256[] memory) {
return new uint256[](0);
return new uint256[](2);
}
}
74 changes: 8 additions & 66 deletions src/ConstantSum/ConstantSumMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ function computeTradingFunction(
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 @@ -24,77 +26,17 @@ 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 L = rx.mulWadDown(params.price) + ry;
uint256[] memory reserves = new uint256[](2);
reserves[0] = rx;
reserves[1] = ry;
return abi.encode(reserves, L, params);
}

function computeDeallocateGivenDeltaX(
function computeDeltaLiquidity(
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) {
deltaX = a.mulWadDown(rX);
}
deltaL = a.mulWadDown(totalLiquidity);
}

function computeAllocateGivenDeltaX(
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
) pure returns (uint256) {
return reserve.mulWadUp(deltaLiquidity.divWadUp(totalLiquidity));
}

function computeDeltaGivenDeltaLRoundDown(
uint256 reserve,
uint256 deltaLiquidity,
uint256 totalLiquidity
uint256 price
) pure returns (uint256) {
return reserve.mulWadDown(deltaLiquidity.divWadDown(totalLiquidity));
return price.mulWadUp(deltaX) + deltaY;
}
12 changes: 7 additions & 5 deletions src/DFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ contract DFMM is IDFMM {
msg.sender, _pools.length, pool, params.data
);

if (reserves.length != params.tokens.length) revert InvalidReserves();

if (!valid) revert InvalidInvariant(invariant);

liquidityToken.initialize(params.name, params.symbol);
Expand All @@ -109,8 +111,8 @@ contract DFMM is IDFMM {
for (uint256 i = 0; i < tokensLength; i++) {
address token = params.tokens[i];

for (uint256 j = 0; j < tokensLength; j++) {
if (i != j && token == params.tokens[j]) {
for (uint256 j = i + 1; j < tokensLength; j++) {
if (token == params.tokens[j]) {
revert InvalidDuplicateTokens();
}
}
Expand All @@ -136,7 +138,7 @@ contract DFMM is IDFMM {
pool.totalLiquidity
);

return (poolId, reserves, totalLiquidity - BURNT_LIQUIDITY);
return (poolId, reserves, totalLiquidity);
}

/// @inheritdoc IDFMM
Expand All @@ -161,8 +163,8 @@ contract DFMM is IDFMM {
_pools[poolId].reserves[i] += deltas[i];
}

_pools[poolId].totalLiquidity += deltaLiquidity;
_manageTokens(msg.sender, poolId, true, deltaLiquidity);
_pools[poolId].totalLiquidity += deltaLiquidity;

for (uint256 i = 0; i < length; i++) {
_transferFrom(_pools[poolId].tokens[i], deltas[i]);
Expand Down Expand Up @@ -362,7 +364,7 @@ contract DFMM is IDFMM {
* @dev Deploys and returns the address of a clone contract that mimics
* the behaviour of the contract deployed at the address `implementation`.
* This function uses the `CREATE` opcode, which should never revert.
* This function was taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/proxy/Clones.sol#L23.
* This function was taken from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/7bd2b2aaf68c21277097166a9a51eb72ae239b34/contracts/proxy/Clones.sol#L23-L41.
*/
function clone(address implementation)
internal
Expand Down
7 changes: 5 additions & 2 deletions src/GeometricMean/G3MMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ function computeInitialPoolData(

L = computeNextLiquidity(amountX, rY, invariant, L, params);

return
abi.encode(amountX, rY, L, params.wX, params.swapFee, params.controller);
uint256[] memory reserves = new uint256[](2);
reserves[0] = amountX;
reserves[1] = rY;

return abi.encode(reserves, L, params.wX, params.swapFee, params.controller);
}
15 changes: 7 additions & 8 deletions src/GeometricMean/GeometricMean.sol
Original file line number Diff line number Diff line change
Expand Up @@ -72,23 +72,22 @@ contract GeometricMean is PairStrategy {
function init(
address,
uint256 poolId,
Pool calldata,
Pool calldata pool,
bytes calldata data
) external onlyDFMM returns (bool, int256, uint256[] memory, uint256) {
InitState memory state;

state.reserves = new uint256[](2);

(
state.reserves[0],
state.reserves[1],
state.reserves,
state.totalLiquidity,
state.wX,
state.swapFee,
state.controller
) = abi.decode(
data, (uint256, uint256, uint256, uint256, uint256, address)
);
) = abi.decode(data, (uint256[], uint256, uint256, uint256, address));

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

if (state.wX >= ONE) {
revert InvalidWeightX();
Expand Down
6 changes: 5 additions & 1 deletion src/LogNormal/LogNormal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract LogNormal is PairStrategy {
function init(
address,
uint256 poolId,
Pool calldata,
Pool calldata pool,
bytes calldata data
)
public
Expand All @@ -74,6 +74,10 @@ contract LogNormal is PairStrategy {
(reserves, totalLiquidity, params) =
abi.decode(data, (uint256[], uint256, LogNormalParams));

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

internalParams[poolId].mean.lastComputedValue = params.mean;
internalParams[poolId].width.lastComputedValue = params.width;
internalParams[poolId].swapFee = params.swapFee;
Expand Down
2 changes: 1 addition & 1 deletion src/PairStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ abstract contract PairStrategy is IStrategy {
) internal view virtual returns (uint256[] memory);

/**
* @dev Computes the deltas to de de allocate given a liquidity.
* @dev Computes the deltas to deallocate given a liquidity.
* delta. This function is meant to be implemented by the
* strategy inheriting from this contract.
* @param deltaLiquidity Amount of liquidity to deallocate.
Expand Down
7 changes: 7 additions & 0 deletions src/interfaces/IDFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pragma solidity ^0.8.13;
* @param reserves Array of token reserves in the pool in WAD.
* @param totalLiquidity Total liquidity in the pool.
* @param liquidityToken Address of the LP token contract.
* @param feeCollector Address receiving controller fees.
* @param controllerFee Fees charged on the swap fee (in WAD).
*/
struct Pool {
address strategy;
Expand All @@ -26,6 +28,8 @@ struct Pool {
* @param strategy Address of the associated strategy contract.
* @param tokens Array of token addresses in the pool.
* @param data An array of bytes used by the strategy contract.
* @param feeCollector Address receiving controller fees.
* @param controllerFee Fees charged on the swap fee (in WAD).
*/
struct InitParams {
string name;
Expand Down Expand Up @@ -56,6 +60,9 @@ interface IDFMM {
/// @dev Thrown when pool tokens are identical.
error InvalidDuplicateTokens();

/// @dev Thrown when a strategy is returning an invalid amount of reserves.
error InvalidReserves();

/// @dev Thrown when a pool is being initalized with less than two tokens.
error InvalidMinimumTokens();

Expand Down
Loading

0 comments on commit df79f5a

Please sign in to comment.