Skip to content

Commit

Permalink
Merge branch 'feat/v0.3.0' into feat/bring-back-tau
Browse files Browse the repository at this point in the history
  • Loading branch information
kinrezC authored Apr 10, 2024
2 parents 3ac78d3 + 41e38be commit 6f7d6fa
Show file tree
Hide file tree
Showing 9 changed files with 139 additions and 28 deletions.
46 changes: 37 additions & 9 deletions src/DFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";
import { SafeTransferLib, ERC20 } from "solmate/utils/SafeTransferLib.sol";
import { WETH } from "solmate/tokens/WETH.sol";
import { IStrategy } from "./interfaces/IStrategy.sol";
import { ISwapCallback } from "./interfaces/ISwapCallback.sol";
import {
computeScalingFactor,
downscaleDown,
Expand Down Expand Up @@ -210,6 +211,8 @@ contract DFMM is IDFMM {
int256 invariant;
uint256 tokenInIndex;
uint256 tokenOutIndex;
address tokenIn;
address tokenOut;
uint256 amountIn;
uint256 amountOut;
uint256 deltaLiquidity;
Expand All @@ -219,7 +222,8 @@ contract DFMM is IDFMM {
function swap(
uint256 poolId,
address recipient,
bytes calldata data
bytes calldata data,
bytes calldata callbackData
) external payable lock returns (address, address, uint256, uint256) {
SwapState memory state;

Expand Down Expand Up @@ -252,28 +256,52 @@ contract DFMM is IDFMM {
_pools[poolId].reserves[state.tokenInIndex] += state.amountIn;
_pools[poolId].reserves[state.tokenOutIndex] -= state.amountOut;

address tokenIn = _pools[poolId].tokens[state.tokenInIndex];
address tokenOut = _pools[poolId].tokens[state.tokenOutIndex];
state.tokenIn = _pools[poolId].tokens[state.tokenInIndex];
state.tokenOut = _pools[poolId].tokens[state.tokenOutIndex];

address[] memory tokens = new address[](1);
tokens[0] = tokenIn;
tokens[0] = state.tokenIn;
uint256[] memory amounts = new uint256[](1);
amounts[0] = state.amountIn;
_transferFrom(tokens, amounts);

_transfer(tokenOut, recipient, state.amountOut);
// Optimistically transfer the output tokens to the recipient.
_transfer(state.tokenOut, recipient, state.amountOut);

// If the callbackData is empty, do a regular `_transferFrom()` call, as in the other operations.
if (callbackData.length == 0) {
_transferFrom(tokens, amounts);
} else {
// Otherwise, execute the callback and assert the input amount has been paid
// given the before and after balances of the input token.
uint256 preBalance = ERC20(state.tokenIn).balanceOf(address(this));

ISwapCallback(msg.sender).swapCallback(
state.tokenIn,
state.tokenOut,
state.amountIn,
state.amountOut,
callbackData
);

uint256 downscaledAmount =
downscaleUp(state.amountIn, computeScalingFactor(state.tokenIn));
uint256 postBalance = ERC20(state.tokenIn).balanceOf(address(this));
if (postBalance < preBalance + downscaledAmount) {
revert InvalidTransfer();
}
}

emit Swap(
msg.sender,
poolId,
recipient,
tokenIn,
tokenOut,
state.tokenIn,
state.tokenOut,
state.amountIn,
state.amountOut
);

return (tokenIn, tokenOut, state.amountIn, state.amountOut);
return (state.tokenIn, state.tokenOut, state.amountIn, state.amountOut);
}

/// @inheritdoc IDFMM
Expand Down
4 changes: 3 additions & 1 deletion src/interfaces/IDFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ interface IDFMM {
* @param poolId Id of the pool to swap tokens into.
* @param recipient Address receiving the output tokens.
* @param data An array of bytes used by the strategy contract.
* @param callbackData An array of bytes used in the callback function.
* @return tokenIn Address of the token being sent.
* @return tokenOut Address of the token being received.
* @return amountIn Amount of token sent by the swapper.
Expand All @@ -204,7 +205,8 @@ interface IDFMM {
function swap(
uint256 poolId,
address recipient,
bytes calldata data
bytes calldata data,
bytes calldata callbackData
)
external
payable
Expand Down
20 changes: 20 additions & 0 deletions src/interfaces/ISwapCallback.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity >=0.5.0;

interface ISwapCallback {
/**
* @notice Triggered when swapping tokens in DFMM.
* @param tokenIn Token to swap from.
* @param tokenOut Token to swap to.
* @param amountIn Amount of tokenIn to swap (in WAD units).
* @param amountOut Amount of tokenOut received (in WAD units).
* @param data Calldata passed on swap function call.
*/
function swapCallback(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
bytes calldata data
) external;
}
8 changes: 4 additions & 4 deletions test/ConstantSum/unit/Swap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ contract ConstantSumSwapTest is ConstantSumSetUp {
(,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, isSwapXForY, amountIn);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");

assertEq(tokenX.balanceOf(address(dfmm)), preDfmmBalanceX + inputAmount);
assertEq(
Expand All @@ -39,7 +39,7 @@ contract ConstantSumSwapTest is ConstantSumSetUp {
(,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, isSwapXForY, amountIn);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");

assertEq(tokenX.balanceOf(address(dfmm)), preDfmmBalanceX + inputAmount);
assertEq(
Expand All @@ -62,7 +62,7 @@ contract ConstantSumSwapTest is ConstantSumSetUp {
(,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, isSwapXForY, amountIn);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");

assertEq(
tokenX.balanceOf(address(dfmm)), preDfmmBalanceX - outputAmount
Expand All @@ -85,7 +85,7 @@ contract ConstantSumSwapTest is ConstantSumSetUp {
(,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, isSwapXForY, amountIn);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");

assertEq(
tokenX.balanceOf(address(dfmm)), preDfmmBalanceX - outputAmount
Expand Down
67 changes: 64 additions & 3 deletions test/DFMM/unit/Swap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,72 @@
pragma solidity ^0.8.13;

import { FixedPointMathLib } from "solmate/utils/FixedPointMathLib.sol";
import { LPToken, Pool } from "src/DFMM.sol";
import { LPToken, Pool, ERC20, IDFMM } from "src/DFMM.sol";
import { ISwapCallback } from "src/interfaces/ISwapCallback.sol";
import { computeScalingFactor, downscaleUp } from "src/lib/ScalingLib.sol";
import { DFMMSetUp } from "./SetUp.sol";

contract DFMMSwapTest is DFMMSetUp {
contract DFMMSwapTest is DFMMSetUp, ISwapCallback {
using FixedPointMathLib for uint256;

enum SwapCallbackType {
Valid,
Invalid
}

function swapCallback(
address tokenIn,
address tokenOut,
uint256 amountIn,
uint256 amountOut,
bytes calldata data
) external {
SwapCallbackType swapCallbackType = abi.decode(data, (SwapCallbackType));

if (swapCallbackType == SwapCallbackType.Valid) {
uint256 downscaledAmountIn =
downscaleUp(amountIn, computeScalingFactor(tokenIn));
ERC20(tokenIn).transfer(msg.sender, downscaledAmountIn);
} else if (swapCallbackType == SwapCallbackType.Invalid) {
return;
}
}

function test_DFMM_swap_ForwardsSwapCallbackData() public {
uint256[] memory reserves = new uint256[](2);
reserves[0] = 10 ether;
reserves[1] = 10 ether;

bytes memory params =
abi.encode(true, int256(1 ether), reserves, uint256(10 ether));
(POOL_ID,,) = dfmm.init(getDefaultPoolParams(params));

dfmm.swap(
POOL_ID,
address(this),
abi.encode(true, 1 ether, 0, 1, 1 ether, 1 ether, 1 ether),
abi.encode(SwapCallbackType.Valid)
);
}

function test_DFMM_swap_RevertsIfInvalidTransfer() public {
uint256[] memory reserves = new uint256[](2);
reserves[0] = 10 ether;
reserves[1] = 10 ether;

bytes memory params =
abi.encode(true, int256(1 ether), reserves, uint256(10 ether));
(POOL_ID,,) = dfmm.init(getDefaultPoolParams(params));

vm.expectRevert(IDFMM.InvalidTransfer.selector);
dfmm.swap(
POOL_ID,
address(this),
abi.encode(true, 1 ether, 0, 1, 1 ether, 1 ether, 1 ether),
abi.encode(SwapCallbackType.Invalid)
);
}

function test_DFMM_swap_IncreasesTotalLiquidity() public {
skip();
}
Expand Down Expand Up @@ -52,7 +112,8 @@ contract DFMMSwapTest is DFMMSetUp {
dfmm.swap(
POOL_ID,
address(this),
abi.encode(true, 0, 0, 1, 1 ether, 1 ether, 1 ether)
abi.encode(true, 0, 0, 1, 1 ether, 1 ether, 1 ether),
""
);
assertEq(token.balanceOf(address(this)), preBalance + feesInToken);
}
Expand Down
4 changes: 2 additions & 2 deletions test/G3M/unit/Swap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ contract G3MSwapTest is G3MSetUp {
(, uint256 amountOut, bytes memory payload) =
solver.simulateSwap(POOL_ID, 0, 1, amountIn);
(,, uint256 deltaX, uint256 deltaY) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");
assertEq(amountIn, deltaX);
assertEq(amountOut, deltaY);

Expand All @@ -40,7 +40,7 @@ contract G3MSwapTest is G3MSetUp {
(, uint256 amountOut, bytes memory payload) =
solver.simulateSwap(POOL_ID, 1, 0, amountIn);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");

assertEq(
tokenX.balanceOf(address(dfmm)), preDfmmBalanceX - outputAmount
Expand Down
4 changes: 2 additions & 2 deletions test/LogNormal/LogNormalTest.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ contract LogNormalTest is Test {
(,,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, xIn, amountIn);

dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");
}

function test_ln_swap_y_in() public basic {
Expand All @@ -177,7 +177,7 @@ contract LogNormalTest is Test {
(,,, bytes memory swapData) =
solver.simulateSwap(POOL_ID, xIn, amountIn);

dfmm.swap(POOL_ID, address(this), swapData);
dfmm.swap(POOL_ID, address(this), swapData, "");
}

// todo: write assertApproxEq
Expand Down
10 changes: 5 additions & 5 deletions test/LogNormal/unit/Swap.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ contract LogNormalSwapTest is LogNormalSetUp {
console.log("amountOut:", amountOut);

(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");
assertEq(tokenX.balanceOf(address(dfmm)), preDfmmBalanceX + inputAmount);
assertEq(
tokenY.balanceOf(address(dfmm)), preDfmmBalanceY - outputAmount
Expand All @@ -50,7 +50,7 @@ contract LogNormalSwapTest is LogNormalSetUp {
solver.simulateSwap(POOL_ID, swapXForY, amountIn);
assertEq(valid, true);
(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");

assertEq(tokenY.balanceOf(address(dfmm)), preDfmmBalanceY + inputAmount);
assertEq(
Expand Down Expand Up @@ -97,7 +97,7 @@ contract LogNormalSwapTest is LogNormalSetUp {
abi.encode(1, 0, amountIn, amountOut, deltaLiquidity);

vm.expectRevert();
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");
}

function test_LogNormal_swap_ChargesCorrectFeesYIn() public deep {
Expand All @@ -108,7 +108,7 @@ contract LogNormalSwapTest is LogNormalSetUp {
solver.simulateSwap(POOL_ID, swapXForY, amountIn);

(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");

console2.log(inputAmount);
console2.log(outputAmount);
Expand All @@ -122,7 +122,7 @@ contract LogNormalSwapTest is LogNormalSetUp {
solver.simulateSwap(POOL_ID, swapXForY, amountIn);

(,, uint256 inputAmount, uint256 outputAmount) =
dfmm.swap(POOL_ID, address(this), payload);
dfmm.swap(POOL_ID, address(this), payload, "");

console2.log(inputAmount);
console2.log(outputAmount);
Expand Down
4 changes: 2 additions & 2 deletions test/NTokenGeometricMean/NTokenGeometricMean.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ contract NTokenGeometricMeanTest is Test {
"price tIn", computePrice(tokenInIndex, preReserves, params)
);

dfmm.swap(POOL_ID, address(this), data);
dfmm.swap(POOL_ID, address(this), data, "");
(uint256[] memory postReserves,) =
solver.getReservesAndLiquidity(POOL_ID);
console2.log(
Expand Down Expand Up @@ -376,7 +376,7 @@ contract NTokenGeometricMeanTest is Test {
(bool valid, uint256 amountOut, bytes memory data) =
solver.simulateSwap(POOL_ID, tokenInIndex, tokenOutIndex, amountIn);

dfmm.swap(POOL_ID, address(this), data);
dfmm.swap(POOL_ID, address(this), data, "");
}

function test_4_token_compute_price_non_uniform()
Expand Down

0 comments on commit 6f7d6fa

Please sign in to comment.