Skip to content

Commit f76f1f2

Browse files
committed
feat: fix rebalance strategy calculation
1 parent 31dd3b0 commit f76f1f2

3 files changed

Lines changed: 36 additions & 28 deletions

File tree

contracts/swap/ReserveLiquidityStrategy.sol

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,10 @@ contract ReserveLiquidityStrategy is LiquidityStrategy {
171171
});
172172

173173
if (priceDirection == PriceDirection.ABOVE_ORACLE) {
174-
(collateralOut, inputAmount) = _calculateExpansionAmounts(params, oraclePrice);
174+
(collateralOut, inputAmount) = _calculateExpansionAmounts(params, oraclePrice, incentive);
175175
stableOut = 0;
176176
} else {
177-
(stableOut, inputAmount) = _calculateContractionAmounts(params, oraclePrice);
177+
(stableOut, inputAmount) = _calculateContractionAmounts(params, oraclePrice, incentive);
178178
collateralOut = 0;
179179
}
180180

@@ -190,16 +190,17 @@ contract ReserveLiquidityStrategy is LiquidityStrategy {
190190
*/
191191
function _calculateContractionAmounts(
192192
RebalanceParams memory params,
193-
uint256 oraclePrice
193+
uint256 oraclePrice,
194+
uint256 incentive
194195
) private pure returns (uint256 stableOut, uint256 collateralIn) {
195196
// Contraction: Sell stables to buy collateral
196-
// CollateralIn = (OraclePrice * StableReserve - CollateralReserve) / 2
197-
// StablesOut = CollateralIn / OraclePrice
197+
// StablesOut = (OraclePrice * StableReserve - CollateralReserve) / (OraclePrice + OraclePrice * (1 - incentive))
198+
// CollateralIn = StablesOut * OraclePrice
198199
uint256 numerator = ((oraclePrice * params.stableReserve) / 1e18) - params.collateralReserve;
199-
uint256 denominator = 2;
200+
uint256 denominator = oraclePrice + ((oraclePrice * (BPS_SCALE - incentive)) / BPS_SCALE);
200201

201-
uint256 collateralInRaw = numerator / denominator;
202-
uint256 stableOutRaw = (numerator * 1e18) / (denominator * oraclePrice);
202+
uint256 stableOutRaw = (numerator * 1e18) / denominator;
203+
uint256 collateralInRaw = (stableOutRaw * oraclePrice) / 1e18;
203204

204205
stableOut = stableOutRaw / params.stablePrecision;
205206
collateralIn = collateralInRaw / params.collateralPrecision;
@@ -214,16 +215,17 @@ contract ReserveLiquidityStrategy is LiquidityStrategy {
214215
*/
215216
function _calculateExpansionAmounts(
216217
RebalanceParams memory params,
217-
uint256 oraclePrice
218+
uint256 oraclePrice,
219+
uint256 incentive
218220
) private pure returns (uint256 collateralOut, uint256 stablesIn) {
219221
// Expansion: Sell collateral to buy stables
220-
// CollateralOut = (CollateralReserve - OraclePrice * StableReserve) / 2
222+
// CollateralOut = (CollateralReserve - OraclePrice * StableReserve) / (1 + 1 - incentive)
221223
// StablesIn = CollateralOut / OraclePrice
222224
uint256 numerator = params.collateralReserve - ((oraclePrice * params.stableReserve) / 1e18);
223-
uint256 denominator = 2;
225+
uint256 denominator = BPS_SCALE * 2 - incentive;
224226

225-
uint256 collateralOutRaw = numerator / denominator;
226-
uint256 stablesInRaw = (numerator * 1e18) / (denominator * oraclePrice);
227+
uint256 collateralOutRaw = (numerator * BPS_SCALE) / denominator;
228+
uint256 stablesInRaw = (collateralOutRaw * 1e18) / oraclePrice;
227229

228230
collateralOut = collateralOutRaw / params.collateralPrecision;
229231
stablesIn = stablesInRaw / params.stablePrecision;

script/DeployReserveFPMM.s.sol

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,10 @@ contract DeployReserveFPMM is Script {
6464
liquidityStrategy.initialize(reserve);
6565

6666
// Add pool to liquidity strategy
67-
liquidityStrategy.addPool(cUSDaxlUSDCFPMM, 600, 50);
67+
// conservative rebalance incentive until we fixed the precision errors
68+
liquidityStrategy.addPool(cUSDaxlUSDCFPMM, 600, 25);
6869

69-
USDm.initializeV2(address(deployer), address(liquidityStrategy));
70+
USDm.initializeV2(address(liquidityStrategy), address(deployer));
7071

7172
FPMM(cUSDaxlUSDCFPMM).setLiquidityStrategy(address(liquidityStrategy), true);
7273

test/unit/swap/ReserveLiquidityStrategy.t.sol

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ contract ReserveLiquidityStrategyTest is Test {
192192
uint256 stableReserveInPool = 1000e18; // S
193193
uint256 collateralReserveInPool = 1000e18; // C
194194
uint256 oraclePrice = 0.9e18; // P_oracle < 1e18 will make stable * (1-P) > 0
195-
uint256 poolPrice = 1.02e18; // P_pool > P_oracle + threshold
195+
uint256 poolPrice = 1e18; // P_pool > P_oracle + threshold
196196
uint256 thresholdBps = 100; // 1%
197197

198198
mockPool.setReserves(stableReserveInPool, collateralReserveInPool);
@@ -203,11 +203,13 @@ contract ReserveLiquidityStrategyTest is Test {
203203
collateralToken.mint(address(mockPool), 1000e18);
204204

205205
// Pre-calculate expected amounts with these values
206-
// Using formula: numerator = C - P * S = 1000e18 - 1000e18*0.9 = 100e18
207-
// collateralOut = numerator / 2 = 50e18
208-
// stablesIn = (collateralOut * 1e18) / P = (50e18 * 1e18) / 0.9 = 55.555...e18
209-
uint256 expectedCollateralOut = 50e18;
210-
uint256 expectedStablesIn = 55555555555555555555; // 55.555...e18
206+
// Using formula:
207+
// numerator = C - P * S = 1000e18 - 1000e18*0.9 = 100e18
208+
// denominator = 1 + 1 - incentive = 2 - 0.1 = 1.9
209+
// collateralOut = numerator / denominator = 100e18 / 1.9 = 50.251256281407035175
210+
// stablesIn = (collateralOut * 1e18) / P = 55.834729201563372416
211+
uint256 expectedCollateralOut = 50251256281407035175;
212+
uint256 expectedStablesIn = 55834729201563372416; // 55.834...
211213
uint256 incentiveAmount = (expectedStablesIn * DEFAULT_INCENTIVE) / 10_000;
212214

213215
// Record balances before rebalance
@@ -274,7 +276,7 @@ contract ReserveLiquidityStrategyTest is Test {
274276
uint256 stableReserveInPool = 1000e18; // S
275277
uint256 collateralReserveInPool = 950e18; // C < P * S (1000e18)
276278
uint256 oraclePrice = 1e18; // P_oracle
277-
uint256 poolPrice = 0.98e18; // P_pool < P_oracle - threshold
279+
uint256 poolPrice = 0.95e18; // P_pool < P_oracle - threshold
278280
uint256 thresholdBps = 100; // 1%
279281

280282
mockPool.setReserves(stableReserveInPool, collateralReserveInPool);
@@ -290,11 +292,13 @@ contract ReserveLiquidityStrategyTest is Test {
290292
uint256 reserveCollateralBefore = collateralToken.balanceOf(address(mockReserve));
291293

292294
// Calculate expected amounts
293-
// Using formula: numerator = P * S - C = 1e18*1000e18 - 950e18 = 50e18
294-
// collateralIn = numerator / 2 = 25e18
295-
// stableOut = collateralIn * 1e18 / P = 25e18 / 1e18 = 25e18
296-
uint256 expectedCollateralIn = 25e18;
297-
uint256 expectedStableOut = 25e18;
295+
// Using formula:
296+
// numerator = P * S - C = 1000e18 - 950e18 = 50e18
297+
// denominator = P + P * (1 - incentive) = 1e18 + 1e18 * (1 - 0.01) = 1.99e18
298+
// stableOut = numerator / denominator = 50e18 / 1.99 = 25.125628140703517587
299+
// collateralIn = stableOut * 1e18 / P = 25.125628140703517587
300+
uint256 expectedCollateralIn = 25125628140703517587;
301+
uint256 expectedStableOut = 25125628140703517587;
298302
uint256 incentiveAmount = (expectedCollateralIn * DEFAULT_INCENTIVE) / 10_000;
299303

300304
// Expect RebalanceInitiated event with calculated amounts
@@ -372,8 +376,9 @@ contract ReserveLiquidityStrategyTest is Test {
372376

373377
// numerator = C_scaled - (P_oracle * S / 1e18)
374378
uint256 numerator = collateralReserveInPool_scaled - (oraclePrice * stableReserveInPool) / 1e18;
379+
uint256 denominator = 10_000 * 2 - DEFAULT_INCENTIVE;
375380

376-
uint256 collateralOut_scaled = numerator / 2;
381+
uint256 collateralOut_scaled = (numerator * 10_000) / denominator;
377382
uint256 expectedCollateralOut = collateralOut_scaled / 1e12;
378383
uint256 expectedStablesIn = (collateralOut_scaled * 1e18) / oraclePrice;
379384
uint256 incentiveAmount = (expectedStablesIn * DEFAULT_INCENTIVE) / 10_000;

0 commit comments

Comments
 (0)