Hello everyone!
For the last few months we at Mento Labs have been working together with Dunia as their tech partner for the launch of eXOF: a stable coin pegged to the CFA Franc.
This post outlines the transactions that will be part of the eXOF activation CGP to be proposed by Dunia. As with previous proposals, the Celo community will have the opportunity to verify them. In preparation for the upcoming CGP, we have done the following:
- Tested the proposal on Baklava and Alfajores
- Simulated the proposal on top of a fork of Celo Mainnet, in addition to running tests against the fork
Some pre-requisite work for the eXOF activation was already done in recent proposals:
- Addition of CELO/XOF, EUROC/XOF and EUR/XOF Oracles (CGP 94, CGP 98)
- Registration of the StableTokenProxyXOF contract in the Celo Registry (CGP 94)
The transactions in the upcoming CGP will be broken down into the following sections, noted here with transaction indices in brackets:
- Initialization of the eXOF stable token (0-7)
- Configure the new exchanges (8-9)
- Configure trading limits (10-13)
- Configure BreakerBox (14-19)
- Configure the MedianDeltaBreaker (20-21)
- Configure the ValueDeltaBreaker (22-24)
Before we dive deep into each section and transaction, we recommend you have celocli
set up to follow along. For the tech savvy, you can find everything we are describing here in our deployment tooling repository as code. For verifying that addresses included in the proposal are correct, please refer to the Appendix at the bottom for some handy tips.
1. Initialization of the eXOF stable token
This includes all the transactions related to registering eXOF as an official mento stable token: initializing the token contract, configuring its constitution parameters, adding it to the reserve, and whitelisting it as a gas currency.
TX#0 - initialize the eXOF proxy token contract and sets its implementation address
This proxy contract was deployed and verified on Celo mainnet, followed by its implementation contract. Both contracts ownership was transferred to governance immediately after. See CGP 94 for more.
- Verify the StableTokenXOFProxy address
- Verify the StableTokenXOF implementation address
- Verify the initialization parameters for the new stable token:
- name:
ECO CFA
- symbol:
eXOF
- decimals:
18
- registryAddress:
0x000000000000000000000000000000000000ce10
(the Celo registry address)
- inflationRate:
1e24
(not used in practice, but same as existing stables for consistency)
- inflationFactorUpdatePeriod:
47304000
(not used in practice, but same as existing stables for consistency)
- initialBalanceAddresses:
[]
(no pre-mint)
- initialBalanceValues:
[]
(no pre-mint)
- exchangeIdentifier:
Broker
TX#1 - set constitution parameters for setRegistry(address)
- Verify the GovernanceProxy address
- Verify the function selector:
bytes4(keccak256(bytes("setRegistry(address)")))
- Verify the threshold:
0.9 * 1e24
(same as existing stables)
TX#2 - set constitution parameters for setInflationParameters(uint256,uint256)
- Verify the GovernanceProxy address
- Verify the function selector:
bytes4(keccak256(bytes("setInflationParameters(uint256,uint256)")))
- Verify the threshold:
0.6 * 1e24
(same as existing stables)
TX#3 - set constitution parameters for transfer(address,uint256)
- Verify the GovernanceProxy address
- Verify the function selector:
bytes4(keccak256(bytes("transfer(address,uint256)")))
- Verify the threshold:
0.6 * 1e24
(same as existing stables)
TX#4 - set constitution parameters for transferWithComment(address,uint256,string)
- Verify the GovernanceProxy address
- Verify the function selector:
bytes4(keccak256(bytes("transferWithComment(address,uint256,string)")))
- Verify the threshold:
0.6 * 1e24
(same as existing stables)
TX#5 - set constitution parameters for approve(address,uint256)
- Verify the GovernanceProxy address
- Verify the function selector:
bytes4(keccak256(bytes("approve(address,uint256)")))
- Verify the threshold:
0.6 * 1e24
(same as existing stables)
TX#6 - add eXOF as a stable token to the PartialReserve
- Verify the PartialReserveProxy address
- Verify the eXOF proxy address
TX#7 - whitelist eXOF as a gas currency
- Verify the FeeCurrencyWhitelistProxy address
- Verify the eXOF proxy address
2. Configure the new exchanges
Verification tip: As in previous proposals, we recommend ignoring the args field from celocli and focusing on params, as well as ignoring the numbered fields and instead focusing only on the named fields. This is due to how nested structs are serialized by celocli, which might make the payload look confusing.
The pool configuration structures are documented here: BiPoolManager - Mento Protocol
TX#8 - create the eXOF/CELO exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
TX#9 - create the eXOF/axlEUROC exchange
- Verify the BiPoolManagerProxy address
- Verify the exchange configuration
3. Configure trading limits
Similar to the previous section, the output of celocli is too verbose when dealing with arguments and structs, so it’s recommended to ignore args and numbered fields and to focus instead on params and named fields.
The TradingLimit.Config structure is documented here: TradingLimits - Mento Protocol
TX#10 - configure trading limits on eXOF for the eXOF/CELO exchange
- Verify Broker address
- Verify the arguments:
- exchangeId: the deterministic id of the eXOF/CELO exchange
- token: which asset in the pair to target, in this case, the eXOF token address
- config: the trading limit configuration
TX#11 - Configure trading limits on CELO for the eXOF/CELO exchange
- Verify Broker address
- Verify the arguments:
- exchangeId: the deterministic id of the eXOF/CELO exchange
- token: which asset in the pair to target, in this case, the CELO token address
- config: the trading limit configuration
TX#12 - Configure trading limits on eXOF for the eXOF/axlEUROC exchange
- Verify Broker address
- Verify the arguments:
- exchangeId: the deterministic id of the eXOF/axlEUROC exchange
- token: which asset in the pair to target, in this case, the eXOF token address
- config: the trading limit configuration
TX#13 - Configure trading limits on axlEUROC for the eXOF/axlEUROC exchange
- Verify Broker address
- Verify the arguments:
- exchangeId: the deterministic id of the eXOF/axlEUROC exchange
- token: which asset in the pair to target, in this case, the axlEUROC token address
- config: the trading limit configuration
4. Configure BreakerBox
TX#14 - adding all rate feeds to the BreakerBox:
- Verify the BreakerBox address
- Verify the arguments:
- newRateFeedIDs an array of oracle feed ids
- EUR/XOF -
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/XOF -
address(uint160(uint256(keccak256("EUROCXOF"))))
- CELO/XOF - the eXOF token address
TX#15 - set CELO/XOF rate feed dependencies to EUR/XOF and EUROC/XOF
- Verify the BreakerBox address
- Verify the arguments
- rateFeedID: the identifier for the CELO/XOF rate, i.e. the eXOF token address
- dependencies: an array of oracle rate feed ids
- EUR/XOF -
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/XOF -
address(uint160(uint256(keccak256("EUROCXOF"))))
TX#16 - set EUROC/XOF rate feed dependencies to EUR/XOF and EUROC/EUR
- Verify the BreakerBox address
- Verify the arguments
- rateFeedID: the identifier for the EUROC/XOF rate,
address(uint160(uint256(keccak256("EUROCXOF"))))
- dependencies: an array of oracle rate feed ids
- EUR/XOF -
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/EUR -
address(uint160(uint256(keccak256("EUROCEUR"))))
TX#17 - enable the MedianDeltaBreaker on the CELO/XOF rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the CELO/XOF rate, i.e. the eXOF token address
- enable true
TX#18 - enable the ValueDeltaBreaker on the EUR/XOF rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the EUR/XOF rate,
address(uint160(uint256(keccak256("EURXOF"))))
- enable true
TX#19 - Enable the ValueDeltaBreaker on the EUROC/XOF rate feed
- Verify the BreakerBox address
- Verify the arguments:
- breakerAddress should be the MedianDeltaBreaker
- rateFeedID should be the identifier for the EUROC/XOF rate,
address(uint160(uint256(keccak256("EUROCXOF"))))
- enable true
5. Configure the MedianDeltaBreaker
TX#20 - set the cooldown time on the MedianDeltaBreaker for the CELO/XOF rate feed
- Verify the MedianDeltaBreaker address
- Verify the arguments:
- rateFeedIDs: the array of rate feeds to configure:
- CELO/XOF: the eXOF token address
- cooldownTimes: the array of cooldown times to be configured per rate feed. The values are in seconds.
- CELO/XOF: 30 minutes = 1800s
TX#21 - set the rate change thresholds on the MedianDeltaBreaker for the CELO/XOF rate feed
- Verify the MedianDeltaBreaker address
- Verify the arguments:
- rateFeedIDs: the array of rate feeds to configure:
- CELO/XOF: the eXOF token address
- rateChangeThresholds: The array of rate change thresholds to be configured per rate feed. The values are fixed-point numbers – a number with 24 decimals, i.e. 1 is written as 1e24.
6. Configure the ValueDeltaBreaker
During the initial launch, a non-recoverable ValueDeltaBreaker will be configured on the EUR/XOF rate feed. When triggered, it will disable the eXOF exchanges and require manual governance intervention in order to re-enable them. This breaker will only trigger if the EUR/XOF exchange rate changes more than 10% from the official exchange rate to the EUR: €1 = F.CFA 655.957.
TX#22 - set the cooldown times on the ValueDeltaBreaker for the EUR/XOF and EUROC/XOF rate feeds
- Verify the ValueDeltaBreaker address
- Verify the arguments:
- rateFeedIDs: the array of rate feeds to configure. Should contain the rate feed identifiers for:
- EUR/XOF
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/XOF
address(uint160(uint256(keccak256("EUROCXOF"))))
- cooldownTimes: the array of cooldown times to be configured, the indices will match with the rateFeedIDs array. The values are seconds.
- EUR/XOF: 0 (unrecoverable breaker which requires manual intervention)
- EUROC/XOF: 15 minutes = 900s
TX#23 - set the rate change thresholds on the ValueDeltaBreaker for the EUR/XOF and EUROC/XOF rate feeds
- Verify the ValueDeltaBreaker address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- EUR/XOF -
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/XOF -
address(uint160(uint256(keccak256("EUROCXOF"))))
- 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.
- EUR/XOF: 1e23 = 10%
- EUROC/XOF: 5e21 = 0.5%
TX#24 - set the rate reference value on the ValueDeltaBreaker for the EUR/XOF and EUROC/XOF rate feeds
- Verify the ValueDeltaBreaker address
- Verify the parameters:
- rateFeedIDs: the array of rate feeds to configure, should contain the rate feed identifiers for:
- EUR/XOF -
address(uint160(uint256(keccak256("EURXOF"))))
- EUROC/XOF -
address(uint160(uint256(keccak256("EUROCXOF"))))
- _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.
- EUR/XOF: 6.55957e26 =
655.957 * 10 ** 24
- EUROC/XOF: 6.55957e26 =
655.957 * 10 ** 24
Appendix: Verifying an address
Verifying that an address in the CGP is correct requires a few different strategies depending on what that address is (see the mento docs for some additional information). Here are common situations:
- Is it a rate feed identifier like:
address(uint160(keccack256(”USDCEUR”)))
- Use Keccak-256 Online 2 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 1
- You can also query the Registry contract on the target network
- No:
- Is it a Core Celo contract?
- Yes:
- It’s probably GoldToken or FeeCurrencyWhiteList which can be both verified in the Celo Registry
- No:
One more thing
Just kidding, that’s it! We are very excited to have partnered with Dunia and look forward to the use cases that eXOF will enable. If you would like to deploy a stable token on top of the Mento Platform we would love to hear from you as well. Please join our discord and reach out.