@@ -98,6 +98,15 @@ contract UFragmentsPolicy is Ownable {
98
98
int256 public rebaseFunctionUpperPercentage;
99
99
int256 public rebaseFunctionGrowth;
100
100
101
+ // NOTE: This was added with v1.5 release, on-chain state will not
102
+ // have the history going back to epoch(0).
103
+ // Mapping between epoch and the supply at that epoch.
104
+ mapping (uint256 => uint256 ) public supplyHistory;
105
+
106
+ // Circuit breaker parameters which limit supply decline within the defined look back period.
107
+ uint8 public epochLookback;
108
+ int256 public tolerableDeclinePercentage;
109
+
101
110
int256 private constant ONE = int256 (10 ** DECIMALS);
102
111
103
112
modifier onlyOrchestrator () {
@@ -139,6 +148,8 @@ contract UFragmentsPolicy is Ownable {
139
148
140
149
uint256 supplyAfterRebase = uFrags.rebase (epoch, supplyDelta);
141
150
assert (supplyAfterRebase <= MAX_SUPPLY);
151
+
152
+ supplyHistory[epoch] = supplyAfterRebase;
142
153
emit LogRebaseV2 (epoch, exchangeRate, targetRate, supplyDelta);
143
154
}
144
155
@@ -206,7 +217,7 @@ contract UFragmentsPolicy is Ownable {
206
217
* @param minRebaseTimeIntervalSec_ More than this much time must pass between rebase
207
218
* operations, in seconds.
208
219
* @param rebaseWindowOffsetSec_ The number of seconds from the beginning of
209
- the rebase interval, where the rebase window begins.
220
+ * the rebase interval, where the rebase window begins.
210
221
* @param rebaseWindowLengthSec_ The length of the rebase window in seconds.
211
222
*/
212
223
function setRebaseTimingParameters (
@@ -222,6 +233,22 @@ contract UFragmentsPolicy is Ownable {
222
233
rebaseWindowLengthSec = rebaseWindowLengthSec_;
223
234
}
224
235
236
+ /**
237
+ * @notice Sets the parameters which control rebase circuit breaker.
238
+ * @param epochLookback_ The number of rebase epochs to look back.
239
+ * @param tolerableDeclinePercentage_ The maximum supply decline percentage which is allowed
240
+ * within the defined look back period.
241
+ */
242
+ function setRebaseCircuitBreakerParameters (
243
+ uint8 epochLookback_ ,
244
+ int256 tolerableDeclinePercentage_
245
+ ) external onlyOwner {
246
+ require (tolerableDeclinePercentage_ > 0 && tolerableDeclinePercentage_ <= ONE);
247
+
248
+ epochLookback = epochLookback_;
249
+ tolerableDeclinePercentage = tolerableDeclinePercentage_;
250
+ }
251
+
225
252
/**
226
253
* @notice A multi-chain AMPL interface method. The Ampleforth monetary policy contract
227
254
* on the base-chain and XC-AmpleController contracts on the satellite-chains
@@ -257,6 +284,9 @@ contract UFragmentsPolicy is Ownable {
257
284
lastRebaseTimestampSec = 0 ;
258
285
epoch = 0 ;
259
286
287
+ epochLookback = 0 ;
288
+ tolerableDeclinePercentage = ONE;
289
+
260
290
uFrags = uFrags_;
261
291
}
262
292
@@ -328,7 +358,8 @@ contract UFragmentsPolicy is Ownable {
328
358
* @return Computes the total supply adjustment in response to the exchange rate
329
359
* and the targetRate.
330
360
*/
331
- function computeSupplyDelta (uint256 rate , uint256 targetRate ) internal view returns (int256 ) {
361
+ function computeSupplyDelta (uint256 rate , uint256 targetRate ) public view returns (int256 ) {
362
+ // No supply change if rate is within deviation threshold
332
363
if (withinDeviationThreshold (rate, targetRate)) {
333
364
return 0 ;
334
365
}
@@ -340,7 +371,28 @@ contract UFragmentsPolicy is Ownable {
340
371
rebaseFunctionUpperPercentage,
341
372
rebaseFunctionGrowth
342
373
);
343
- return uFrags.totalSupply ().toInt256Safe ().mul (rebasePercentage).div (ONE);
374
+
375
+ int256 currentSupply = uFrags.totalSupply ().toInt256Safe (); // (or) supplyHistory[epoch]
376
+ int256 newSupply = ONE.add (rebasePercentage).mul (currentSupply).div (ONE);
377
+
378
+ // When supply is decreasing:
379
+ // We limit the supply delta, based on recent supply history.
380
+ if (rebasePercentage < 0 ) {
381
+ int256 maxSupplyInHistory = currentSupply;
382
+ for (uint8 i = 1 ; i < epochLookback && epoch > i; i++ ) {
383
+ int256 epochSupply = supplyHistory[epoch - i].toInt256Safe ();
384
+ if (epochSupply > maxSupplyInHistory) {
385
+ maxSupplyInHistory = epochSupply;
386
+ }
387
+ }
388
+ int256 allowedSupplyMinimum = maxSupplyInHistory
389
+ .mul (ONE.sub (tolerableDeclinePercentage))
390
+ .div (ONE);
391
+ newSupply = (newSupply > allowedSupplyMinimum) ? newSupply : allowedSupplyMinimum;
392
+ require (newSupply <= currentSupply);
393
+ }
394
+
395
+ return newSupply.sub (currentSupply);
344
396
}
345
397
346
398
/**
@@ -349,11 +401,7 @@ contract UFragmentsPolicy is Ownable {
349
401
* @return If the rate is within the deviation threshold from the target rate, returns true.
350
402
* Otherwise, returns false.
351
403
*/
352
- function withinDeviationThreshold (uint256 rate , uint256 targetRate )
353
- internal
354
- view
355
- returns (bool )
356
- {
404
+ function withinDeviationThreshold (uint256 rate , uint256 targetRate ) public view returns (bool ) {
357
405
uint256 absoluteDeviationThreshold = targetRate.mul (deviationThreshold).div (10 ** DECIMALS);
358
406
359
407
return
0 commit comments