Overview
The aim of MU03 is to introduce three new trading pairs – cEUR/USDC, cBRL/USDC, and cEUR/EUROC – and to increase the trading limits on the existing pairs, as a next step towards fully deprecating Mento V1 exchanges.
In order to support those new pairs safely we improved our circuit breaker and our constant sum pricing calculation. For the circuit breaker, we introduced the idea of dependent rate feeds, i.e. a trading pair like cEUR/USDC will react to both EUR/USD and USDC/USD oracle rate movement that is outside of normal ranges. For the constant sum pricing logic, we updated it to allow for 1:1 swaps between assets with different exchange rates, as opposed to the initial implementation which was aimed at cUSD/USDC swaps.
The deployment of the new contracts was done by the mento team prior to creating the CGP and Ownership over all new implementations was transferred to Governance.
In preparation for this CGP, we tested the proposal on Alfajores and Baklava. Additionally, we’ve simulated the proposal on a fork of Celo mainnet and ran tests against the fork.
We can break down the CGP logically into the following sections, noted here with transaction indices in brackets:
- Add axlEUROC to the PartialReserve (0-1)
- Update implementation for existing proxies (2-4)
- Update BreakerBox references to the new instance (5-6)
- Configure the exchanges (7-12)
- Configure trading limits (13-19)
- Scale down Exchange reserve fractions(20-22)
- Configure BreakerBox (23-34)
- Configure the MedianDeltaBreaker (35-38)
- Configure the ValueDeltaBreaker (39-41)
- Whitelist Diwu as an oracle provider for EUROCEUR (42)
Now, let’s dive into each section of the CGP, and look at each transaction. We recommend following along with your celocli
output handy.
Some of the steps that follow require verifying that an address is correct, We’ve extracted what that entails into a separate section Appendix A: Verifying an address where you’ll find instructions for the different scenarios that come up, including how to verify contracts deployed in the preparation phase.
For the technically savvy, you can find everything we are describing here in our deployment tooling repository as code.
1. Add axlEUROC to the PartialReserve
As part of MU03, we introduce a new cEUR/EUROC exchange therefore we need to add bridged EUROC as a collateral asset to the PartialReserve and also configure the daily spending ratio.
TX#0 adds axlEUROC as a collateral asset to the PartialReserve
- Verify the PartialReserveProxy address
- Verify the axlEUROC address
TX#1 sets the daily spending ratio for axlEUROC to 100%. The value is represented as a fixed point number with 24 decimals, i.e. 1 is written as 1e24
- Verify the PartialReserveProxy address
- Verify the axlEUROC address
- Verify the ratio: 1e24 = 100%
2. Update implementation for existing proxies
In MU03, we’ve made a few changes to existing contracts, namely:
BiPoolManager – where we’ve changed some logic to support a value-based constant sum PricingModule.
Broker - where we’ve added additional validation checks on TradingLimits.
SortedOracles - where we’ve added an additional check of the on-chain circuit breakers on removing reports.
For the corresponding proxies, we need to update the implementations.
TX#2 upgrades BiPoolMangerProxy
- Verify the BiPoolMangerProxy address
- Verify the new implementation address
TX#3 upgrades BrokerProxy
- Verify the BrokerProxy address
- Verify the new implementation address
TX#4 upgrades SortedOraclesProxy
- Verify the SortedOraclesProxy address
- Verify the new implementation address
3. Update BreakerBox references to the new instance
As explained in the intro we’ve deployed a new BreakerBox so we need to update references in the contracts that interact with it.
TX#5 Set BreakerBox address in BiPoolManager
- Verify the BiPoolMangerProxy address
- Verify the BreakerBox address
TX#6 Set BreakerBox address in SortedOracles
- Verify the SortedOraclesProxy address
- Verify the BreakerBox address
4. Configure the exchanges
Since MU03 deploys an updated version of the ConstantSumPricingModule the current cUSD/axlUSDC exchange that is referencing the previous version needs to be destroyed and redeployed with an updated reference.
In order to prevent future exchanges from being created with a reference to an outdated Pricing Module we introduced a pricingModules
mapping in MU03. The mapping tracks the addresses of the current Pricing Module implementations and is checked when exchanges get created. This mapping needs to be filled with the current version of the ConstantSum and ConstantProduct Pricing Module before creating exchanges.
Also as part of MU03 three new exchanges are created, namely cEUR/axlUSDC, cBRL/axlUSDC, and cEUR/axlEUROC.
Verifying an exchange payload feels a bit complicated at first sight because the argument to the function is a solidity struct with a nested struct as well, which gets serialized verbosely in celocli
with all arguments both named and using indices. The structure also gets serialized both under args
and params
so something like 11 attributes are repeated 4 times in the outputs making it feel harder to parse. When reading the output from celocli
we recommend ignoring the args
field and focusing on the params
, and also ignoring the numbered fields and focusing only on the named fields.
To verify the exchange configuration is sane, check the docs BiPoolManager - Mento Protocol for an explanation of the structure.
TX#7 Set current Pricing Modules
- Verify the BiPoolManagerProxy address
- Verify ConstantSumPricingModule address
- Verify ConstantProductPricingModule address
TX#8 Destroy the cUSD/axlUSDC exchange
- Verify the BiPoolManagerProxy address
- Verify parameters:
- exchangeId: the deterministic id of the cUSD/axlUSDC exchange
- index: the index of the exchange in the BiPoolManager internal array
TX#9 Re-Create the cUSD/axlUSDC exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
TX#10 Create the cEUR/axlUSDC exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
TX#11 Create the cBRL/axlUSDC exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
TX#12 Create the cEUR/axlEUROC exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
5. Configure trading limits
In order to proceed with the transition from Mento V1 to Multi Collateral Mento the trading limits for existing exchanges are increased and trading limits for the new exchanges are set.
Similar to the previous section, the out of celocli
is too verbose when dealing with arguments and structs, so it’s recommended to ignore the args
field and focus only on params
and there ignore the numbered fields and focus on the named ones.
The TradingLimit.Config structure consists of these fields:
- timestep0: the time-window duration of L0
- timestep1: the time-window duration of L1
- limit0: the net flow limit of L0
- limit1: the net flow limit of L1
- limitGlobal: the net flow limit of LG
- flags: binary flags determining which limits are enabled possible values:
- 1 (or 0b001) - just L0
- 3 (or 0b011)- L0 and L1
- 5 (or 0b101) - L0 and LG
- 7 (or 0b111)- L0, L1 and LG
- to simplify some logic L1 can be enabled if and only if L0 is enabled, therefore 6 (0b110) is not a valid configuration
TX#13 Configure trading limits for the cUSD/CELO exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cUSD/CELO exchange
- token: which asset in the pair to target, in this case, the cUSD token address
- config: the trading limit configuration
TX#14 Configure trading limits for the cEUR/CELO exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cEUR/CELO exchange
- token: which asset in the pair to target, in this case, the cEUR token address
- config: the trading limit configuration
TX#15 Configure trading limits for the cREAL/CELO exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cREAL/CELO exchange
- token: which asset in the pair to target, in this case, the cREAL token address
- config: the trading limit configuration
TX#16 Configure trading limits for the cUSD/USDC exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cUSD/USDC exchange
- token: which asset in the pair to target, in this case, the cUSD token address
- config: the trading limit configuration
TX#17 Configure trading limits for the cEUR/USDC exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cEUR/USDC exchange
- token: which asset in the pair to target, in this case, the cEUR token address
- config: the trading limit configuration
TX#18 Configure trading limits for the cBRL/USDC exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cBRL/USDC exchange
- token: which asset in the pair to target, in this case, the cBRL token address
- config: the trading limit configuration
TX#19 Configure trading limits for the cEUR/EUROC exchange
- Verify Broker address
- Verify parameters:
- exchangeId: the deterministic id of the cEUR/EUROC exchange
- token: which asset in the pair to target, in this case, the cEUR token address
- config: the trading limit configuration
6. Scale down Exchange reserve fractions
Since we are increasing the feasible trading volume for Mento V2 we are going to continue scaling down Mento V1. This is done by decreasing the reserve fractions on the exchange contracts. In MU03 we are going to half them for all three exchanges.
TX#20 Reduce the reserve fraction of the cUSD Exchange to half of its current value.
- Verify ExchangeProxy address
- Verify new reserve fraction is half of its current value.
TX#21 Reduce the reserve fraction of the cBRL ExchangeBRL to half of its current value.
- Verify ExchangeBRLProxy address
- Verify new reserve fraction is half of its current value.
TX#22 Reduce the reserve fraction of the cEUR ExchangeEUR to half of its current value.
- Verify ExchangeEURProxy address
- Verify new reserve fraction is half of its current value.
7. Configure BreakerBox
As part of MU03, we made some changes to the BreakerBox contract. These include changing the meaning of different trading modes, introducing configurable dependencies between rate feeds, and making the BreakerBox Non-upgradable.
With this update, each breaker can set rate feeds into one trading mode that is configured when calling addBreaker. These trading modes are interpreted by the BiPoolManager.
Trading mode 0 ⇒ Bidirectional trading
Trading mode 1 ⇒ Inflow only
Trading mode 2 ⇒ Outflow only
Trading mode 3 ⇒ Trading halted
TX#23 adding all rate feeds to the BreakerBox:
- Verify that the BreakerBox address is correct
- Verify that the rate feed identifiers are correct:
- newRateFeedIDs an array of oracle feed ids
- CELO/USD - cUSD token address
- CELO/EUR - cEUR token address
- CELO/BRL - cREAL token address
- USDC/USD -
address(uint160(keccak256(“USDCUSD”)))
- USDC/EUR -
address(uint160(keccak256(“USDCEUR”)))
- USDC/BRL -
address(uint160(keccak256(“USDCBRL”)))
- EUROC/EUR -
address(uint160(keccak256(“EUROCEUR”)))
- newRateFeedIDs an array of oracle feed ids
TX#24 Add the MedianDeltaBreaker to the BreakerBox
- Verify the BreakerBox address
- Verify the MedianDeltaBreaker address
- Verify trading mode is set to 3
TX#25 Add the ValueDeltaBreaker to the BreakerBox
- Verify the BreakerBox address
- Verify the ValueDeltaBreaker address
- Verify trading mode is set to 3
TX#26 Set USDC/EUR rate feed dependency to USDC/USD
- Verify the BreakerBox address
- Verify the USDC/EUR rate feed identifier:
address(uint160(keccak256(“USDCEUR”)))
- Verify the USDC/USD rate feed identifier:
address(uint160(keccak256(“USDCUSD”)))
TX#27 Set USDC/BRL rate feed dependency to USDC/USD
- Verify the BreakerBox address
- Verify the USDC/BRL rate feed identifier:
address(uint160(keccak256(“USDCBRL”)))
- Verify the USDC/USD rate feed identifier:
address(uint160(keccak256(“USDCUSD”)))
TX#28 Enable the MedianDeltaBreaker on the CELO/USD rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the CELO/USD rate, i.e. the cUSD token address
- enable true
TX#29 Enable the MedianDeltaBreaker on the CELO/EUR rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the CELO/EUR rate, i.e. the cEUR token address
- enable true
TX#30 Enable the MedianDeltaBreaker on the CELO/BRL rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the CELO/BRL rate, i.e. the cREAL token address
- enable true
TX#31 Enable the MedianDeltaBreaker on the USDC/EUR rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the USDC/EUR rate,
address(uint160(keccak256(“USDCEUR”)))
- enable true
TX#32 Enable the MedianDeltaBreaker on the USDC/BRL rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the USDC/BRL rate,
address(uint160(keccak256(“USDCBRL”)))
- enable true
TX#33 Enable the ValueDeltaBreaker on the USDC/USD rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the ValueDeltaBreaker
- rateFeedID should be the identifier for the USDC/USD rate,
address(uint160(keccak256(“USDCUSD”)))
- enable true
TX#34 Enable the ValueDeltaBreaker on the EUROC/EUR rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the ValueDeltaBreaker
- rateFeedID should be the identifier for the EUROC/EUR rate,
address(uint160(keccak256(“EUROCEUR”)))
- enable true
8. Configure the MedianDeltaBreaker
This Upgrade makes some improvements to the MedianDeltaBreaker. The new MedianDeltaBreaker calculates an exponential moving average (EMA) over previous medians that is compared to the current median. When the difference between the two values exceeds a defined threshold the MedianDeltaBreaker breaks. The smoothing factor is the weight of the current median in the calculation of the next EMA.
Smoothing factor = 1 :
$=> new EMA = current median * 1 + previous EMA * 0$
If not set a default smoothing factor of 1 is used, which simply translates to using the current median as the reference value.
TX#35 Set the cooldown times on the MedianDeltaBreaker
- Verify the MedianDeltaBreaker address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- CELO/USD: the cUSD token address
- CELO/EUR: the cEUR token address
- CELO/BRL: the cBRL token address
- USDC/EUR:
address(uint160(keccak256(“USDCEUR”)))
- USDC/BRL:
address(uint160(keccak256(“USDCBRL”)))
- cooldownTimes: the array of cooldown times to be configured, the indices will match with the rateFeedIDs array. The values are seconds.
- CELO/USD: 30 minutes = 1800s
- CELO/EUR: 30 minutes = 1800s
- CELO/BRL: 30 minutes = 1800s
- USDC/EUR: 15 minutes = 900s
- USDC/BRL: 15 minutes = 900s
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
TX#36 Set the rate change thresholds on the MedianDeltaBreaker
- Verify the MedianDeltaBreaker address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- CELO/USD: the cUSD token address
- CELO/EUR: the cEUR token address
- CELO/BRL: the cBRL token address
- USDC/EUR:
address(uint160(keccak256(“USDCEUR”)))
- USDC/BRL:
address(uint160(keccak256(“USDCBRL”)))
- rateChangeThresholds: The array of rate change thresholds to be configured, the indices will match with the rateFeedIDs array. The values are fixed-point numbers – a number with 24 decimals, i.e. 1 is written as 1e24.
- CELO/USD: 3e22 = 3%
- CELO/EUR: 3e22 = 3%
- CELO/BRL: 3e22 = 3%
- USDC/EUR: 2e22 = ****2%
- USDC/BRL: 25e21 = ****2.5%
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
TX#37 Set the smoothing factor on the MedianDeltaBreaker for the USDC/EUR rate feed. The smoothing factor values are fixed-point numbers – a number with 24 decimals, i.e. 1 is written as 1e24.
- Verify the MedianDeltaBreaker address
- Verify the parameters:
- rateFeedID: USDC/EUR:
address(uint160(keccak256(“USDCEUR”)))
- newSmoothingFactor: 5e20 = 0.05%
- rateFeedID: USDC/EUR:
TX#38 Set the smoothing factor on the MedianDeltaBreaker for the USDC/BRL rate feed.
- Verify the MedianDeltaBreaker address
- Verify the parameters:
- rateFeedID: USDC/BRL:
address(uint160(keccak256(“USDCBRL”)))
- newSmoothingFactor: 5e20 = 0.05%
- rateFeedID: USDC/BRL:
9. Configure the ValueDeltaBreaker
ValueDeltaBreaker: verifies that a new median is within a percentage threshold of a configurable reference value. Since the ValueDeltaBreaker hasn’t changed in this update and it is already configured for the USDC/USD rate feed we only need to add the configuration for the new EUROC/EUR rate feed.
TX#39 Set the EUROC/EUR reference value for the ValueDeltaBreaker
- Verify the address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- EUROC/EUR:
address(uint160(keccak256(“EUROCEUR”)))
- EUROC/EUR:
- _referenceValues: the array of reference values to set, indices will match the rateFeedIDs array. The values are fixed point numbers – a number with 24 decimals, i.e. 1 is written as 1e24.
- EUROC/EUR: 1e24
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
TX#40 Set the EUROC/EUR cooldown time for the ValueDeltaBreaker
- Verify the address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- EUROC/EUR:
address(uint160(keccak256(“EUROCEUR”)))
- EUROC/EUR:
- cooldownTimes: the array of cooldown times to be configured, the indices will match with the rateFeedIDs array. The values are seconds.
- EUROC/EUR: 1
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
TX#41 Set the EUROC/EUR rate change percentage threshold for the ValueDeltaBreaker
- Verify the address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- EUROC/EUR:
address(uint160(keccak256(“EUROCEUR”)))
- EUROC/EUR:
- rateChangeThresholds: the array of rate change thresholds to be configured, the indices will match with the rateFeedIDs array. The values are fixed point numbers – a number with 24 decimals, i.e. 1 is written as 1e24.
- EUROC/EUR: 5e21 = 0.5%
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
9. Whitelist Diwu as an oracle provider for EUROCEUR
We’ve executed a preparatory CGP centered around Oracles previously but didn’t get one of the 3rd party oracles provided aligned in time – details here: https://github.com/celo-org/governance/blob/main/CGPs/cgp-0094.md#di-wu – so we’re also whitelisting them as an oracle provider for EUROCEUR as part of this CGP.
TX#42 Whitelist Diwu as an oracle provider for EUROCEUR:
- Verify SortedOracles proxy address
- Verify the parameters:
- rateFeedId: EUROC/EUR:
address(uint160(keccak256(“EUROCEUR”)))
- oracle address confirmed by Diwu:
0xBD136a625299A0ac5Ca7Ce9220aCA6e08a624e37
- rateFeedId: EUROC/EUR:
Appendix A: Verifying an address
Verifying that an address in the CGP is correct requires a few different strategies depending on what that address is. Here are common situations:
- Is it a rate feed identifier like:
address(uint160(keccack256(”USDCEUR”)))
- Use Keccak-256 Online to compute the keccak
- Take the last 40 characters (20 bytes) of the keccak and compare with the value
- Is it a Mento contract?
- Did it exist prior to MU01? (e.g. SortedOracles, StableToken, etc)
- Yes:
- It’s probably a proxy so you can check the addresses in the docs: Addresses - Mento Protocol
- You can also query the Registry contract on the target network
- No:
- New contracts can also be checked in the docs: Addresses - Mento Protocol
- Bytecode verification can be done by following the instructions here: https://docs.mento.org/mento/developers/deployments/verification
- Yes:
- Did it exist prior to MU01? (e.g. SortedOracles, StableToken, etc)
- Is it a Core Celo contract?
- Yes:
- It’s probably GoldToken which can be verified in the Celo Registry
- No:
- It’s axlUSDC which can be verified here: Axelar Documentation
- or axlEURC which wasn’t yet included in the docs but can be verified here: BurnableMintableCappedERC20 | Address 0x061cc5a2c863e0c1cb404006d559db18a34c762d | CeloScan
- Yes: