This post aims to explain how Ampleforth’s rebase operation is implemented. While the whitepaper provides an overview of the different components involved, it misses some implementation details.
I hope the post helps community members to gain a better understanding of the underlying details. Please comment if things are unclear, missing, or faulty.
The Rebase Operation
The rebase operation is an atomically executed adjustment to AMPL’s supply and user balances according to a discretional rule set.
Overview
1. A User initiates the Rebase Operation
To initiate the rebase a user needs to call the rebase
function in the Orchestrator
contract. The intention behind the Orchestrator
is to coordinate AMPL’s supply change with external contracts. This becomes important later in step 7.
It is also worth mentioning that the Orchestrator
’s rebase
function fails if its called by a contract, meaning only EOAs (Externally Owned Contracts) can initiate a rebase. This makes it harder to atomically sandwich-trade AMPL’s supply adjustment.
2. The Orchestrator
calls the MonetaryPolicy
’s rebase
function
Ampleforth’s monetary policy is rules-based, i.e. supply adjustments are done algorithmically. These rules are implemented inside the MonetaryPolicy
contract.
The first rule enforced is that the rebase
is only allowed to happen once during a reoccurring time window. Currently, the time window is set to 20 minutes and opens every 24 hours.
3. The MonetaryPolicy
fetches the Target and Market Rate from the Oracles
In order to compute AMPL’s supply delta, the policy needs to know AMPL’s current target and market rate. These values are fetched from two different instances of the same MedianOracle
contract.
A MedianOracle
is a contract in which a set of authorized providers
are allowed to push reports
. Each report becomes only valid after some time passed (the reportDelaySec
) and invalid again after some more time (the reportExpirationTimeSec
).
Upon request, the MedianOracle
returns the median of the averages of valid reports for each provider.
Currently, the value pushed to the MedianOracle
the MonetaryPolicy
queries as the target rate is the 2019 CPI value. For the market rate MedianOracle
, AMPL’s VWAP (Volume Weighted Average Price) of the last 24 hours is pushed by the providers.
For more info about the oracle’s providers, see for example this discussion.
4. The MonetaryPolicy
computes AMPL’s supplyDelta
Having AMPL’s target and market rate, the MonetaryPolicy
computes how much AMPL’s supply has to change. This supplyDelta
can be either positive (expansion) or negative (contraction).
For more info about the mathematical details, see the AIP-5 discussion.
5. The MonetaryPolicy
calls AMPL
’s rebase
function
The AMPL ERC20
’s rebase
function receives the computed supplyDelta
as an argument. The function must be only callable by the MonetaryPolicy
contract.
6. AMPL’s rebase scalar
is updated
To update all user balances at once, the AMPL token uses an internal, fixed (i.e. non-rebasing) user balance and “simulates” an external user balance (the AMPL balance in your wallet) by multiplying the fixed balance with some number called rebase scalar
.
Changing AMPL’s supply, that is the sum of all external user balances (isn’t it? ), only involves updating the rebase scalar
with which the external balances are calculated.
This implementation ensures the supply adjustment is always independent of the number of user balances!
Furthermore, it is guaranteed that a supply adjustment is always non-dilutive. (This is an interesting property, meaning even the MonetaryPolicy
can not introduce any Cantillon effect)
7. The Orchestrator
calls external Contracts to help Coordinate the Supply Adjustment
Now comes the job of the Orchestrator
contract.
The Orchestrator
manages a set of transactions that get executed directly after AMPL’s supply adjustment.
Two of the transactions currently included are:
- Adjusting Balancer’s AMPL-USDC smart pool ratio. For more info see this post.
- Call UniswapV2’s
sync
function for AMPL pools to force the Uniswap pools to update its cached balance of AMPL tokens inside the pool. Without calling this function, Uniswap’sx * y = k
property would operate on an outdated AMPL balance.
If any of the function calls fail, the whole rebase operation is reverted. Furthermore, every transaction added to the Orchestrator
increases the gas costs to execute a rebase operation. It should also be noted that the transactions must not depend on each other because the order in which they are executed is random.
One policy of the ForthDAO should therefore be to not blow the Orchestrator
with transactions but rather integrate external contracts by other methods, e.g. wrapped AMPL.