-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #53 from primitivefinance/experimental/n-token-pools
Experimental/n token pools
- Loading branch information
Showing
101 changed files
with
7,728 additions
and
4,288 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,8 +7,6 @@ optimized-out | |
kit/target | ||
target | ||
|
||
# VSCode | ||
.vscode | ||
.env | ||
report/main.aux | ||
report/main.pyg | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"recommendations": [ | ||
"remisa.mathover", | ||
"nomicfoundation.hardhat-solidity" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
{ | ||
"editor.formatOnSave": true, | ||
"solidity.formatter": "forge", | ||
"[solidity]": { | ||
"editor.defaultFormatter": "NomicFoundation.hardhat-solidity" | ||
}, | ||
"mathover.matchRegex": "\\$\\$(.*?)\\$\\$", | ||
"mathover.forwardSkip": 2, | ||
"mathover.backwardSkip": 2 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,228 +1,130 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
pragma solidity ^0.8.13; | ||
|
||
import "./ConstantSumLib.sol"; | ||
import "src/interfaces/IDFMM.sol"; | ||
import "src/interfaces/IStrategy.sol"; | ||
import "forge-std/Test.sol"; | ||
|
||
contract ConstantSum is IStrategy { | ||
using FixedPointMathLib for uint256; | ||
import "./ConstantSumMath.sol"; | ||
import "./ConstantSumUtils.sol"; | ||
import { PairStrategy, IStrategy, Pool } from "src/PairStrategy.sol"; | ||
|
||
struct InternalParams { | ||
uint256 price; | ||
uint256 swapFee; | ||
address controller; | ||
} | ||
|
||
struct InternalParams { | ||
uint256 price; | ||
uint256 swapFee; | ||
address controller; | ||
} | ||
struct ConstantSumParams { | ||
uint256 price; | ||
uint256 swapFee; | ||
address controller; | ||
} | ||
|
||
struct ConstantSumParams { | ||
uint256 price; | ||
uint256 swapFee; | ||
address controller; | ||
} | ||
enum UpdateCode { | ||
Invalid, | ||
SwapFee, | ||
Price, | ||
Controller | ||
} | ||
|
||
/// @inheritdoc IStrategy | ||
address public dfmm; | ||
contract ConstantSum is PairStrategy { | ||
using FixedPointMathLib for uint256; | ||
|
||
/// @inheritdoc IStrategy | ||
string public constant name = "ConstantSum"; | ||
|
||
mapping(uint256 => InternalParams) public internalParams; | ||
|
||
constructor(address dfmm_) { | ||
dfmm = dfmm_; | ||
} | ||
|
||
modifier onlyDFMM() { | ||
if (msg.sender != dfmm) revert NotDFMM(); | ||
_; | ||
} | ||
constructor(address dfmm_) PairStrategy(dfmm_) { } | ||
|
||
function init( | ||
address, | ||
uint256 poolId, | ||
IDFMM.Pool calldata pool, | ||
Pool calldata, | ||
bytes calldata data | ||
) | ||
public | ||
onlyDFMM | ||
returns ( | ||
bool valid, | ||
int256 invariant, | ||
uint256 reserveX, | ||
uint256 reserveY, | ||
uint256[] memory reserves, | ||
uint256 totalLiquidity | ||
) | ||
{ | ||
ConstantSumParams memory params; | ||
(reserveX, reserveY, totalLiquidity, params) = | ||
abi.decode(data, (uint256, uint256, uint256, ConstantSumParams)); | ||
(reserves, totalLiquidity, params) = | ||
abi.decode(data, (uint256[], uint256, ConstantSumParams)); | ||
|
||
internalParams[poolId].price = params.price; | ||
internalParams[poolId].swapFee = params.swapFee; | ||
|
||
// Get the trading function and check this is valid | ||
invariant = ConstantSumLib.tradingFunction( | ||
reserveX, reserveY, totalLiquidity, params.price | ||
); | ||
|
||
valid = -EPSILON < invariant && invariant < EPSILON; | ||
|
||
return (valid, invariant, reserveX, reserveY, totalLiquidity); | ||
} | ||
|
||
function validateSwap( | ||
address, | ||
uint256 poolId, | ||
IDFMM.Pool calldata pool, | ||
bytes calldata data | ||
) | ||
external | ||
view | ||
returns ( | ||
bool valid, | ||
int256 invariant, | ||
int256 liquidityDelta, | ||
uint256 nextRx, | ||
uint256 nextRy, | ||
uint256 nextL | ||
) | ||
{ | ||
ConstantSumParams memory params = | ||
abi.decode(getPoolParams(poolId), (ConstantSumParams)); | ||
|
||
(nextRx, nextRy, nextL) = abi.decode(data, (uint256, uint256, uint256)); | ||
|
||
uint256 minLiquidityDelta; | ||
uint256 amountIn; | ||
uint256 fees; | ||
if (nextRx > pool.reserveX) { | ||
amountIn = nextRx - pool.reserveX; | ||
fees = amountIn.mulWadUp(params.swapFee); | ||
minLiquidityDelta += fees; | ||
} else if (nextRy > pool.reserveY) { | ||
amountIn = nextRy - pool.reserveY; | ||
fees = amountIn.mulWadUp(params.swapFee); | ||
minLiquidityDelta += fees.divWadUp(params.price); | ||
} else { | ||
revert("invalid swap: inputs x and y have the same sign!"); | ||
} | ||
|
||
liquidityDelta = int256(nextL) - int256(pool.totalLiquidity); | ||
assert(liquidityDelta >= int256(minLiquidityDelta)); | ||
|
||
invariant = | ||
ConstantSumLib.tradingFunction(nextRx, nextRy, nextL, params.price); | ||
|
||
valid = -EPSILON < invariant && invariant < EPSILON; | ||
} | ||
|
||
function computeSwapConstant( | ||
uint256 poolId, | ||
bytes memory data | ||
) external view returns (int256) { | ||
(uint256 reserveX, uint256 reserveY, uint256 totalLiquidity) = | ||
abi.decode(data, (uint256, uint256, uint256)); | ||
return ConstantSumLib.tradingFunction( | ||
reserveX, | ||
reserveY, | ||
totalLiquidity, | ||
abi.decode(getPoolParams(poolId), (ConstantSumParams)).price | ||
); | ||
} | ||
|
||
// This should literally always work lol | ||
function validateAllocate( | ||
address, | ||
uint256 poolId, | ||
IDFMM.Pool calldata pool, | ||
bytes calldata data | ||
) | ||
external | ||
view | ||
returns ( | ||
bool valid, | ||
int256 invariant, | ||
uint256 deltaX, | ||
uint256 deltaY, | ||
uint256 deltaLiquidity | ||
) | ||
{ | ||
(deltaX, deltaY, deltaLiquidity) = | ||
abi.decode(data, (uint256, uint256, uint256)); | ||
|
||
invariant = ConstantSumLib.tradingFunction( | ||
pool.reserveX + deltaX, | ||
pool.reserveX + deltaY, | ||
pool.totalLiquidity + deltaLiquidity, | ||
abi.decode(getPoolParams(poolId), (ConstantSumParams)).price | ||
); | ||
tradingFunction(reserves, totalLiquidity, abi.encode(params)); | ||
|
||
valid = -EPSILON < invariant && invariant < EPSILON; | ||
} | ||
|
||
// This should literally always work lol | ||
function validateDeallocate( | ||
address, | ||
uint256 poolId, | ||
IDFMM.Pool calldata pool, | ||
bytes calldata data | ||
) | ||
external | ||
view | ||
returns ( | ||
bool valid, | ||
int256 invariant, | ||
uint256 deltaX, | ||
uint256 deltaY, | ||
uint256 deltaLiquidity | ||
) | ||
{ | ||
(deltaX, deltaY, deltaLiquidity) = | ||
abi.decode(data, (uint256, uint256, uint256)); | ||
|
||
invariant = ConstantSumLib.tradingFunction( | ||
pool.reserveX - deltaX, | ||
pool.reserveY - deltaY, | ||
pool.totalLiquidity - deltaLiquidity, | ||
abi.decode(getPoolParams(poolId), (ConstantSumParams)).price | ||
); | ||
|
||
valid = -EPSILON < invariant && invariant < EPSILON; | ||
return (valid, invariant, reserves, totalLiquidity); | ||
} | ||
|
||
/// @inheritdoc IStrategy | ||
function update( | ||
address sender, | ||
uint256 poolId, | ||
IDFMM.Pool calldata pool, | ||
Pool calldata, | ||
bytes calldata data | ||
) external onlyDFMM { | ||
if (sender != internalParams[poolId].controller) revert InvalidSender(); | ||
ConstantSumLib.ConstantSumUpdateCode updateCode = | ||
abi.decode(data, (ConstantSumLib.ConstantSumUpdateCode)); | ||
|
||
if (updateCode == ConstantSumLib.ConstantSumUpdateCode.Price) { | ||
internalParams[poolId].price = | ||
ConstantSumLib.decodePriceUpdate(data); | ||
} else if (updateCode == ConstantSumLib.ConstantSumUpdateCode.SwapFee) { | ||
internalParams[poolId].swapFee = | ||
ConstantSumLib.decodeFeeUpdate(data); | ||
} else if ( | ||
updateCode == ConstantSumLib.ConstantSumUpdateCode.Controller | ||
) { | ||
internalParams[poolId].controller = | ||
ConstantSumLib.decodeControllerUpdate(data); | ||
UpdateCode updateCode = abi.decode(data, (UpdateCode)); | ||
|
||
if (updateCode == UpdateCode.Price) { | ||
(internalParams[poolId].price,) = decodePriceUpdate(data); | ||
} else if (updateCode == UpdateCode.SwapFee) { | ||
internalParams[poolId].swapFee = decodeFeeUpdate(data); | ||
} else if (updateCode == UpdateCode.Controller) { | ||
internalParams[poolId].controller = decodeControllerUpdate(data); | ||
} else { | ||
revert InvalidUpdateCode(); | ||
} | ||
} | ||
|
||
function getPoolParams(uint256 poolId) public view returns (bytes memory) { | ||
function getPoolParams(uint256 poolId) | ||
public | ||
view | ||
override | ||
returns (bytes memory) | ||
{ | ||
ConstantSumParams memory params; | ||
|
||
params.price = internalParams[poolId].price; | ||
params.swapFee = internalParams[poolId].swapFee; | ||
|
||
return abi.encode(params); | ||
} | ||
|
||
function tradingFunction( | ||
uint256[] memory reserves, | ||
uint256 totalLiquidity, | ||
bytes memory params | ||
) public pure override returns (int256) { | ||
return computeTradingFunction( | ||
reserves, | ||
totalLiquidity, | ||
abi.decode(params, (ConstantSumParams)).price | ||
); | ||
} | ||
|
||
function _computeAllocateDeltasGivenDeltaL( | ||
uint256, | ||
Pool memory, | ||
bytes memory | ||
) internal pure override returns (uint256[] memory) { | ||
return new uint256[](0); | ||
} | ||
|
||
function _computeDeallocateDeltasGivenDeltaL( | ||
uint256, | ||
Pool memory, | ||
bytes memory | ||
) internal pure override returns (uint256[] memory) { | ||
return new uint256[](0); | ||
} | ||
} |
Oops, something went wrong.