fixed-b
This commit is contained in:
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user