Skip to content

Commit f806325

Browse files
committed
added dr eq range back
1 parent fe0d36c commit f806325

3 files changed

Lines changed: 68 additions & 15 deletions

File tree

spot-vaults/contracts/DRBalancerVault.sol

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ import { IERC20MetadataUpgradeable } from "@openzeppelin/contracts-upgradeable/t
1313
import { IPerpetualTranche } from "@ampleforthorg/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol";
1414
import { IRolloverVault } from "@ampleforthorg/spot-contracts/contracts/_interfaces/IRolloverVault.sol";
1515
import { ERC20Helpers } from "@ampleforthorg/spot-contracts/contracts/_utils/ERC20Helpers.sol";
16-
import { UnauthorizedCall, InvalidPerc } from "./_interfaces/errors/CommonErrors.sol";
16+
import { UnauthorizedCall, InvalidPerc, InvalidRange } from "./_interfaces/errors/CommonErrors.sol";
17+
import { Range } from "@ampleforthorg/spot-contracts/contracts/_interfaces/CommonTypes.sol";
1718
import { LastRebalanceTooRecent, SlippageTooHigh, InvalidLagFactor } from "./_interfaces/errors/DRBalancerErrors.sol";
1819

1920
/**
@@ -99,6 +100,10 @@ contract DRBalancerVault is
99100
/// @notice The target system deviation ratio (typically 1.0 = DR_ONE, using 8 decimals).
100101
uint256 public targetDR;
101102

103+
/// @notice The range of deviation ratios which define the equilibrium zone.
104+
/// @dev When the system's dr is within the equilibrium zone, no value is transferred during rebalance.
105+
Range public equilibriumDR;
106+
102107
/// @notice The lag factor for underlying->perp swaps (when DR is high).
103108
uint256 public lagFactorUnderlyingToPerp;
104109

@@ -198,6 +203,11 @@ contract DRBalancerVault is
198203
// Default configuration
199204
// Target DR is 1.0 (system in balance) with 8 decimals
200205
targetDR = DR_ONE;
206+
// Equilibrium DR range (skip rebalances when DR is within this window)
207+
equilibriumDR = Range({
208+
lower: (DR_ONE * 95) / 100,
209+
upper: (DR_ONE * 105) / 100
210+
});
201211
// Default lag factors
202212
lagFactorUnderlyingToPerp = 3;
203213
lagFactorPerpToUnderlying = 3;
@@ -216,10 +226,22 @@ contract DRBalancerVault is
216226
keeper = keeper_;
217227
}
218228

219-
/// @notice Updates the target system deviation ratio.
229+
/// @notice Updates the target system deviation ratio and equilibrium DR range together.
220230
/// @param targetDR_ The new target DR as a fixed point number with 8 decimals.
221-
function updateTargetDR(uint256 targetDR_) external onlyOwner {
231+
/// @param equilibriumDR_ The new equilibrium DR range as tuple of fixed-point numbers with {DR_DECIMALS} places.
232+
function updateTargetAndEquilibriumDR(
233+
uint256 targetDR_,
234+
Range memory equilibriumDR_
235+
) external onlyOwner {
236+
if (
237+
equilibriumDR_.lower >= equilibriumDR_.upper ||
238+
targetDR_ <= equilibriumDR_.lower ||
239+
targetDR_ >= equilibriumDR_.upper
240+
) {
241+
revert InvalidRange();
242+
}
222243
targetDR = targetDR_;
244+
equilibriumDR = equilibriumDR_;
223245
}
224246

225247
/// @notice Updates the lag factors for rebalancing.
@@ -528,6 +550,11 @@ contract DRBalancerVault is
528550
return (0, false);
529551
}
530552

553+
// Skip rebalancing if DR is within the equilibrium range.
554+
if (dr >= equilibriumDR.lower && dr <= equilibriumDR.upper) {
555+
return (0, false);
556+
}
557+
531558
// Determine direction, magnitude, and available liquidity for the swap
532559
// If DR < target: perpTVL is too high, redeem perps to decrease it
533560
// If DR > target: perpTVL is too low, mint perps to increase it

spot-vaults/test/DRBalancerVault.ts

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ describe("DRBalancerVault", function () {
7676

7777
// Target DR is 1.0 (system in balance) with 8 decimals
7878
expect(await vault.targetDR()).to.eq(DR_ONE);
79+
const eqRange = await vault.equilibriumDR();
80+
expect(eqRange[0]).to.eq(drFP("0.95"));
81+
expect(eqRange[1]).to.eq(drFP("1.05"));
7982

8083
expect(await vault.lagFactorUnderlyingToPerp()).to.eq(3);
8184
expect(await vault.lagFactorPerpToUnderlying()).to.eq(3);
@@ -106,22 +109,44 @@ describe("DRBalancerVault", function () {
106109
});
107110
});
108111

109-
describe("#updateTargetDR", function () {
112+
describe("#updateTargetAndEquilibriumDR", function () {
110113
describe("when triggered by non-owner", function () {
111114
it("should revert", async function () {
112115
const { vault } = await loadFixture(setupContracts);
113116
await vault.renounceOwnership();
114-
await expect(vault.updateTargetDR(DR_ONE)).to.be.revertedWith(
115-
"Ownable: caller is not the owner",
116-
);
117+
await expect(
118+
vault.updateTargetAndEquilibriumDR(DR_ONE, [drFP("0.9"), drFP("1.1")]),
119+
).to.be.revertedWith("Ownable: caller is not the owner");
120+
});
121+
});
122+
123+
describe("when target is outside range", function () {
124+
it("should revert", async function () {
125+
const { vault } = await loadFixture(setupContracts);
126+
await expect(
127+
vault.updateTargetAndEquilibriumDR(DR_ONE, [drFP("1.01"), drFP("1.1")]),
128+
).to.be.revertedWithCustomError(vault, "InvalidRange");
129+
});
130+
});
131+
132+
describe("when target equals a boundary", function () {
133+
it("should revert", async function () {
134+
const { vault } = await loadFixture(setupContracts);
135+
await expect(
136+
vault.updateTargetAndEquilibriumDR(DR_ONE, [DR_ONE, drFP("1.1")]),
137+
).to.be.revertedWithCustomError(vault, "InvalidRange");
117138
});
118139
});
119140

120141
describe("when valid", function () {
121-
it("should update target DR", async function () {
142+
it("should update target DR and equilibrium range", async function () {
122143
const { vault } = await loadFixture(setupContracts);
123-
await vault.updateTargetDR((DR_ONE * 110n) / 100n);
124-
expect(await vault.targetDR()).to.eq((DR_ONE * 110n) / 100n);
144+
const newTarget = drFP("1.02");
145+
await vault.updateTargetAndEquilibriumDR(newTarget, [drFP("0.98"), drFP("1.1")]);
146+
expect(await vault.targetDR()).to.eq(newTarget);
147+
const r = await vault.equilibriumDR();
148+
expect(r[0]).to.eq(drFP("0.98"));
149+
expect(r[1]).to.eq(drFP("1.1"));
125150
});
126151
});
127152
});

spot-vaults/test/DRBalancerVault_rebalance.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ describe("DRBalancerVault", function () {
8989
describe("#computeRebalanceAmount", function () {
9090
// Test Matrix:
9191
// - DR at target: returns 0
92+
// - DR within equilibrium range: returns 0
9293
// - DR below target: perp -> underlying swap (isUnderlyingIntoPerp = false)
9394
// - DR above target: underlying -> perp swap (isUnderlyingIntoPerp = true)
9495
// - Caps: minRebalanceVal threshold, availableLiquidity cap, requiredChange cap
@@ -101,8 +102,8 @@ describe("DRBalancerVault", function () {
101102
const [amt, isUnderlyingIntoPerp] =
102103
await vault.computeRebalanceAmount.staticCall();
103104
expect(amt).to.eq(0n);
104-
// When DR = targetDR, goes to else branch (dr >= targetDR), so isUnderlyingIntoPerp = true
105-
expect(isUnderlyingIntoPerp).to.eq(true);
105+
// When DR is within equilibrium range, no swap occurs and direction is false
106+
expect(isUnderlyingIntoPerp).to.eq(false);
106107
});
107108
});
108109

@@ -345,7 +346,7 @@ describe("DRBalancerVault", function () {
345346
await time.increase(DAY + 1);
346347
await expect(vault.rebalance())
347348
.to.emit(vault, "Rebalance")
348-
.withArgs(drFP("1"), drFP("1"), 0n, true);
349+
.withArgs(drFP("1"), drFP("1"), 0n, false);
349350
});
350351
});
351352

@@ -354,10 +355,10 @@ describe("DRBalancerVault", function () {
354355
const { vault, rolloverVault } = await loadFixture(setupWithBalancedLiquidity);
355356
await rolloverVault.mockMethod("deviationRatio()", [drFP("1")]);
356357

357-
// When DR = targetDR, isUnderlyingIntoPerp = true (dr >= targetDR branch)
358+
// When DR is within equilibrium range, no swap occurs and direction is false
358359
await expect(vault.rebalance())
359360
.to.emit(vault, "Rebalance")
360-
.withArgs(drFP("1"), drFP("1"), 0n, true);
361+
.withArgs(drFP("1"), drFP("1"), 0n, false);
361362
});
362363

363364
it("should update lastRebalanceTimestampSec", async function () {

0 commit comments

Comments
 (0)