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/add remove liquidity #269

Merged
merged 5 commits into from
Jan 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
104 changes: 87 additions & 17 deletions src/DFMM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import "solmate/tokens/ERC20.sol";
/// @title DFMM
/// @notice Dynamic Function Market Maker
contract DFMM is ICore {
using FixedPointMathLib for uint256;
using FixedPointMathLib for int256;

address public strategy;
bool public inited;
uint256 public locked = 1;
Expand All @@ -16,7 +19,11 @@ contract DFMM is ICore {
uint256 public reserveXWad;
uint256 public reserveYWad;
uint256 public totalLiquidity;

uint256 public feeGrowth;

mapping(address account => uint256 balance) public balanceOf;
mapping(address account => uint256 balance) public feeGrowthLast;

event LogPoolStats(
uint256 rx,
Expand Down Expand Up @@ -86,6 +93,67 @@ contract DFMM is ICore {
return (reserveXWad, reserveYWad, totalLiquidity);
}

function allocate(bytes calldata data)
public
lock
returns (uint256, uint256, uint256)
{
(bool valid, int256 invariant, uint256 rx, uint256 ry, uint256 L) =
IStrategy(strategy).validateAllocationOrDeallocation(data);
if (!valid) {
revert Invalid(invariant < 0, abs(invariant));
}
if (
balanceOf[msg.sender] != 0 && feeGrowth != feeGrowthLast[msg.sender]
) {
uint256 growth = feeGrowth.mulWadDown(feeGrowthLast[msg.sender]);
balanceOf[msg.sender] = balanceOf[msg.sender].mulWadDown(growth);
}
uint256 deltaX = rx - reserveXWad;
uint256 deltaY = ry - reserveYWad;
uint256 deltaL = L - totalLiquidity;
reserveXWad = rx;
reserveYWad = ry;
totalLiquidity = L;
balanceOf[msg.sender] += deltaL;
feeGrowthLast[msg.sender] = feeGrowth;
ERC20(tokenX).transferFrom(msg.sender, address(this), deltaX);
ERC20(tokenY).transferFrom(msg.sender, address(this), deltaY);
return (reserveXWad, reserveYWad, totalLiquidity);
}

function deallocate(bytes calldata data)
public
lock
returns (uint256, uint256, uint256)
{
(bool valid, int256 invariant, uint256 rx, uint256 ry, uint256 L) =
IStrategy(strategy).validateAllocationOrDeallocation(data);
if (!valid) {
revert Invalid(invariant < 0, abs(invariant));
}

if (
balanceOf[msg.sender] != 0 && feeGrowth != feeGrowthLast[msg.sender]
) {
uint256 growth = feeGrowth.mulWadDown(feeGrowthLast[msg.sender]);
balanceOf[msg.sender] = balanceOf[msg.sender].mulWadDown(growth);
console2.log("in here");
}

uint256 deltaX = reserveXWad - rx;
uint256 deltaY = reserveYWad - ry;
uint256 deltaL = totalLiquidity - L;
reserveXWad = rx;
reserveYWad = ry;
totalLiquidity = L;
balanceOf[msg.sender] -= deltaL;
feeGrowthLast[msg.sender] = feeGrowth;
ERC20(tokenX).transfer(msg.sender, deltaX);
ERC20(tokenY).transfer(msg.sender, deltaY);
return (reserveXWad, reserveYWad, totalLiquidity);
}

/// @param data The data to be passed to the source strategy contract for pool initialization & validation.
function init(bytes calldata data)
public
Expand All @@ -107,6 +175,8 @@ contract DFMM is ICore {
reserveYWad = YYYYYY;
totalLiquidity = LLLLLL;
balanceOf[msg.sender] = LLLLLL;
feeGrowth = 1 ether;
feeGrowthLast[msg.sender] = feeGrowth;
ERC20(tokenX).transferFrom(msg.sender, address(this), XXXXXXX);
ERC20(tokenY).transferFrom(msg.sender, address(this), YYYYYY);
emit Init(msg.sender, strategy, XXXXXXX, YYYYYY, LLLLLL);
Expand All @@ -127,25 +197,28 @@ contract DFMM is ICore {
revert Invalid(swapConstantGrowth < 0, abs(swapConstantGrowth));
}

uint256 preLiquidity = totalLiquidity;
totalLiquidity = LLLLLL;
uint256 growth = totalLiquidity.divWadDown(preLiquidity);
feeGrowth = feeGrowth.mulWadDown(growth);

{
_settle({ adjustedReserveXWad: XXXXXXX, adjustedReserveYWad: YYYYYY });

bytes memory strategyData = IStrategy(strategy).dynamicSlot();
(uint256 strike, uint256 sigma, uint256 tau) =
abi.decode(strategyData, (uint256, uint256, uint256));

emit LogPoolStats(
XXXXXXX,
YYYYYY,
LLLLLL,
swapConstantGrowth,
sigma,
strike,
tau,
block.timestamp
);
// bytes memory strategyData = IStrategy(strategy).dynamicSlot();
// (uint256 strike, uint256 sigma, uint256 tau) =
// abi.decode(strategyData, (uint256, uint256, uint256));

// emit LogPoolStats(
// XXXXXXX,
// YYYYYY,
// LLLLLL,
// swapConstantGrowth,
// sigma,
// strike,
// tau,
// block.timestamp
// );
}
}

Expand Down Expand Up @@ -205,6 +278,3 @@ contract DFMM is ICore {
);
}
}

// move pure functions to solver
// pass solver address into core functions in strategy
11 changes: 11 additions & 0 deletions src/interfaces/IStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,16 @@ interface IStrategy {
uint256 totalLiquidity
);

function validateAllocationOrDeallocation(bytes calldata data)
external
view
returns (
bool valid,
int256 invariant,
uint256 rx,
uint256 ry,
uint256 L
);

function dynamicSlot() external view returns (bytes memory data);
}
72 changes: 55 additions & 17 deletions src/lib/lognormal/LogNormalExtendedLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,17 +126,41 @@ function computeInitialPoolData(
) pure returns (bytes memory) {
uint256 L = computeLGivenX(amountX, initialPrice, params);
uint256 ry = computeYGivenL(L, initialPrice, params);
int256 swapConstant =
int256 invariant =
tradingFunction({ rx: amountX, ry: ry, L: L, params: params });
L = computeNextLiquidity(amountX, ry, swapConstant, L, params);
L = computeNextLiquidity(amountX, ry, invariant, L, params);
return abi.encode(amountX, ry, L, params);
}

/// @dev Finds the root of the swapConstant given the independent variables reserveXWad and reserveYWad.
function computeAllocationGivenX(
bool add,
Comment on lines +135 to +136
Copy link
Contributor

Choose a reason for hiding this comment

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

Eventually will need to move to rounding conventions used in portfolio for allocates/deallocates computations: https://github.com/primitivefinance/portfolio/blob/08fd1ced15373d8a871555487e9fb96d44c368ab/contracts/libraries/PoolLib.sol#L383

uint256 amountX,
uint256 rx,
uint256 L
) pure returns (uint256 nextRx, uint256 nextL) {
uint256 liquidityPerRx = L.divWadUp(rx);
uint256 deltaL = amountX.mulWadUp(liquidityPerRx);
nextRx = add ? rx + amountX : rx - amountX;
nextL = add ? L + deltaL : L - deltaL;
}

function computeAllocationGivenY(
bool add,
uint256 amountY,
uint256 ry,
uint256 L
) pure returns (uint256 nextRy, uint256 nextL) {
uint256 liquidityPerRy = L.divWadUp(ry);
uint256 deltaL = amountY.mulWadUp(liquidityPerRy);
nextRy = add ? ry + amountY : ry - amountY;
nextL = add ? L + deltaL : L - deltaL;
}

/// @dev Finds the root of the invariant given the independent variables reserveXWad and reserveYWad.
function computeNextLiquidity(
uint256 rx,
uint256 ry,
int256 swapConstant,
int256 invariant,
uint256 currentL,
LogNormParameters memory params
) pure returns (uint256 nextL) {
Expand All @@ -145,9 +169,9 @@ function computeNextLiquidity(
uint256 iters;
uint256 yOverK = ry.divWadDown(params.strike);

if (swapConstant < EPSILON && swapConstant > -(EPSILON)) {
if (invariant < EPSILON && invariant > -(EPSILON)) {
return currentL;
} else if (swapConstant < 0) {
} else if (invariant < 0) {
upper = currentL;
lower = rx > yOverK ? rx + 1 : yOverK + 1;
iters = 256;
Expand All @@ -157,7 +181,7 @@ function computeNextLiquidity(
iters = 256;
}
nextL = bisection(
abi.encode(rx, ry, swapConstant, params),
abi.encode(rx, ry, invariant, params),
lower,
upper,
uint256(EPSILON),
Expand All @@ -166,18 +190,25 @@ function computeNextLiquidity(
);
}

/// @dev Finds the root of the swapConstant given the independent variable reserveXWad.
/// @dev Finds the root of the invariant given the independent variable reserveXWad.
function computeNextRy(
uint256 rx,
uint256 L,
int256 swapConstant,
int256 invariant,
uint256 currentRy,
LogNormParameters memory params
) pure returns (uint256 ry) {
uint256 lower = currentRy.mulDivDown(50, 100);
uint256 upper = currentRy; // Can use `currentRy` as upper because function is monotonic and this is only invoked if swapping x in --> must satisfy currentRy > nextRy
uint256 upper;
uint256 lower;
if (invariant < 0) {
lower = currentRy;
upper = currentRy.mulDivUp(150, 100);
} else {
lower = currentRy.mulDivDown(50, 100);
upper = currentRy; // Can use `currentRy` as upper because function is monotonic and this is only invoked if swapping x in --> must satisfy currentRy > nextRy
}
ry = bisection(
abi.encode(rx, L, swapConstant, params),
abi.encode(rx, L, invariant, params),
lower,
upper,
uint256(EPSILON),
Expand All @@ -186,18 +217,25 @@ function computeNextRy(
);
}

/// @dev Finds the root of the swapConstant given the independent variable reserveYWad.
/// @dev Finds the root of the invariant given the independent variable reserveYWad.
function computeNextRx(
uint256 ry,
uint256 L,
int256 swapConstant,
int256 invariant,
uint256 currentRx,
LogNormParameters memory params
) pure returns (uint256 rx) {
uint256 lower = currentRx.mulDivDown(50, 100);
uint256 upper = currentRx; // can use `currentRx` as upper because function is monotonic and this is only invoked if swapping y in --> must satisfy currentRx > nextRx
uint256 upper;
uint256 lower;
if (invariant < 0) {
lower = currentRx;
upper = currentRx.mulDivUp(150, 100);
} else {
lower = currentRx.mulDivDown(50, 100);
upper = currentRx; // can use `currentRx` as upper because function is monotonic and this is only invoked if swapping y in --> must satisfy currentRx > nextRx
}
rx = bisection(
abi.encode(ry, L, swapConstant, params),
abi.encode(ry, L, invariant, params),
lower,
upper,
uint256(EPSILON),
Expand Down
Loading
Loading