Skip to content
New issue

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

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

Already on GitHub? # to your account

Fix/post review #119

Merged
merged 18 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
e1d1a4d
fix: use token variable instead of array value in DFMM init loop
clemlak Apr 5, 2024
2c700a6
fix: replace MIN_WIDTH by MIN_MEAN in LogNormal
clemlak Apr 5, 2024
13aa509
fix: maxDeltaL check in ConstantSum
clemlak Apr 5, 2024
016b81a
feat: remove computeSwapDeltaLiquidity from ConstantSumSolver simulat…
clemlak Apr 5, 2024
348dbed
feat: remove SimulateSwapState in ConstantSumSolver simulateSwap
clemlak Apr 5, 2024
50d121a
fix: wrap for loop with unchecked to save some gas
clemlak Apr 5, 2024
c086b1b
fix: skip transfer if amount is 0
clemlak Apr 5, 2024
38917c1
fix(DOS): cross pool dos vector removed by refunding eth only if weth…
Alexangelj Apr 5, 2024
624a29a
fix(native-eth): use msg.value to determine branch case, refund exces…
Alexangelj Apr 5, 2024
bd0222f
Merge branch 'fix/post-review' into fix/native-eth
clemlak Apr 7, 2024
54f0e2a
Merge pull request #122 from primitivefinance/fix/native-eth
clemlak Apr 7, 2024
1009b0f
fix: use ONE constant instead of actual value in G3M trading function
clemlak Apr 7, 2024
cc42a4e
fix: change rounding direction in NTokenG3M trading function
clemlak Apr 9, 2024
039093f
fix: change rounding direction in NTokenG3M computeSwapDeltaLiquidity
clemlak Apr 9, 2024
754916c
fix: use mulDiv in NTokenG3M computeDeltaGivenDeltaLR
clemlak Apr 9, 2024
6b5a3f5
fix: computeSwapDeltaLiquidity in ConstantSumMath
clemlak Apr 9, 2024
54d10bb
fix: add rounding direction for ConstantSum computeDeltaLiquidity
clemlak Apr 9, 2024
1e51776
fix: compute delta liquidity in ConstantSum with different rounding d…
clemlak Apr 9, 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
20 changes: 12 additions & 8 deletions src/ConstantSum/ConstantSum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
FixedPointMathLib,
computeTradingFunction,
computeSwapDeltaLiquidity,
computeDeltaLiquidity
computeDeltaLiquidityRoundDown,
computeDeltaLiquidityRoundUp
} from "./ConstantSumMath.sol";
import {
decodePriceUpdate,
Expand Down Expand Up @@ -67,8 +68,9 @@ contract ConstantSum is PairStrategy {
ConstantSumParams memory params;

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

if (pool.reserves.length != 2 || reserves.length != 2) {
revert InvalidReservesLength();
Expand Down Expand Up @@ -106,8 +108,9 @@ contract ConstantSum is PairStrategy {
(uint256 deltaX, uint256 deltaY, uint256 minDeltaL) =
abi.decode(data, (uint256, uint256, uint256));

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

deltas = new uint256[](2);
Expand Down Expand Up @@ -145,9 +148,10 @@ contract ConstantSum is PairStrategy {
(uint256 deltaX, uint256 deltaY, uint256 maxDeltaL) =
abi.decode(data, (uint256, uint256, uint256));

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

deltas = new uint256[](2);
deltas[0] = deltaX;
Expand Down
14 changes: 11 additions & 3 deletions src/ConstantSum/ConstantSumMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,30 @@ function computeInitialPoolData(
return abi.encode(reserves, params);
}

function computeDeltaLiquidity(
function computeDeltaLiquidityRoundUp(
uint256 deltaX,
uint256 deltaY,
uint256 price
) pure returns (uint256) {
return price.mulWadUp(deltaX) + deltaY;
}

function computeDeltaLiquidityRoundDown(
uint256 deltaX,
uint256 deltaY,
uint256 price
) pure returns (uint256) {
return price.mulWadDown(deltaX) + deltaY;
}

function computeSwapDeltaLiquidity(
uint256 delta,
ConstantSumParams memory params,
bool isSwapXForY
) pure returns (uint256) {
if (isSwapXForY) {
return (params.swapFee).mulWadUp(delta);
return params.swapFee.mulWadUp(delta.mulWadUp(params.price));
} else {
return (params.swapFee).mulDivUp(delta, params.price);
return params.swapFee.mulWadUp(delta);
}
}
23 changes: 8 additions & 15 deletions src/ConstantSum/ConstantSumSolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,6 @@ contract ConstantSumSolver {
return computeInitialPoolData(rx, ry, params);
}

struct SimulateSwapState {
uint256 amountOut;
uint256 deltaLiquidity;
}

function simulateSwap(
uint256 poolId,
bool swapXIn,
Expand All @@ -56,39 +51,37 @@ contract ConstantSumSolver {
IStrategy(strategy).getPoolParams(poolId), (ConstantSumParams)
);

SimulateSwapState memory state;
uint256 amountOut;

if (swapXIn) {
computeSwapDeltaLiquidity(amountIn, poolParams, true);
state.amountOut = amountIn.mulWadDown(poolParams.price).mulWadDown(
amountOut = amountIn.mulWadDown(poolParams.price).mulWadDown(
ONE - poolParams.swapFee
);

if (pool.reserves[1] < state.amountOut) {
if (pool.reserves[1] < amountOut) {
revert NotEnoughLiquidity();
}
} else {
computeSwapDeltaLiquidity(amountIn, poolParams, false);
state.amountOut = (ONE - poolParams.swapFee).mulWadDown(amountIn)
amountOut = (ONE - poolParams.swapFee).mulWadDown(amountIn)
.divWadDown(poolParams.price);

if (pool.reserves[0] < state.amountOut) {
if (pool.reserves[0] < amountOut) {
revert NotEnoughLiquidity();
}
}

bytes memory swapData;

if (swapXIn) {
swapData = abi.encode(0, 1, amountIn, state.amountOut);
swapData = abi.encode(0, 1, amountIn, amountOut);
} else {
swapData = abi.encode(1, 0, amountIn, state.amountOut);
swapData = abi.encode(1, 0, amountIn, amountOut);
}

(bool valid,,,,,,) = IStrategy(strategy).validateSwap(
address(this), poolId, pool, swapData
);
return (valid, state.amountOut, swapData);
return (valid, amountOut, swapData);
}

function preparePriceUpdate(uint256 newPrice)
Expand Down
38 changes: 26 additions & 12 deletions src/DFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,17 @@ contract DFMM is IDFMM {

for (uint256 i = 0; i < tokensLength; i++) {
address token = params.tokens[i];
uint256 decimals = ERC20(params.tokens[i]).decimals();
uint256 decimals = ERC20(token).decimals();

if (decimals > 18 || decimals < 6) {
revert InvalidTokenDecimals();
}

for (uint256 j = i + 1; j < tokensLength; j++) {
if (token == params.tokens[j]) {
revert InvalidDuplicateTokens();
unchecked {
for (uint256 j = i + 1; j < tokensLength; j++) {
if (token == params.tokens[j]) {
revert InvalidDuplicateTokens();
}
}
}
}
Expand Down Expand Up @@ -281,9 +283,13 @@ contract DFMM is IDFMM {
// Internals

/**
* @dev Transfers `amounts` of `tokens` from the sender to the contract. Note
* that if any ETH is present in the contract, it will be wrapped to WETH and
* used if sufficient. Any excess of ETH will be sent back to the sender.
* @notice Transfers `amounts` of `tokens` from the sender to the contract.
* @dev Note that for pools with `WETH` as a token, the contract will accept
* full payments in native ether. If the contract receives more ether than
* the amount of `WETH` needed, the contract will refund the remaining ether.
* If the contract receives less ether than the amount of `WETH` needed, the
* contract will refund the native ether and request the full amount of `WETH`
* needed instead.
* @param tokens An array of token addresses to transfer.
* @param amounts An array of amounts to transfer expressed in WAD.
*/
Expand All @@ -301,23 +307,30 @@ contract DFMM is IDFMM {
downscaleUp(amount, computeScalingFactor(token));
uint256 preBalance = ERC20(token).balanceOf(address(this));

if (token == weth && address(this).balance >= amount) {
// note: `msg.value` can be used safely in a loop because `weth` is a unique token,
// therefore we only enter this branch once.
if (token == weth && msg.value >= amount) {
WETH(payable(weth)).deposit{ value: amount }();
} else {
SafeTransferLib.safeTransferFrom(
ERC20(token), msg.sender, address(this), downscaledAmount
);
}

// If not enough native ether was sent as payment
// or too much ether was sent,
// refund all the remaining ether back to the sender.
if (token == weth && msg.value != 0) {
SafeTransferLib.safeTransferETH(
msg.sender, address(this).balance
);
}

uint256 postBalance = ERC20(token).balanceOf(address(this));
if (postBalance < preBalance + downscaledAmount) {
revert InvalidTransfer();
}
}

if (address(this).balance > 0) {
SafeTransferLib.safeTransferETH(msg.sender, address(this).balance);
}
}

/**
Expand All @@ -334,6 +347,7 @@ contract DFMM is IDFMM {
} else {
uint256 downscaledAmount =
downscaleDown(amount, computeScalingFactor(token));
if (downscaledAmount == 0) return;
uint256 preBalance = ERC20(token).balanceOf(address(this));
SafeTransferLib.safeTransfer(ERC20(token), to, downscaledAmount);
uint256 postBalance = ERC20(token).balanceOf(address(this));
Expand Down
2 changes: 1 addition & 1 deletion src/GeometricMean/G3MMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ function computeTradingFunction(
uint256 a = uint256(int256(rX.divWadUp(L)).powWad(int256(params.wX)));
uint256 b = uint256(int256(rY.divWadUp(L)).powWad(int256(params.wY)));

return int256(a.mulWadUp(b)) - int256(1 ether);
return int256(a.mulWadUp(b)) - int256(ONE);
}

function computeDeltaGivenDeltaLRoundUp(
Expand Down
2 changes: 1 addition & 1 deletion src/LogNormal/LogNormal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ contract LogNormal is PairStrategy {
(reserves, totalLiquidity, params) =
abi.decode(data, (uint256[], uint256, LogNormalParams));

if (params.mean < MIN_WIDTH || params.mean > MAX_MEAN) {
if (params.mean < MIN_MEAN || params.mean > MAX_MEAN) {
revert InvalidMean();
}

Expand Down
10 changes: 5 additions & 5 deletions src/NTokenGeometricMean/NTokenGeometricMeanMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function computeTradingFunction(
uint256 accumulator = ONE;
for (uint256 i = 0; i < reserves.length; i++) {
uint256 a = uint256(
int256(reserves[i].divWadDown(L)).powWad(int256(params.weights[i]))
int256(reserves[i].divWadUp(L)).powWad(int256(params.weights[i]))
);
accumulator = accumulator.mulWadUp(a);
}
Expand All @@ -30,15 +30,15 @@ function computeDeltaGivenDeltaLRoundUp(
uint256 deltaLiquidity,
uint256 totalLiquidity
) pure returns (uint256) {
return reserve.mulWadUp(deltaLiquidity.divWadUp(totalLiquidity));
return reserve.mulDivUp(deltaLiquidity, totalLiquidity);
}

function computeDeltaGivenDeltaLRoundDown(
uint256 reserve,
uint256 deltaLiquidity,
uint256 totalLiquidity
) pure returns (uint256) {
return reserve.mulWadDown(deltaLiquidity.divWadDown(totalLiquidity));
return reserve.mulDivDown(deltaLiquidity, totalLiquidity);
}

function computeL(
Expand Down Expand Up @@ -137,7 +137,7 @@ function computeSwapDeltaLiquidity(
uint256 weight,
uint256 swapFee
) pure returns (uint256) {
return weight.mulWadDown(swapFee).mulWadDown(totalLiquidity).mulWadDown(
amountIn.divWadDown(reserve)
return weight.mulWadUp(swapFee).mulWadUp(totalLiquidity).mulWadUp(
amountIn.divWadUp(reserve)
);
}
5 changes: 3 additions & 2 deletions test/ConstantSum/unit/Allocate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.13;

import { ConstantSumSetUp } from "./SetUp.sol";
import {
computeDeltaLiquidity,
computeDeltaLiquidityRoundDown,
ConstantSumParams
} from "src/ConstantSum/ConstantSumMath.sol";

Expand All @@ -15,7 +15,8 @@ contract ConstantSumAllocateTest is ConstantSumSetUp {
ConstantSumParams memory params =
abi.decode(constantSum.getPoolParams(POOL_ID), (ConstantSumParams));

uint256 deltaL = computeDeltaLiquidity(deltaX, deltaY, params.price);
uint256 deltaL =
computeDeltaLiquidityRoundDown(deltaX, deltaY, params.price);
dfmm.allocate(POOL_ID, abi.encode(deltaX, deltaY, deltaL));
}
}
3 changes: 3 additions & 0 deletions test/DFMM/unit/Init.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ contract DFMMInit is DFMMSetUp, Script {
bytes defaultData =
abi.encode(valid, initialInvariant, defaultReserves, initialLiquidity);

/// @notice for handling ether refunds
receive() external payable { }

function test_DFMM_init_StoresStrategyInitialReservesAndLiquidity()
public
{
Expand Down
Loading