This commit is contained in:
tim
2025-10-24 20:01:24 -04:00
parent 2972152e58
commit 96dc134769
11 changed files with 421 additions and 253 deletions

View File

@@ -114,6 +114,22 @@ contract GasTest is Test {
uint256 constant internal INIT_BAL = 1_000_000; // initial token units for each token (internal==amount when base==1)
uint256 constant internal BASE = 1; // use base=1 so internal amounts correspond to raw integers (Q64.64 units)
// Compute fixed b from a target slippage profile for a pool that will be initialized
// with numTokens tokens each deposited with INIT_BAL and BASE==1.
function _computeFixedB(uint256 numTokens) internal view returns (int128) {
// Size metric S = sum q_i (in 64.64) with q_i = INIT_BAL for each token (BASE == 1)
int128 S = ABDKMath64x64.fromUInt(numTokens * INIT_BAL);
// E = (1 - s*(n-1)) / (1 + s)
int128 one = ABDKMath64x64.fromInt(1);
int128 nMinus1 = ABDKMath64x64.fromUInt(numTokens - 1);
int128 numerator = one.sub(targetSlippage.mul(nMinus1));
int128 denominator = one.add(targetSlippage);
int128 E = numerator.div(denominator);
// y = -ln(E) / f, b = S / y
int128 y = ABDKMath64x64.ln(E).neg().div(tradeFrac);
return S.div(y);
}
/// @notice Helper function to create a pool with the specified number of _tokens
function createPool(uint256 numTokens) internal returns (PartyPool) {
// Deploy _tokens dynamically
@@ -139,9 +155,9 @@ contract GasTest is Test {
for (uint i = 0; i < tokens.length; i++) {
ierc20Tokens[i] = IERC20(tokens[i]);
}
// Compute kappa from slippage params and number of _tokens, then construct pool with kappa
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(ierc20Tokens.length, tradeFrac, targetSlippage);
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, false);
// Compute fixed b from slippage and expected initial S, then construct pool with fixed b
int128 bFixed = _computeFixedB(ierc20Tokens.length);
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, bFixed, feePpm, feePpm, false);
// Transfer initial deposit amounts into pool before initial mint
for (uint256 i = 0; i < numTokens; i++) {
@@ -180,8 +196,8 @@ contract GasTest is Test {
for (uint i = 0; i < tokens.length; i++) {
ierc20Tokens[i] = IERC20(tokens[i]);
}
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(ierc20Tokens.length, tradeFrac, targetSlippage);
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, computedKappa, feePpm, feePpm, true);
int128 bFixed = _computeFixedB(ierc20Tokens.length);
PartyPool newPool = Deploy.newPartyPool(address(this), poolName, poolName, ierc20Tokens, bases, bFixed, feePpm, feePpm, true);
// Transfer initial deposit amounts into pool before initial mint
for (uint256 i = 0; i < numTokens; i++) {

View File

@@ -30,7 +30,8 @@ contract LMSRStabilizedTest is Test {
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
q[2] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 b = _computeBFromSlippage(3, q, stdTradeSize, stdSlippage);
s.init(q, b);
}
function initAlmostBalanced() internal {
@@ -38,7 +39,8 @@ contract LMSRStabilizedTest is Test {
q[0] = ABDKMath64x64.fromUInt(999_999);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
q[2] = ABDKMath64x64.fromUInt(1_000_001);
s.init(q, stdTradeSize, stdSlippage);
int128 b = _computeBFromSlippage(3, q, stdTradeSize, stdSlippage);
s.init(q, b);
}
function initImbalanced() internal {
@@ -47,7 +49,8 @@ contract LMSRStabilizedTest is Test {
q[1] = ABDKMath64x64.fromUInt(1e9);
q[2] = ABDKMath64x64.fromUInt(1);
q[3] = ABDKMath64x64.divu(1, 1e9);
s.init(q, stdTradeSize, stdSlippage);
int128 b = _computeBFromSlippage(4, q, stdTradeSize, stdSlippage);
s.init(q, b);
}
@@ -193,7 +196,6 @@ contract LMSRStabilizedTest is Test {
// Verify basic state is still functional
assertTrue(s.nAssets > 0, "State should still be initialized");
assertTrue(s.kappa > int128(0), "Kappa should still be positive");
}
function testRescalingAfterDeposit() public {
@@ -211,7 +213,6 @@ contract LMSRStabilizedTest is Test {
// Store initial parameters
int128 initialB = _computeB(initialQ);
int128 initialKappa = s.kappa;
// Simulate a deposit by increasing all asset quantities by 50%
int128[] memory newQ = new int128[](s.nAssets);
@@ -223,18 +224,15 @@ contract LMSRStabilizedTest is Test {
// Apply the update for proportional change
s.updateForProportionalChange(newQ);
// Verify that b has been rescaled proportionally
// Verify that b remains constant after proportional deposit (fixed-b model)
int128 newB = _computeB(s.qInternal);
int128 expectedRatio = ABDKMath64x64.fromUInt(3).div(ABDKMath64x64.fromUInt(2)); // 1.5x
int128 expectedRatio = ABDKMath64x64.fromInt(1); // invariant b
int128 actualRatio = newB.div(initialB);
int128 tolerance = ABDKMath64x64.divu(1, 1000); // 0.1% tolerance
assertTrue((actualRatio.sub(expectedRatio)).abs() < tolerance, "b did not scale proportionally after deposit");
assertTrue((actualRatio.sub(expectedRatio)).abs() < tolerance, "b should remain constant after deposit");
// Verify kappa remained unchanged
assertTrue((s.kappa.sub(initialKappa)).abs() < tolerance, "kappa should not change after deposit");
// Verify slippage target is still met by performing a trade
// Perform a trade and verify outputs are reasonable
int128 tradeAmount = s.qInternal[0].mul(stdTradeSize);
(int128 amountIn, int128 amountOut) = s.swapAmountsForExactInput(0, 1, tradeAmount, 0);
@@ -250,8 +248,10 @@ contract LMSRStabilizedTest is Test {
int128 slippage = slippageRatio.sub(ABDKMath64x64.fromInt(1));
console2.log('post-deposit slippage', slippage);
int128 relativeError = slippage.sub(stdSlippage).abs().div(stdSlippage);
assertLt(relativeError, ABDKMath64x64.divu(1, 100), "Slippage target not met after deposit");
// With fixed b, theoretical slippage is exp(a/b) - 1
int128 expectedSlippage = _exp(tradeAmount.div(newB)).sub(ABDKMath64x64.fromInt(1));
int128 slippageError = (slippage.sub(expectedSlippage)).abs();
assertLt(slippageError, ABDKMath64x64.divu(1, 1_000_000), "Observed slippage deviates from model");
}
/// @notice Test balanced2 handling of limitPrice that causes truncation of input a
@@ -260,7 +260,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
// Compute b for constructing meaningful a and limits
int128 b = _computeB(q);
@@ -296,7 +297,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
// Small input a
int128 a = q[0].mul(ABDKMath64x64.divu(1, 1000)); // 0.1% of asset
@@ -326,7 +328,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
int128 limitPrice = ABDKMath64x64.fromInt(1); // equal to current price
@@ -359,7 +362,6 @@ contract LMSRStabilizedTest is Test {
// Store initial parameters
int128 initialB = _computeB(initialQ);
int128 initialKappa = s.kappa;
// Simulate a withdrawal by decreasing all asset quantities by 30%
int128[] memory newQ = new int128[](s.nAssets);
@@ -371,18 +373,15 @@ contract LMSRStabilizedTest is Test {
// Apply the update for proportional change
s.updateForProportionalChange(newQ);
// Verify that b has been rescaled proportionally
// Verify that b remains constant after proportional withdrawal (fixed-b model)
int128 newB = _computeB(s.qInternal);
int128 expectedRatio = ABDKMath64x64.fromUInt(7).div(ABDKMath64x64.fromUInt(10)); // 0.7x
int128 expectedRatio = ABDKMath64x64.fromInt(1); // invariant b
int128 actualRatio = newB.div(initialB);
int128 tolerance = ABDKMath64x64.divu(1, 1000); // 0.1% tolerance
assertTrue((actualRatio.sub(expectedRatio)).abs() < tolerance, "b did not scale proportionally after withdrawal");
assertTrue((actualRatio.sub(expectedRatio)).abs() < tolerance, "b should remain constant after withdrawal");
// Verify kappa remained unchanged
assertTrue((s.kappa.sub(initialKappa)).abs() < tolerance, "kappa should not change after withdrawal");
// Verify slippage target is still met by performing a trade
// Perform a trade and verify outputs are reasonable
int128 tradeAmount = s.qInternal[0].mul(stdTradeSize);
(int128 amountIn, int128 amountOut) = s.swapAmountsForExactInput(0, 1, tradeAmount, 0);
@@ -398,8 +397,10 @@ contract LMSRStabilizedTest is Test {
int128 slippage = slippageRatio.sub(ABDKMath64x64.fromInt(1));
console2.log('post-withdrawal slippage', slippage);
int128 relativeError = slippage.sub(stdSlippage).abs().div(stdSlippage);
assertLt(relativeError, ABDKMath64x64.divu(1, 100), "Slippage target not met after withdrawal");
// With fixed b, theoretical slippage is exp(a/b) - 1
int128 expectedSlippage = _exp(tradeAmount.div(newB)).sub(ABDKMath64x64.fromInt(1));
int128 slippageError = (slippage.sub(expectedSlippage)).abs();
assertLt(slippageError, ABDKMath64x64.divu(1, 1_000_000), "Observed slippage deviates from model");
}
// --- tests probing numerical stability and boundary conditions ---
@@ -431,8 +432,8 @@ contract LMSRStabilizedTest is Test {
this.externalSwapAmountsForExactInput(0, 1, tradeAmount, ABDKMath64x64.fromInt(1));
}
/// @notice If e_j == 0 we should revert early to avoid div-by-zero
function testEJZeroReverts() public {
/// @notice If q_j == 0, kernel should still handle computation without revert (wrapper enforces caps)
function testZeroQuantityOutputAssetDoesNotRevert() public {
initBalanced();
// Create mock qInternal where asset 1 has zero quantity
@@ -446,8 +447,10 @@ contract LMSRStabilizedTest is Test {
int128 tradeAmount = mockQInternal[0].mul(stdTradeSize);
vm.expectRevert(bytes("LMSR: e_j==0"));
this.externalSwapAmountsForExactInput(0, 1, tradeAmount, 0);
// Should not revert; exact-input uses full input and returns a defined output
(int128 usedIn, int128 outAmt) = this.externalSwapAmountsForExactInput(0, 1, tradeAmount, 0);
assertEq(usedIn, tradeAmount, "exact-input should consume full input without limit");
assertTrue(outAmt >= 0, "output amount should be non-negative when q_j == 0");
}
/// @notice swapAmountsForPriceLimit returns zero if limit equals current price
@@ -534,18 +537,16 @@ contract LMSRStabilizedTest is Test {
this.externalSwapAmountsForExactInput(0, 1, a, 0);
}
// Helper function to compute b from qInternal (either from provided array or state)
// Helper function to fetch fixed b (independent of qInternal)
function _computeB(int128[] memory qInternal) internal view returns (int128) {
int128 sizeMetric = _computeSizeMetric(qInternal);
require(sizeMetric > int128(0), "LMSR: size metric zero");
return s.kappa.mul(sizeMetric);
// silence unused warning for qInternal in tests
qInternal;
return s.bFixed;
}
// Overload that uses state's cached qInternal
// Overload that uses state's fixed b
function _computeB() internal view returns (int128) {
int128 sizeMetric = _computeSizeMetric(s.qInternal);
require(sizeMetric > int128(0), "LMSR: size metric zero");
return s.kappa.mul(sizeMetric);
return s.bFixed;
}
// Helper function to compute size metric (sum of all asset quantities)
@@ -558,6 +559,41 @@ contract LMSRStabilizedTest is Test {
return total;
}
// Local helper: compute fixed b from a target slippage profile.
// For a trade of fraction f of S with target slippage s across n assets:
// E = (1 - s*(n-1)) / (1 + s)
// y = -ln(E) / f
// b = S / y
function _computeBFromSlippage(
uint256 nAssets,
int128[] memory qInternal,
int128 tradeFrac,
int128 targetSlippage
) internal pure returns (int128) {
require(nAssets > 1, "test: n>1");
int128 S = _computeSizeMetric(qInternal);
require(S > int128(0), "test: S<=0");
int128 f = tradeFrac;
require(f > int128(0) && f < ABDKMath64x64.fromInt(1), "test: f out of range");
int128 one = ABDKMath64x64.fromInt(1);
int128 nMinus1 = ABDKMath64x64.fromUInt(nAssets - 1);
// E must be in (0,1)
int128 numerator = one.sub(targetSlippage.mul(nMinus1)); // 1 - s*(n-1)
int128 denominator = one.add(targetSlippage); // 1 + s
require(numerator > int128(0), "test: bad slippage");
int128 E = numerator.div(denominator);
require(E > int128(0) && E < one, "test: E out of range");
int128 y = ABDKMath64x64.ln(E).neg().div(f);
require(y > int128(0), "test: y<=0");
return S.div(y);
}
// Helper function to update the state's cached qInternal
function _updateCachedQInternal(int128[] memory mockQInternal) internal {
// First ensure qInternal array exists with the right size
@@ -955,7 +991,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
// Small trade (well within u <= 0.5 and delta <= 1%)
int128 a = q[0].mul(ABDKMath64x64.divu(1, 1000)); // 0.1% of asset
@@ -986,7 +1023,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
// Prepare newQ starting from equal quantities; we'll grow q0 until delta > DELTA_MAX
int128[] memory newQ = new int128[](2);
@@ -1045,7 +1083,8 @@ contract LMSRStabilizedTest is Test {
int128[] memory q = new int128[](2);
q[0] = ABDKMath64x64.fromUInt(1_000_000);
q[1] = ABDKMath64x64.fromUInt(1_000_000);
s.init(q, stdTradeSize, stdSlippage);
int128 bInit = _computeBFromSlippage(2, q, stdTradeSize, stdSlippage);
s.init(q, bInit);
// Compute b
int128 b = _computeB(q);

View File

@@ -53,6 +53,18 @@ contract NativeTest is Test {
uint256 constant INIT_BAL = 1_000_000; // initial token units for each token
uint256 constant BASE = 1; // use base=1 so internal amounts correspond to raw integers
// Compute fixed b for a pool initialized with numTokens tokens, each deposited with INIT_BAL (BASE == 1).
function _computeFixedB(uint256 numTokens) internal view returns (int128) {
int128 S = ABDKMath64x64.fromUInt(numTokens * INIT_BAL);
int128 one = ABDKMath64x64.fromInt(1);
int128 nMinus1 = ABDKMath64x64.fromUInt(numTokens - 1);
int128 numerator = one.sub(targetSlippage.mul(nMinus1));
int128 denominator = one.add(targetSlippage);
int128 E = numerator.div(denominator);
int128 y = ABDKMath64x64.ln(E).neg().div(tradeFrac);
return S.div(y);
}
function setUp() public {
alice = address(0xA11ce);
bob = address(0xB0b);
@@ -93,8 +105,8 @@ contract NativeTest is Test {
// Deploy pool with a small fee (0.1%)
uint256 feePpm = 1000;
int128 kappa = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, kappa, feePpm, feePpm, weth, false);
int128 bFixed = _computeFixedB(tokens.length);
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, bFixed, feePpm, feePpm, weth, false);
// Transfer initial deposit amounts into pool
token0.transfer(address(pool), INIT_BAL);

View File

@@ -15,6 +15,7 @@ import {LMSRStabilized} from "../src/LMSRStabilized.sol";
import {PartyPlanner} from "../src/PartyPlanner.sol";
import {PartyPool} from "../src/PartyPool.sol";
import {MockERC20} from "./PartyPlanner.t.sol";
import "@abdk/ABDKMath64x64.sol";
// Mock ERC20 token for testing
contract MockERC20 is ERC20 {
@@ -34,11 +35,37 @@ contract MockERC20 is ERC20 {
}
contract PartyPlannerTest is Test {
using ABDKMath64x64 for int128;
PartyPlanner public planner;
MockERC20 public tokenA;
MockERC20 public tokenB;
MockERC20 public tokenC;
function _computeFixedBFromDeposits(
uint256 nAssets,
uint256[] memory bases,
uint256[] memory initialDeposits,
int128 tradeFrac,
int128 targetSlippage
) internal pure returns (int128) {
require(bases.length == initialDeposits.length && bases.length == nAssets, "length mismatch");
// Compute S = sum(deposit_i / base_i) in Q64.64
int128 S = 0;
for (uint256 i = 0; i < nAssets; i++) {
S = S + ABDKMath64x64.divu(initialDeposits[i], bases[i]);
}
// E = (1 - s*(n-1)) / (1 + s)
int128 one = ABDKMath64x64.fromInt(1);
int128 nMinus1 = ABDKMath64x64.fromUInt(nAssets - 1);
int128 numerator = one.sub(targetSlippage.mul(nMinus1));
int128 denominator = one.add(targetSlippage);
int128 E = numerator.div(denominator);
// y = -ln(E) / f, b = S / y
int128 y = ABDKMath64x64.ln(E).neg().div(tradeFrac);
return S.div(y);
}
address public payer = makeAddr("payer");
address public receiver = makeAddr("receiver");
@@ -93,15 +120,15 @@ contract PartyPlannerTest is Test {
uint256 initialTokenACount = planner.poolsByTokenCount(IERC20(address(tokenA)));
uint256 initialTokenBCount = planner.poolsByTokenCount(IERC20(address(tokenB)));
// Compute kappa then create pool via kappa overload
int128 computedKappa = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
// Compute fixed b from slippage and initial deposits, then create pool
int128 bFixed = _computeFixedBFromDeposits(tokens.length, bases, initialDeposits, tradeFrac, targetSlippage);
(IPartyPool pool, uint256 lpAmount) = planner.newPool(
name,
symbol,
tokens,
bases,
computedKappa,
bFixed,
swapFeePpm,
flashFeePpm,
false, // not stable
@@ -174,10 +201,10 @@ contract PartyPlannerTest is Test {
deposits1[0] = INITIAL_DEPOSIT_AMOUNT;
deposits1[1] = INITIAL_DEPOSIT_AMOUNT;
int128 kappa1 = LMSRStabilized.computeKappaFromSlippage(tokens1.length, int128((1 << 64) - 1), int128(1 << 62));
int128 b1 = _computeFixedBFromDeposits(tokens1.length, bases1, deposits1, int128((1 << 64) - 1), int128(1 << 62));
(IPartyPool pool1,) = planner.newPool(
"Pool 1", "LP1", tokens1, bases1,
kappa1, 3000, 5000, false,
b1, 3000, 5000, false,
payer, receiver, deposits1, 1000e18, 0
);
@@ -194,10 +221,10 @@ contract PartyPlannerTest is Test {
deposits2[0] = INITIAL_DEPOSIT_AMOUNT;
deposits2[1] = INITIAL_DEPOSIT_AMOUNT / 1e12; // Adjust for 6 decimals
int128 kappa2 = LMSRStabilized.computeKappaFromSlippage(tokens2.length, int128((1 << 64) - 1), int128(1 << 62));
int128 b2 = _computeFixedBFromDeposits(tokens2.length, bases2, deposits2, int128((1 << 64) - 1), int128(1 << 62));
(IPartyPool pool2,) = planner.newPool(
"Pool 2", "LP2", tokens2, bases2,
kappa2, 3000, 5000, false,
b2, 3000, 5000, false,
payer, receiver, deposits2, 1000e18, 0
);
@@ -250,12 +277,12 @@ contract PartyPlannerTest is Test {
validDeposits[0] = INITIAL_DEPOSIT_AMOUNT;
validDeposits[1] = INITIAL_DEPOSIT_AMOUNT;
int128 kappaErr = LMSRStabilized.computeKappaFromSlippage(tokens.length, int128((1 << 64) - 1), int128(1 << 62));
int128 bErr = _computeFixedBFromDeposits(tokens.length, bases, validDeposits, int128((1 << 64) - 1), int128(1 << 62));
vm.expectRevert("Planner: payer cannot be zero address");
planner.newPool(
"Test Pool", "TESTLP", tokens, bases,
kappaErr, 3000, 5000, false,
bErr, 3000, 5000, false,
address(0), receiver, validDeposits, 1000e18, 0
);
@@ -263,18 +290,18 @@ contract PartyPlannerTest is Test {
vm.expectRevert("Planner: receiver cannot be zero address");
planner.newPool(
"Test Pool", "TESTLP", tokens, bases,
kappaErr, 3000, 5000, false,
bErr, 3000, 5000, false,
payer, address(0), validDeposits, 1000e18, 0
);
// Test deadline exceeded
// The default timestamp is 1 and 1-0 is 0 which means "ignore deadline," so we need to set a proper timestamp.
int128 kappaDeadline = LMSRStabilized.computeKappaFromSlippage(tokens.length, int128((1 << 64) - 1), int128(1 << 62));
int128 bDeadline = _computeFixedBFromDeposits(tokens.length, bases, validDeposits, int128((1 << 64) - 1), int128(1 << 62));
vm.warp(1000);
vm.expectRevert("Planner: deadline exceeded");
planner.newPool(
"Test Pool", "TESTLP", tokens, bases,
kappaDeadline, 3000, 5000, false,
bDeadline, 3000, 5000, false,
payer, receiver, validDeposits, 1000e18, block.timestamp - 1
);
}
@@ -297,12 +324,12 @@ contract PartyPlannerTest is Test {
deposits[0] = INITIAL_DEPOSIT_AMOUNT;
deposits[1] = INITIAL_DEPOSIT_AMOUNT;
int128 kappaLoop = LMSRStabilized.computeKappaFromSlippage(tokens.length, int128((1 << 64) - 1), int128(1 << 62));
int128 bLoop = _computeFixedBFromDeposits(tokens.length, bases, deposits, int128((1 << 64) - 1), int128(1 << 62));
(IPartyPool pool,) = planner.newPool(
string(abi.encodePacked("Pool ", vm.toString(i))),
string(abi.encodePacked("LP", vm.toString(i))),
tokens, bases,
kappaLoop, 3000, 5000, false,
bLoop, 3000, 5000, false,
payer, receiver, deposits, 1000e18, 0
);

View File

@@ -125,6 +125,18 @@ contract PartyPoolTest is Test {
uint256 constant INIT_BAL = 1_000_000; // initial token units for each token (internal==amount when base==1)
uint256 constant BASE = 1; // use base=1 so internal amounts correspond to raw integers (Q64.64 units)
// Compute fixed b for a pool initialized with numTokens tokens, each deposited with INIT_BAL (BASE == 1).
function _computeFixedB(uint256 numTokens) internal view returns (int128) {
int128 S = ABDKMath64x64.fromUInt(numTokens * INIT_BAL);
int128 one = ABDKMath64x64.fromInt(1);
int128 nMinus1 = ABDKMath64x64.fromUInt(numTokens - 1);
int128 numerator = one.sub(targetSlippage.mul(nMinus1));
int128 denominator = one.add(targetSlippage);
int128 E = numerator.div(denominator);
int128 y = ABDKMath64x64.ln(E).neg().div(tradeFrac);
return S.div(y);
}
function setUp() public {
planner = Deploy.newPartyPlanner();
alice = address(0xA11ce);
@@ -172,8 +184,8 @@ contract PartyPoolTest is Test {
// Deploy pool with a small fee to test fee-handling paths (use 1000 ppm = 0.1%)
uint256 feePpm = 1000;
int128 kappa3 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, kappa3, feePpm, feePpm, false);
int128 b3 = _computeFixedB(tokens.length);
pool = Deploy.newPartyPool(address(this), "LP", "LP", tokens, bases, b3, feePpm, feePpm, false);
// Transfer initial deposit amounts into pool before initial mint (pool expects _tokens already in contract)
// We deposit equal amounts INIT_BAL for each token
@@ -202,8 +214,8 @@ contract PartyPoolTest is Test {
bases10[i] = BASE;
}
int128 kappa10 = LMSRStabilized.computeKappaFromSlippage(tokens10.length, tradeFrac, targetSlippage);
pool10 = Deploy.newPartyPool(address(this), "LP10", "LP10", tokens10, bases10, kappa10, feePpm, feePpm, false);
int128 b10 = _computeFixedB(tokens10.length);
pool10 = Deploy.newPartyPool(address(this), "LP10", "LP10", tokens10, bases10, b10, feePpm, feePpm, false);
// Mint additional _tokens for pool10 initial deposit
token0.mint(address(this), INIT_BAL);
@@ -991,12 +1003,12 @@ contract PartyPoolTest is Test {
uint256 feePpm = 1000;
// Pool with default initialization (lpTokens = 0)
int128 kappaDefault = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault, feePpm, feePpm, false);
int128 bDefault = _computeFixedB(tokens.length);
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, bDefault, feePpm, feePpm, false);
// Pool with custom initialization (lpTokens = custom amount)
int128 kappaCustom = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom, feePpm, feePpm, false);
int128 bCustom = _computeFixedB(tokens.length);
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, bCustom, feePpm, feePpm, false);
// Mint additional _tokens for both pools
token0.mint(address(this), INIT_BAL * 2);
@@ -1067,10 +1079,10 @@ contract PartyPoolTest is Test {
uint256 feePpm = 1000;
int128 kappaDefault2 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, kappaDefault2, feePpm, feePpm, false);
int128 kappaCustom2 = LMSRStabilized.computeKappaFromSlippage(tokens.length, tradeFrac, targetSlippage);
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, kappaCustom2, feePpm, feePpm, false);
int128 bDefault2 = _computeFixedB(tokens.length);
PartyPool poolDefault = Deploy.newPartyPool(address(this), "LP_DEFAULT", "LP_DEFAULT", tokens, bases, bDefault2, feePpm, feePpm, false);
int128 bCustom2 = _computeFixedB(tokens.length);
PartyPool poolCustom = Deploy.newPartyPool(address(this), "LP_CUSTOM", "LP_CUSTOM", tokens, bases, bCustom2, feePpm, feePpm, false);
// Mint additional _tokens
token0.mint(address(this), INIT_BAL * 4);