-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Rebase Circuit Breaker #294
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -98,6 +98,15 @@ contract UFragmentsPolicy is Ownable { | |||||
int256 public rebaseFunctionUpperPercentage; | ||||||
int256 public rebaseFunctionGrowth; | ||||||
|
||||||
// NOTE: This was added with v1.5 release, on-chain state will not | ||||||
// have the history going back to epoch(0). | ||||||
// Mapping between epoch and the supply at that epoch. | ||||||
mapping(uint256 => uint256) public supplyHistory; | ||||||
|
||||||
// Circuit breaker parameters which limit supply decline within the defined look back period. | ||||||
uint8 public epochLookback; | ||||||
int256 public tolerableDeclinePercentage; | ||||||
|
||||||
int256 private constant ONE = int256(10**DECIMALS); | ||||||
|
||||||
modifier onlyOrchestrator() { | ||||||
|
@@ -139,6 +148,8 @@ contract UFragmentsPolicy is Ownable { | |||||
|
||||||
uint256 supplyAfterRebase = uFrags.rebase(epoch, supplyDelta); | ||||||
assert(supplyAfterRebase <= MAX_SUPPLY); | ||||||
|
||||||
supplyHistory[epoch] = supplyAfterRebase; | ||||||
emit LogRebaseV2(epoch, exchangeRate, targetRate, supplyDelta); | ||||||
} | ||||||
|
||||||
|
@@ -206,7 +217,7 @@ contract UFragmentsPolicy is Ownable { | |||||
* @param minRebaseTimeIntervalSec_ More than this much time must pass between rebase | ||||||
* operations, in seconds. | ||||||
* @param rebaseWindowOffsetSec_ The number of seconds from the beginning of | ||||||
the rebase interval, where the rebase window begins. | ||||||
* the rebase interval, where the rebase window begins. | ||||||
* @param rebaseWindowLengthSec_ The length of the rebase window in seconds. | ||||||
*/ | ||||||
function setRebaseTimingParameters( | ||||||
|
@@ -222,6 +233,22 @@ contract UFragmentsPolicy is Ownable { | |||||
rebaseWindowLengthSec = rebaseWindowLengthSec_; | ||||||
} | ||||||
|
||||||
/** | ||||||
* @notice Sets the parameters which control rebase circuit breaker. | ||||||
* @param epochLookback_ The number of rebase epochs to look back. | ||||||
* @param tolerableDeclinePercentage_ The supply decline percentage which is allowed | ||||||
* within the defined look back period. | ||||||
*/ | ||||||
function setRebaseCircuitBreakerParameters( | ||||||
uint8 epochLookback_, | ||||||
int256 tolerableDeclinePercentage_ | ||||||
) external onlyOwner { | ||||||
require(tolerableDeclinePercentage_ > 0 && tolerableDeclinePercentage_ <= ONE); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I guess 0 "should" be an allowable value... It's not a useful config per se, but there's no mechanical reason the implementation wouldn't allow it. |
||||||
|
||||||
epochLookback = epochLookback_; | ||||||
tolerableDeclinePercentage = tolerableDeclinePercentage_; | ||||||
} | ||||||
|
||||||
/** | ||||||
* @notice A multi-chain AMPL interface method. The Ampleforth monetary policy contract | ||||||
* on the base-chain and XC-AmpleController contracts on the satellite-chains | ||||||
|
@@ -257,6 +284,9 @@ contract UFragmentsPolicy is Ownable { | |||||
lastRebaseTimestampSec = 0; | ||||||
epoch = 0; | ||||||
|
||||||
epochLookback = 0; | ||||||
tolerableDeclinePercentage = ONE; | ||||||
|
||||||
uFrags = uFrags_; | ||||||
} | ||||||
|
||||||
|
@@ -328,7 +358,8 @@ contract UFragmentsPolicy is Ownable { | |||||
* @return Computes the total supply adjustment in response to the exchange rate | ||||||
* and the targetRate. | ||||||
*/ | ||||||
function computeSupplyDelta(uint256 rate, uint256 targetRate) internal view returns (int256) { | ||||||
function computeSupplyDelta(uint256 rate, uint256 targetRate) public view returns (int256) { | ||||||
// No supply change if rate is within deviation threshold | ||||||
if (withinDeviationThreshold(rate, targetRate)) { | ||||||
return 0; | ||||||
} | ||||||
|
@@ -340,7 +371,27 @@ contract UFragmentsPolicy is Ownable { | |||||
rebaseFunctionUpperPercentage, | ||||||
rebaseFunctionGrowth | ||||||
); | ||||||
return uFrags.totalSupply().toInt256Safe().mul(rebasePercentage).div(ONE); | ||||||
|
||||||
int256 currentSupply = uFrags.totalSupply().toInt256Safe(); // (or) supplyHistory[epoch] | ||||||
brandoniles marked this conversation as resolved.
Show resolved
Hide resolved
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
I don't think the supplyHistory has been written to yet, in the core rebase flow. |
||||||
int256 newSupply = ONE.add(rebasePercentage).mul(currentSupply).div(ONE); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looking for it, but I don't see this change... Where did rebasePercentage move from being 1-centered to 0-centered? (We add one here but not in the previous version's line) |
||||||
|
||||||
// When supply is decreasing: | ||||||
// We limit the supply delta, based on recent supply history. | ||||||
if (rebasePercentage < 0 && epoch > epochLookback) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In the case that the supply history hasn't been written to yet, and is 0, then the below code basically no-ops? That's what it seems like. Do we have an explicit test case for this? i.e.
|
||||||
int256 allowedMin = supplyHistory[epoch - epochLookback] | ||||||
.toInt256Safe() | ||||||
.mul(ONE.sub(tolerableDeclinePercentage)) | ||||||
.div(ONE); | ||||||
if (allowedMin > currentSupply) { | ||||||
// NOTE: Allowed minimum supply can only be at most the current supply. | ||||||
allowedMin = currentSupply; | ||||||
} | ||||||
if (newSupply < allowedMin) { | ||||||
newSupply = allowedMin; | ||||||
} | ||||||
} | ||||||
|
||||||
return newSupply.sub(currentSupply); | ||||||
} | ||||||
|
||||||
/** | ||||||
|
@@ -349,11 +400,7 @@ contract UFragmentsPolicy is Ownable { | |||||
* @return If the rate is within the deviation threshold from the target rate, returns true. | ||||||
* Otherwise, returns false. | ||||||
*/ | ||||||
function withinDeviationThreshold(uint256 rate, uint256 targetRate) | ||||||
internal | ||||||
view | ||||||
returns (bool) | ||||||
{ | ||||||
function withinDeviationThreshold(uint256 rate, uint256 targetRate) public view returns (bool) { | ||||||
uint256 absoluteDeviationThreshold = targetRate.mul(deviationThreshold).div(10**DECIMALS); | ||||||
|
||||||
return | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.