From 83d1a07fe3084eb4b17ca9d1c9a57902b24638e7 Mon Sep 17 00:00:00 2001 From: brendan Date: Fri, 23 Feb 2024 16:54:35 -0800 Subject: [PATCH 01/12] remove extraneous line + add natspec --- src/pumps/MultiFlowPump.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 23a62765..91f99b9a 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -94,7 +94,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu capExponent = ((deltaTimestamp - 1) / capInterval + 1); } - (numberOfReserves, pumpState.lastTimestamp, pumpState.lastReserves) = slot.readLastReserves(); pumpState.lastReserves = _capReserves(msg.sender, pumpState.lastReserves, reserves, capExponent, crp); // Read: Cumulative & EMA Reserves @@ -202,6 +201,17 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu cappedReserves = _capReserves(well, cappedReserves, currentReserves, capExponent, crp); } + /** + * @notice Cap `reserves` to have at most a maximum % increase/decrease in rate and a maximum % increase/decrease in total liquidity + * in relation to `lastReserves` based on the parameters defined in `crp` and the time passed since the last update, which is used + * to calculate `capExponent`. + * @param well The address of the Well + * @param lastReserves The last capped reserves. + * @param reserves The current reserves being capped. + * @param capExponent The exponent to raise the all % changes to. + * @param crp The parameters for capping reserves. See {CapReservesParameters}. + * @return cappedReserves The current reserves capped to the maximum % changes defined by `crp`. + */ function _capReserves( address well, uint256[] memory lastReserves, From a20420ab8b72d6d1af242800c1b2cae300f2bc9f Mon Sep 17 00:00:00 2001 From: brendan Date: Fri, 23 Feb 2024 16:56:53 -0800 Subject: [PATCH 02/12] remove commented out line --- src/pumps/MultiFlowPump.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 91f99b9a..ba9cf0ff 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -557,7 +557,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu uint256 lpTokenSupply, bytes memory data ) internal view returns (uint256[] memory underlyingAmounts) { - // return wf.calcLPTokenUnderlying(lpTokenAmount, reserves, lpTokenSupply, data); try wf.calcLPTokenUnderlying(lpTokenAmount, reserves, lpTokenSupply, data) returns ( uint256[] memory _underlyingAmounts ) { From 3a6ed099127c827354e6381f5637c68c37d8c29b Mon Sep 17 00:00:00 2001 From: brendan Date: Fri, 23 Feb 2024 16:58:05 -0800 Subject: [PATCH 03/12] remove TODO --- src/pumps/MultiFlowPump.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index ba9cf0ff..15399d72 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -279,7 +279,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu crv.ratios[i] = crv.rLimit; crv.ratios[j] = CAP_PRECISION; // Use a minimum of 1 for reserve. Geometric means will be set to 0 if a reserve is 0. - // TODO: Make sure this works. uint256 cappedReserveI = Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, i, crv.ratios, data), 1); cappedReserves[j] = Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, j, crv.ratios, data), 1); From 8b439efb84fa92484b7b4edbe391c96d75f61c96 Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 24 Feb 2024 14:16:38 -0800 Subject: [PATCH 04/12] ratio -> rate --- src/pumps/MultiFlowPump.sol | 6 +++--- test/pumps/PumpHelpers.sol | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 15399d72..d6519322 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -234,11 +234,11 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu cappedReserves = _capLpTokenSupply(lastReserves, cappedReserves, capExponent, crp, mfpWf, wf.data, true); if (cappedReserves.length == 0) { - cappedReserves = _capRatios(lastReserves, reserves, capExponent, crp, mfpWf, wf.data); + cappedReserves = _capRates(lastReserves, reserves, capExponent, crp, mfpWf, wf.data); cappedReserves = _capLpTokenSupply(lastReserves, cappedReserves, capExponent, crp, mfpWf, wf.data, false); } else { - cappedReserves = _capRatios(lastReserves, cappedReserves, capExponent, crp, mfpWf, wf.data); + cappedReserves = _capRates(lastReserves, cappedReserves, capExponent, crp, mfpWf, wf.data); } } @@ -252,7 +252,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu /** * @dev Cap the change in ratio of `reserves` to a maximum % change from `lastReserves`. */ - function _capRatios( + function _capRates( uint256[] memory lastReserves, uint256[] memory reserves, uint256 capExponent, diff --git a/test/pumps/PumpHelpers.sol b/test/pumps/PumpHelpers.sol index e88fc773..8e0d6990 100644 --- a/test/pumps/PumpHelpers.sol +++ b/test/pumps/PumpHelpers.sol @@ -72,7 +72,7 @@ function encodePumpData( data = abi.encode(alpha, capInterval, crp); } -function mockPumpData() view returns (bytes memory data) { +function mockPumpData() pure returns (bytes memory data) { bytes16[][] memory maxRatioChanges = new bytes16[][](2); maxRatioChanges[0] = new bytes16[](2); maxRatioChanges[1] = new bytes16[](2); From e697188b91b09b22b48eb20d9a5fffc3dda5415b Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 24 Feb 2024 14:53:30 -0800 Subject: [PATCH 05/12] ratio -> rate p2 --- src/pumps/MultiFlowPump.sol | 2 +- test/pumps/Pump.CapReserves.t.sol | 22 +++++++++++----------- test/pumps/PumpHelpers.sol | 14 ++++++-------- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index d6519322..9a97e4cd 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -52,7 +52,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu } struct CapReservesParameters { - bytes16[][] maxRatioChanges; + bytes16[][] maxRateChanges; bytes16 maxLpSupplyIncrease; bytes16 maxLpSupplyDecrease; } diff --git a/test/pumps/Pump.CapReserves.t.sol b/test/pumps/Pump.CapReserves.t.sol index 3df75ca9..e06d7aa4 100644 --- a/test/pumps/Pump.CapReserves.t.sol +++ b/test/pumps/Pump.CapReserves.t.sol @@ -21,7 +21,7 @@ contract CapBalanceTest is TestHelper, MultiFlowPump { uint256[] lastReserves; uint256[] reserves; - bytes16[][] maxRatioChanges; + bytes16[][] maxRateChanges; bytes16 maxLpSupplyIncrease; bytes16 maxLpSupplyDecrease; // uint256 MAX_RESERVE = 1e30; @@ -39,13 +39,13 @@ contract CapBalanceTest is TestHelper, MultiFlowPump { maxLpSupplyIncrease = from18(0.05e18); maxLpSupplyDecrease = from18(0.04761904762e18); - maxRatioChanges = new bytes16[][](2); - maxRatioChanges[0] = new bytes16[](2); - maxRatioChanges[1] = new bytes16[](2); - maxRatioChanges[0][1] = from18(0.05e18); - maxRatioChanges[1][0] = from18(0.05e18); + maxRateChanges = new bytes16[][](2); + maxRateChanges[0] = new bytes16[](2); + maxRateChanges[1] = new bytes16[](2); + maxRateChanges[0][1] = from18(0.05e18); + maxRateChanges[1][0] = from18(0.05e18); - crp = MultiFlowPump.CapReservesParameters(maxRatioChanges, maxLpSupplyIncrease, maxLpSupplyDecrease); + crp = MultiFlowPump.CapReservesParameters(maxRateChanges, maxLpSupplyIncrease, maxLpSupplyDecrease); wf = new ConstantProduct2(); @@ -72,7 +72,7 @@ contract CapBalanceTest is TestHelper, MultiFlowPump { lastReserves, reserves, 1, // capExponent - CapReservesParameters(maxRatioChanges, maxLpSupplyIncrease, maxLpSupplyDecrease) + CapReservesParameters(maxRateChanges, maxLpSupplyIncrease, maxLpSupplyDecrease) ); assertEq(cappedReserves[0], 101); @@ -174,8 +174,8 @@ contract CapBalanceTest is TestHelper, MultiFlowPump { (uint256 i, uint256 j) = lastReserves[0] > lastReserves[1] ? (0, 1) : (1, 0); - uint256 rIJMax = lastReserves[i] * (1e18 + to18(maxRatioChanges[i][j])) / lastReserves[j]; - uint256 rIJMin = lastReserves[i] * 1e18 / (1 + to18(maxRatioChanges[i][j])) / lastReserves[j]; + uint256 rIJMax = lastReserves[i] * (1e18 + to18(maxRateChanges[i][j])) / lastReserves[j]; + uint256 rIJMin = lastReserves[i] * 1e18 / (1 + to18(maxRateChanges[i][j])) / lastReserves[j]; uint256 rIJCapped = cappedReserves[i] * 1e18 / cappedReserves[j]; console.log("Max R: %s", rIJMax); console.log("Min R: %s", rIJMin); @@ -271,7 +271,7 @@ contract CapBalanceTest is TestHelper, MultiFlowPump { uint256 r = mfpWf.calcRate(_reserves, i, j, _wf.data); if (r < rLast) { ratioDigits = rLast.mulDiv( - ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(_crp.maxRatioChanges[j][i])).powu(capExponent).to128x128() + ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(_crp.maxRateChanges[j][i])).powu(capExponent).to128x128() .toUint256(), CAP_PRECISION2 ); diff --git a/test/pumps/PumpHelpers.sol b/test/pumps/PumpHelpers.sol index 8e0d6990..24dca9d3 100644 --- a/test/pumps/PumpHelpers.sol +++ b/test/pumps/PumpHelpers.sol @@ -73,15 +73,13 @@ function encodePumpData( } function mockPumpData() pure returns (bytes memory data) { - bytes16[][] memory maxRatioChanges = new bytes16[][](2); - maxRatioChanges[0] = new bytes16[](2); - maxRatioChanges[1] = new bytes16[](2); - maxRatioChanges[0][1] = from18(0.5e18); - maxRatioChanges[1][0] = from18(0.5e18); + bytes16[][] memory maxRateChanges = new bytes16[][](2); + maxRateChanges[0] = new bytes16[](2); + maxRateChanges[1] = new bytes16[](2); + maxRateChanges[0][1] = from18(0.5e18); + maxRateChanges[1][0] = from18(0.5e18); data = encodePumpData( - from18(0.9e18), - 12, - MultiFlowPump.CapReservesParameters(maxRatioChanges, from18(0.5e18), from18(0.4761904762e18)) + from18(0.9e18), 12, MultiFlowPump.CapReservesParameters(maxRateChanges, from18(0.5e18), from18(0.4761904762e18)) ); } From 09ab092239fc5fee1e82aa09eb3366f694cdae3b Mon Sep 17 00:00:00 2001 From: publius Date: Sat, 24 Feb 2024 14:55:00 -0800 Subject: [PATCH 06/12] Ratio -> Rate p3 --- src/pumps/MultiFlowPump.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 9a97e4cd..b321d569 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -242,7 +242,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu } } - struct CapRatiosVariables { + struct CapRatesVariables { uint256 r; uint256 rLast; uint256 rLimit; @@ -261,16 +261,16 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu bytes memory data ) internal view returns (uint256[] memory cappedReserves) { cappedReserves = reserves; - // Part 1: Cap Ratios + // Part 1: Cap Rates // Use the larger reserve as the numerator for the ratio to maximize precision (uint256 i, uint256 j) = lastReserves[0] > lastReserves[1] ? (0, 1) : (1, 0); - CapRatiosVariables memory crv; + CapRatesVariables memory crv; crv.rLast = mfpWf.calcRate(lastReserves, i, j, data); crv.r = mfpWf.calcRate(cappedReserves, i, j, data); // If the ratio increased, check that it didn't increase above the max. if (crv.r > crv.rLast) { - bytes16 tempExp = ABDKMathQuad.ONE.add(crp.maxRatioChanges[i][j]).powu(capExponent); + bytes16 tempExp = ABDKMathQuad.ONE.add(crp.maxRateChanges[i][j]).powu(capExponent); crv.rLimit = tempExp.cmp(MAX_CONVERT_TO_128x128) != -1 ? crv.rLimit = type(uint256).max : crv.rLast.mulDivOrMax(tempExp.to128x128().toUint256(), CAP_PRECISION2); @@ -287,7 +287,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu // If the ratio decreased, check that it didn't decrease below the max. } else if (crv.r < crv.rLast) { crv.rLimit = crv.rLast.mulDiv( - ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(crp.maxRatioChanges[j][i])).powu(capExponent).to128x128() + ABDKMathQuad.ONE.div(ABDKMathQuad.ONE.add(crp.maxRateChanges[j][i])).powu(capExponent).to128x128() .toUint256(), CAP_PRECISION2 ); From b88d7f29f595648a4f2f04d362d4c5a92ec7cebb Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 24 Feb 2024 14:55:21 -0800 Subject: [PATCH 07/12] add documentation around cap order --- src/pumps/MultiFlowPump.sol | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index b321d569..4a42e943 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -224,15 +224,13 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu revert TooManyTokens(); } - cappedReserves = new uint256[](2); - cappedReserves[0] = reserves[0]; - cappedReserves[1] = reserves[1]; - Call memory wf = IWell(well).wellFunction(); IMultiFlowPumpWellFunction mfpWf = IMultiFlowPumpWellFunction(wf.target); - cappedReserves = _capLpTokenSupply(lastReserves, cappedReserves, capExponent, crp, mfpWf, wf.data, true); + // The order that the LP token supply and the rates are capped are dependent upon the values of the reserves to maximize precision. + cappedReserves = _capLpTokenSupply(lastReserves, reserves, capExponent, crp, mfpWf, wf.data, true); + // If `_capLpTokenSupply` returns an empty array, then the rates should be capped first. if (cappedReserves.length == 0) { cappedReserves = _capRates(lastReserves, reserves, capExponent, crp, mfpWf, wf.data); From 80ed67dbb116650af353e99dd7b7b288b3affcdc Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 24 Feb 2024 15:02:26 -0800 Subject: [PATCH 08/12] add calcCapExponent --- src/pumps/MultiFlowPump.sol | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 4a42e943..d80a9b8b 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -91,7 +91,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu alphaN = alpha.powu(deltaTimestamp); deltaTimestampBytes = deltaTimestamp.fromUInt(); // Round up in case capInterval > block time to guarantee capExponent > 0 if time has passed since the last update. - capExponent = ((deltaTimestamp - 1) / capInterval + 1); + capExponent = calcCapExponent(deltaTimestamp, capInterval); } pumpState.lastReserves = _capReserves(msg.sender, pumpState.lastReserves, reserves, capExponent, crp); @@ -197,7 +197,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu return cappedReserves; } - uint256 capExponent = ((deltaTimestamp - 1) / capInterval + 1); + uint256 capExponent = calcCapExponent(deltaTimestamp, capInterval); cappedReserves = _capReserves(well, cappedReserves, currentReserves, capExponent, crp); } @@ -393,7 +393,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu } return emaReserves; } - uint256 capExponent = ((deltaTimestamp - 1) / capInterval + 1); + uint256 capExponent = calcCapExponent(deltaTimestamp, capInterval); lastReserves = _capReserves(well, lastReserves, reserves, capExponent, crp); bytes16 alphaN = alpha.powu(deltaTimestamp); for (uint256 i; i < numberOfReserves; ++i) { @@ -455,7 +455,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu return cumulativeReserves; } bytes16 deltaTimestampBytes = deltaTimestamp.fromUInt(); - uint256 capExponent = ((deltaTimestamp - 1) / capInterval + 1); + uint256 capExponent = calcCapExponent(deltaTimestamp, capInterval); lastReserves = _capReserves(well, lastReserves, reserves, capExponent, crp); // Currently, there is so support for overflow. for (uint256 i; i < cumulativeReserves.length; ++i) { @@ -488,6 +488,13 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu //////////////////// HELPERS //////////////////// + /** + * @dev Calculate the cap exponent for a given `deltaTimestamp` and `capInterval`. + */ + function calcCapExponent(uint256 deltaTimestamp, uint256 capInterval) internal pure returns (uint256 capExponent) { + capExponent = ((deltaTimestamp - 1) / capInterval + 1); + } + /** * @dev Convert an `address` into a `bytes32` by zero padding the right 12 bytes. */ From 4a2f50ee54b42155085ec2f01baadb63bc41eccc Mon Sep 17 00:00:00 2001 From: brendan Date: Sat, 24 Feb 2024 15:05:41 -0800 Subject: [PATCH 09/12] move struct up --- src/pumps/MultiFlowPump.sol | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index d80a9b8b..fb3875e6 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -57,6 +57,13 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu bytes16 maxLpSupplyDecrease; } + struct CapRatesVariables { + uint256 r; + uint256 rLast; + uint256 rLimit; + uint256[] ratios; + } + //////////////////// PUMP //////////////////// /** @@ -240,13 +247,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu } } - struct CapRatesVariables { - uint256 r; - uint256 rLast; - uint256 rLimit; - uint256[] ratios; - } - /** * @dev Cap the change in ratio of `reserves` to a maximum % change from `lastReserves`. */ From c37e6fc1e4895ae40647fb82cca69c69ee1a2b68 Mon Sep 17 00:00:00 2001 From: brendan Date: Sun, 25 Feb 2024 11:10:34 -0800 Subject: [PATCH 10/12] move enforce min of 1 --- src/pumps/MultiFlowPump.sol | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index fb3875e6..c3d028ac 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -330,8 +330,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu // If `_capLpTokenSupply` decreases the reserves, cap the ratio first, to maximize precision. if (returnIfBelowMin) return new uint256[](0); cappedReserves = tryCalcLPTokenUnderlying(mfpWf, maxLpTokenSupply, cappedReserves, lpTokenSupply, data); - if (cappedReserves[0] == 0) cappedReserves[0] = 1; - if (cappedReserves[1] == 0) cappedReserves[1] = 1; } // If LP Token Suppply decreased, check that it didn't increase below the min. } else if (lpTokenSupply < lastLpTokenSupply) { @@ -339,8 +337,6 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu * (ABDKMathQuad.ONE.sub(crp.maxLpSupplyDecrease)).powu(capExponent).to128x128().toUint256() / CAP_PRECISION2; if (lpTokenSupply < minLpTokenSupply) { cappedReserves = tryCalcLPTokenUnderlying(mfpWf, minLpTokenSupply, cappedReserves, lpTokenSupply, data); - if (cappedReserves[0] == 0) cappedReserves[0] = 1; - if (cappedReserves[1] == 0) cappedReserves[1] = 1; } } } @@ -553,6 +549,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu /** * @dev Assumes that if `calcLPTokenUnderlying` fails, it fails because of overflow. * If the call fails, returns the maximum possible return value for `calcLPTokenUnderlying`. + * Also, enforces a minimum of 1 for each reserve. */ function tryCalcLPTokenUnderlying( IMultiFlowPumpWellFunction wf, @@ -565,6 +562,11 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu uint256[] memory _underlyingAmounts ) { underlyingAmounts = _underlyingAmounts; + for (uint256 i; i < underlyingAmounts.length; ++i) { + if (underlyingAmounts[i] == 0) { + underlyingAmounts[i] = 1; + } + } } catch { underlyingAmounts = new uint256[](reserves.length); for (uint256 i; i < reserves.length; ++i) { From f46d5ff41b35131b2ad1cfb8c0b180e343c66aa3 Mon Sep 17 00:00:00 2001 From: brendan Date: Sun, 25 Feb 2024 11:12:30 -0800 Subject: [PATCH 11/12] mulDiv --- src/pumps/MultiFlowPump.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index c3d028ac..60b38661 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -272,7 +272,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu crv.rLimit = tempExp.cmp(MAX_CONVERT_TO_128x128) != -1 ? crv.rLimit = type(uint256).max : crv.rLast.mulDivOrMax(tempExp.to128x128().toUint256(), CAP_PRECISION2); - if (cappedReserves[i] * CAP_PRECISION / cappedReserves[j] > crv.rLimit) { + if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) > crv.rLimit) { crv.ratios = new uint256[](2); crv.ratios[i] = crv.rLimit; crv.ratios[j] = CAP_PRECISION; @@ -289,7 +289,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu .toUint256(), CAP_PRECISION2 ); - if (cappedReserves[i] * CAP_PRECISION / cappedReserves[j] < crv.rLimit) { + if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) < crv.rLimit) { crv.ratios = new uint256[](2); crv.ratios[i] = crv.rLimit; crv.ratios[j] = CAP_PRECISION; From c13a263ec9f1c62c1fd5482808156b4f342d88df Mon Sep 17 00:00:00 2001 From: brendan Date: Sun, 25 Feb 2024 12:39:29 -0800 Subject: [PATCH 12/12] add calcReservesAtRatioSwap --- src/pumps/MultiFlowPump.sol | 41 ++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/src/pumps/MultiFlowPump.sol b/src/pumps/MultiFlowPump.sol index 60b38661..ea29f6e7 100644 --- a/src/pumps/MultiFlowPump.sol +++ b/src/pumps/MultiFlowPump.sol @@ -273,14 +273,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu ? crv.rLimit = type(uint256).max : crv.rLast.mulDivOrMax(tempExp.to128x128().toUint256(), CAP_PRECISION2); if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) > crv.rLimit) { - crv.ratios = new uint256[](2); - crv.ratios[i] = crv.rLimit; - crv.ratios[j] = CAP_PRECISION; - // Use a minimum of 1 for reserve. Geometric means will be set to 0 if a reserve is 0. - uint256 cappedReserveI = - Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, i, crv.ratios, data), 1); - cappedReserves[j] = Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, j, crv.ratios, data), 1); - cappedReserves[i] = cappedReserveI; + calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data); } // If the ratio decreased, check that it didn't decrease below the max. } else if (crv.r < crv.rLast) { @@ -290,14 +283,7 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu CAP_PRECISION2 ); if (cappedReserves[i].mulDiv(CAP_PRECISION, cappedReserves[j]) < crv.rLimit) { - crv.ratios = new uint256[](2); - crv.ratios[i] = crv.rLimit; - crv.ratios[j] = CAP_PRECISION; - // Use a minimum of 1 for reserve. Geometric means will be set to 0 if a reserve is 0. - uint256 cappedReserveI = - Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, i, crv.ratios, data), 1); - cappedReserves[j] = Math.max(tryCalcReserveAtRatioSwap(mfpWf, cappedReserves, j, crv.ratios, data), 1); - cappedReserves[i] = cappedReserveI; + calcReservesAtRatioSwap(mfpWf, crv.rLimit, cappedReserves, i, j, data); } } } @@ -487,10 +473,31 @@ contract MultiFlowPump is IPump, IMultiFlowPumpErrors, IInstantaneousPump, ICumu /** * @dev Calculate the cap exponent for a given `deltaTimestamp` and `capInterval`. */ - function calcCapExponent(uint256 deltaTimestamp, uint256 capInterval) internal pure returns (uint256 capExponent) { + function calcCapExponent(uint256 deltaTimestamp, uint256 capInterval) private pure returns (uint256 capExponent) { capExponent = ((deltaTimestamp - 1) / capInterval + 1); } + /** + * @dev Calculates the capped reserves given a rate limit. + */ + function calcReservesAtRatioSwap( + IMultiFlowPumpWellFunction mfpWf, + uint256 rLimit, + uint256[] memory reserves, + uint256 i, + uint256 j, + bytes memory data + ) private view returns (uint256[] memory) { + uint256[] memory ratios = new uint256[](2); + ratios[i] = rLimit; + ratios[j] = CAP_PRECISION; + // Use a minimum of 1 for reserve. Geometric means will be set to 0 if a reserve is 0. + uint256 cappedReserveI = Math.max(tryCalcReserveAtRatioSwap(mfpWf, reserves, i, ratios, data), 1); + reserves[j] = Math.max(tryCalcReserveAtRatioSwap(mfpWf, reserves, j, ratios, data), 1); + reserves[i] = cappedReserveI; + return reserves; + } + /** * @dev Convert an `address` into a `bytes32` by zero padding the right 12 bytes. */