Launch new Mento Stablecoins [JPY, GBP, AUD, CAD, CHF, NGN, ZAR]

Receiver Entity: Celo Governance
Status: DRAFT
Title: Launch new Mento Stablecoins [JPY, GBP, AUD, CAD, CHF, NGN, ZAR]
Author(s): @MichaelKwan
Type of Request: Mento Proposal
Funding Request: N/A

Summary

On behalf of Stabila Foundation, Mento Labs, and the broader Celo community, this proposal calls for Mento to launch seven new local currency stablecoins for G7 and key African markets: JPY, GBP, AUD, CAD, CHF, NGN, and ZAR. These stablecoins can enhance on-chain FX use cases and deepen market adoption, complementing Mento’s existing stablecoin portfolio (cKES, cGHS, eXOF, cUSD, cEUR, cREAL, cCOP, PUSO). Notably, NGN and ZAR could strengthen adoption in two of Africa’s largest Pan-African markets.

By supporting this proposal, the Celo community grants pre-approval for Mento Labs to proceed with these stablecoin launches, subject to Mento Lab’s technical, feasibility, and regulatory assessments.

  • Disclaimer: The Stabila Foundation is not issuing, managing, or guaranteeing these stablecoins and assumes no liability for their implementation or compliance

Motivation

After USD and EUR (which are already live on Mento), JPY, GBP, AUD, CAD, CHF, are the next most widely traded currencies globally, with the deepest liquidity. According to BIS’ Triennial Central Bank Survey (2022), USD/JPY and GBP/USD are the 2nd and 3rd most traded pairs, accounting for ~23% of the global daily turnover, followed only by USD/EUR. The rest of these currencies make up the remainder of the top 10 most traded pairs globally. Organic swap volumes for these pairs are expected to be strong from day one.

Africa has unique dynamics associated with these currencies and international trade. High transaction costs and forex risks often lead businesses and traders to rely on informal markets or stablecoins (like USDT) to mitigate high spreads and banking fees. Mento can address these pain points by launching NGN and ZAR stablecoins, driving deeper blockchain adoption and stablecoin usage in the Mento and Celo ecosystems.

Collectively, these local currency stablecoins can power key on-chain FX use cases such as cross-border (B2B) payments, remittances, converting USD savings into local currency, providing credit in local currency, and enabling FX hedging for businesses and individuals

Specification

Top Currency Pairs

Source: BIS Triennial Central Bank Survey (2022)

Africa’s Most Traded Currencies

Sources: FXempire

Metrics and KPIs

  • Total Circulation – Primary indicator of demand for each new stablecoin.
  • Gross Trading Volume
  • Unique Wallets Holding the Stables

Current Status

This is a new proposal. As a reference point, Mento’s most recent local currency stablecoin launch was cGHS.

Timeline and Milestones

Upon approval, Mento Labs will evaluate each stablecoin’s feasibility, regulatory considerations, and liquidity requirements before proceeding with implementation. Mento will also provide an updated roadmap, with a target launch for all seven stablecoins by the end of H1 2025.

Detailed Budget

N/A

Payment Terms

N/A

Team

Stabila Foundation

A community-funded initiative on Celo that supports stablecoin adoption and real-world use cases within the Celo ecosystem. Stabila is facilitating this proposal to encourage community discussion and decision on expanding Mento’s stablecoin offerings.

Mento Labs

The team responsible for the code and infrastructure supporting stablecoins on the Mento platform (e.g., cGHS).

Additional Support/Resources

N/A

11 Likes

Hey folks, the first proposal to enable the needed oracles for the launch of the following new Mento stablecoins ($cGBP, $cAUD, $cCAD, $cCHF, and $cZAR) will be submitted on-chain soon.

Chainlink oracles for JPY and NGN are not yet ready, so those will be whitelisted in a future proposal. Proposals for the official launch of the different tokens will also follow soon.

This post provides an overview of the transactions to whitelist the Chainlink relayer contracts that will report the rates needed for these new stablecoins. As with previous proposals, the Celo community will have the opportunity to verify these transactions. In preparation for the upcoming CGP, we have completed the following steps:

  • Tested the proposal on Alfajores
  • Simulated the proposal on a fork of Celo Mainnet and conducted tests against this fork

Before we dive into the details of each section and transaction, we recommend setting up celocli to follow along. For those technically inclined, you can find everything described here in our deployment tooling repository. For verifying the addresses included in the proposal, please refer to the Appendix at the bottom for helpful tips.

1. Whitelisting and configuring of Chainlink Relayers in SortedOracles

The ChainlinkRelayer transmits rate feed data from a single Chainlink price feed or an aggregation of multiple Chainlink price feeds to the SortedOracles contract.

This contract can aggregate multiple price feeds to provide derived rate feeds to the protocol. This is necessary because it is more efficient for Oracle providers to report FX rates against the dollar and crypto-asset rates against the dollar, rather than providing all possible combinations.

For example, Chainlink reports the GBP/USD rate, but it does not directly provide the CELO/GBP rate, which is needed for gas payments in a GBP stable token. However, by using both the GBP/USD and CELO/USD rates, the following path can be derived:

CELO/USD * inverse(GBP/USD) = CELO/GBP.

A separate ChainlinkRelayer instance will be deployed for each required rate feed.

Each token requires a total of 5 transactions, and they all follow the same pattern:

  1. Whitelist the CELO/XXX relayer (for gas payments purposes)
  2. Set the report expiry time for the CELO/XXX pair
  3. Whistelist the XXX/USD relayer
  4. Set the report expiry time for the XXX/USD pair
  5. Set the equivalent token for XXX to be CELO/XXX (for gas payments purposes)

AUD Relayers

TX#0 - whitelist CELO/AUD Relayer in SortedOracles - addOracle()

  • Verify the SortedOracleProxy address
  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOAUD")))));
    • Should match: 0x1aA86eAd81936a1E9707c6B4A7AEfb2B4A538B58
  • Verify the ChainlinkRelayer address: 0x50DA4b658076B86970EC6e6650823B4A24E7026f

TX#1 - configure CELO/AUD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x1aA86eAd81936a1E9707c6B4A7AEfb2B4A538B58
  • Verify Report Expiry Seconds: 360 (6 minutes)

TX#2 - whitelist AUD/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:AUDUSD")))));
    • Should match: 0x646bD504C3864Ea5b8A6B6D25743721f61864A07
  • Verify the ChainlinkRelayer address: 0xA8869Bb55d1082D12c8F993A56cE8D050551a3d9

TX#3 - Configure AUD/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x646bD504C3864Ea5b8A6B6D25743721f61864A07
  • Verify Report Expiry Seconds: 360

TX#4 - Configure equivalent token for AUD rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0x7175504C455076F15c04A2F90a8e352281F492F9 (StableTokenAUDProxy)
  • Verify the Rate Feed Identifier: 0x1aA86eAd81936a1E9707c6B4A7AEfb2B4A538B58

CAD Relayers

TX#5 - whitelist CELO/CAD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOCAD")))));
    • Should match: 0x15339E57E761F006834893CD5134138339e7bfCb
  • Verify the ChainlinkRelayer address: 0x98aB92521fd13026292Cb6B31229ADf3B60fAE56

TX#6 - Configure CELO/CAD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x15339E57E761F006834893CD5134138339e7bfCb
  • Verify Report Expiry Seconds: 360

TX#7 - whitelist CAD/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CADUSD")))));
    • Should match: 0x20869cF54Ead821C45DFb2aB0C23d2e10Fbb65A4
  • Verify the ChainlinkRelayer address: 0x3a7af4E6f53ac13BC9e67Cb6ed5866d855692390

TX#8 - Configure CAD/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x20869cF54Ead821C45DFb2aB0C23d2e10Fbb65A4
  • Verify Report Expiry Seconds: 360

TX#9 - Configure equivalent token for CAD rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0xff4Ab19391af240c311c54200a492233052B6325 (StableTokenCADProxy)
  • Verify the Rate Feed Identifier: 0x15339E57E761F006834893CD5134138339e7bfCb

CHF Relayers

TX#10 - whitelist CELO/CHF Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOCHF")))));
    • Should match: 0xD808031cC050CFC81e7609156002361af6a579A6
  • Verify the ChainlinkRelayer address: 0x09B310c2D4b0CDCE563762C0e3992e352Cacdda6

TX#11 - configure CELO/CHF token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xD808031cC050CFC81e7609156002361af6a579A6
  • Verify Report Expiry Seconds: 360

TX#12 - whitelist CHF/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CHFUSD")))));
    • Should match: 0x0f61BA9c30ef7CaEE7E5CC1F96BFFCb0f52ccD64
  • Verify the ChainlinkRelayer address: 0x1b904277b22cA598ef17b38f64De5F9C29cd31BD

TX#13 - Configure CHF/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x0f61BA9c30ef7CaEE7E5CC1F96BFFCb0f52ccD64
  • Verify Report Expiry Seconds: 360

TX#14 - configure equivalent token for CHF rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0xb55a79F398E759E43C95b979163f30eC87Ee131D (StableTokenCHFProxy)
  • Verify the Rate Feed Identifier: 0xD808031cC050CFC81e7609156002361af6a579A6

GBP Relayers

TX#15 - whitelist CELO/GBP Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOGBP")))));
    • Should match: 0x6732fEF1b6EE8003A06a3D7eECFF1a36550CFDF5
  • Verify the ChainlinkRelayer address: 0x3E3e3E04a4d4654042CB7f0efe10DeF73Fda6223

TX#16 - configure CELO/GBP token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x6732fEF1b6EE8003A06a3D7eECFF1a36550CFDF5
  • Verify Report Expiry Seconds: 360

TX#17 - whitelist GBP/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:GBPUSD")))));
    • Should match: 0xf590b62f9cfcc6409075b1ecAc8176fe25744B88
  • Verify the ChainlinkRelayer address: 0x215d3ba962597DeFb38Da439ED4dB8E8a63e409a

TX#18 - configure GBP/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xf590b62f9cfcc6409075b1ecAc8176fe25744B88
  • Verify Report Expiry Seconds: 360

TX#19 - configure equivalent token for GBP rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0xCCF663b1fF11028f0b19058d0f7B674004a40746 (StableTokenGBPProxy)
  • Verify the Rate Feed Identifier: 0x6732fEF1b6EE8003A06a3D7eECFF1a36550CFDF5

ZAR Relayers

TX#20 - whitelist CELO/ZAR Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOZAR")))));
    • Should match: 0xD064b6CcFF2AE8968bA6725e9A92f3F0431bf5D0
  • Verify the ChainlinkRelayer address: 0x28EFfAbD76589Dd822F41e79C965c74Ab9d27160

TX#21 - configure CELO/ZAR token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xD064b6CcFF2AE8968bA6725e9A92f3F0431bf5D0
  • Verify Report Expiry Seconds: 360

TX#22 - whitelist ZAR/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:ZARUSD")))));
    • Should match: 0x17ef04Af0c52465694a841552fc2415169b1114c
  • Verify the ChainlinkRelayer address: 0x4FF9042aF59AF2B507b9423bE385f664FF87F7af

TX#23 - Configure ZAR/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0x17ef04Af0c52465694a841552fc2415169b1114c
  • Verify Report Expiry Seconds: 360

TX#24 - Configure equivalent token for ZAR rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0x4c35853A3B4e647fD266f4de678dCc8fEC410BF6 (StableTokenZARProxy)
  • Verify the Rate Feed Identifier: 0xD064b6CcFF2AE8968bA6725e9A92f3F0431bf5D0

Appendix: Verifying an address

Verifying that an address in the CGP is correct requires a few different strategies depending on what the address is (see the mento docs for some additional information). Here are common situations:

  • Is it a rate feed identifier like: address(uint160(keccack256("relayed:CELOGBP")))
    • Use Keccak-256 Online 2 to compute the keccak
    • Take the last 40 characters (20 bytes) of the keccak and compare them with the value
  • Is it a Mento contract?
    • It’s probably either SortedOracles or the proxy address of one of the 5 stables included in this launch.
  • Is it a Core Celo contract?
    • Yes:
      • It can be verified in the Celo Registry
4 Likes

Hi Folks, The first proposal to enable the oracles needed for the launch of cGBP, cAUD, cCAD, cCHF, and cZAR stable coins has been submitted:

https://mondo.celo.org/governance/cgp-177

A second proposal CGP-0178 has also been submitted for voting and will officially launch the following stable tokens:

  • cGBP (British Pound)
  • cZAR (South African Rand)
  • cCAD (Canadian Dollar)
  • cAUD (Australian Dollar)

https://mondo.celo.org/governance/cgp-178

Note: Follow-up proposals for the oracle whitelist and launch of cJPY, cNGN, and cCHF will be submitted in the coming weeks

This post serves as a guide to help verify the transactions that will activate these new stable tokens and integrate them into the Mento protocol. You can follow along with the raw transactions found in the mainnet.json file in the Celo governance repo.

We have prepared for this launch by:

  • Testing the proposals on Alfajores
  • Simulating the proposals on a fork of Celo Mainnet
  • Running comprehensive tests against the fork

The transactions for the CGP will be broken down into the following sections, with transaction indices in brackets:

  1. Initialization of Stable Tokens (0-27)
  2. Configure Exchanges & Trading Limits (28-39)
  3. Configure Circuit Breaker (40-59)

Before diving into each section and transaction, it would be useful to have the following available so you can follow along:

For the tech-savvy, you can find everything we are describing here in our deployment tooling repository as code, in the file FX01.sol. To verify that the addresses included in the proposal are correct, please refer to the Appendix at the bottom for guidance.

1. Initialization of the Stable Tokens

For each stable token (cGBP, cZAR, cCAD, cAUD), the initialization process includes:

  • Initializing the proxy token contract & setting its implementation address
  • Calling the StableTokenV2 initializer
  • Configuring constitution parameters for various token functions
  • Adding the token to the Reserve
  • Whitelisting as a gas currency

Specific verification steps are included for each transaction, such as:

  • Verifying proxy and implementation contract addresses
  • Checking initialization parameters
  • Confirming governance and function thresholds

The table below details the initialization transactions for the new stable tokens, including their symbols, full names, contract names, and the corresponding transaction number ranges.

Symbol Name Contract Name Initialization Txs
cGBP Celo British Pound StableTokenGBPProxy 0 - 6
cZAR Celo South African Rand StableTokenZARProxy 7 - 13
cCAD Celo Canadian Dollar StableTokenCADProxy 14 - 20
cAUD Celo Australian Dollar StableTokenAUDProxy 21 - 27

Below is a breakdown of the initialization transactions for cGBP, the same verification steps apply to all other stable tokens.

TX#0 - initialize the stable token proxy contract and set its implementation address

The proxy contract has been deployed and verified on Celo mainnet. Immediately after, ownership of the proxy contract was transferred to governance. This transaction has two arguments, the first being the address of the StableTokenV2 implementation and the second being the initialisation parameters.

  • Verify the StableTokenGBPProxy address
  • Verify the StableTokenV2 implementation address
  • Verify the initialization parameters for the new stable token:
    • name: Celo British Pound
    • symbol: cGBP
    • initialBalanceAddresses: [] (no pre-mint)
    • initialBalanceValues: [] (no pre-mint)

TX#1 - Call StableTokenV2 initializer

  • Verify the StableTokenGBPProxy address
  • Verify the StableTokenV2 implementation address
  • Verify the initialization parameters:
    • broker: - Verify the broker proxy address
    • validators: - Verify the validators address
    • exchange: address(0) - Exchanges have been deprecated and replaced by the broker

TX#2 - set constitution parameters for transfer(address,uint256)

  • Verify the GovernanceProxy address
  • Verify the StableTokenGBPProxy address
  • Verify the function selector: bytes4(keccak256(bytes("transfer(address,uint256)")))
  • Verify the threshold: 0.9 * 1e24 (same as existing stables)

TX#3 - set constitution parameters for transferWithComment(address,uint256,string)

  • Verify the GovernanceProxy address
  • Verify the StableTokenGBPProxy address
  • Verify the function selector: bytes4(keccak256(bytes("transferWithComment(address,uint256,string)")))
  • Verify the threshold: 0.6 * 1e24 (same as existing stables)

TX#4 - set constitution parameters for approve(address,uint256)

  • Verify the GovernanceProxy address
  • Verify the StableTokenGBPProxy address
  • Verify the function selector: bytes4(keccak256(bytes("approve(address,uint256)")))
  • Verify the threshold: 0.6 * 1e24 (same as existing stables)

TX#5 - add cGBP as a stable token to the Reserve

  • Verify the ReserveProxy address
  • Verify the StableTokenGBPProxy address

TX#6 - whitelist cGBP as a gas currency

  • Verify the FeeCurrencyWhitelistProxy address
  • Verify the StableTokenGBPProxy address

2. Configure the new exchanges and trading limits

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 may make the payload look confusing.

The table below details the transactions for the exchange creation and trading limit configuration

Exchange Txs
cCUSD < - > cGBP 28 - 30
cCUSD < - > cZAR 31 - 33
cCUSD < - > cCAD 34 - 36
cCUSD < - > cAUD 37 - 39

Below is a breakdown of the trading limit configuration and exchange creation for the cCUSD/cGBP exchange, the same verification steps apply to all other exchanges.

Create & configure exchange

The pool configuration structures are documented here: https://docs.mento.org/mento/developers/smart-contracts/bipoolmanager

TX#28 - Create the cUSD/cGBP exchange

  • Verify the BiPoolManagerProxy address
  • Verify the exchange configuration

Configure trading limits

The TradingLimit.Config structure is documented here: Mento Trading Limits

TX#29 - Configure trading limits on cUSD for the cUSD/cGBP exchange

  • Verify BrokerProxy address
  • Verify the arguments:
    • exchangeId: the deterministic id of the cUSD/cGBP exchange
    • token: which asset in the pair to target, in this case, the cUSD token address
    • config: the trading limit configuration

TX#30 - Configure trading limits on cGBP for the cUSD/cGBP exchange

  • Verify Broker address
  • Verify the arguments:
    • exchangeId: the deterministic id of the cUSD/cGBP exchange
    • token: which asset in the pair to target, in this case, the cGBP token address
    • config: the trading limit configuration

3. Configure CircuitBreaker

Each new exchange requires a rate feed that must be monitored for stability. The circuit breaker system performs this monitoring through two configuration steps:

  1. Configure the breaker box to track rate changes
  2. Set up monitoring thresholds through the median delta breaker

The table below details the ratefeed name, its ID and the corresponding transactions for the BreakerBox configuration.

Rate Feed Rate Feed Id Txs
relayed:GBPUSD 0xf590b62f9cfcc6409075b1ecAc8176fe25744B88 40 - 44
relayed:ZARUSD 0x17ef04Af0c52465694a841552fc2415169b1114c 45 - 49
relayed:CADUSD 0x20869cF54Ead821C45DFb2aB0C23d2e10Fbb65A4 50 - 54
relayed:AUDUSD 0x646bD504C3864Ea5b8A6B6D25743721f61864A07 55 - 59

Below is a breakdown of the circuit breaker configuration for the GBP/USD rate feed. The same verification steps apply to all other rate feeds.

Configure the BreakerBox

Here we configure the BreakerBox to ensure it monitors changes to the new GBP/USD rate.

TX#40 - Add rate feed to the BreakerBox:

  • Verify the BreakerBox address
  • Verify the arguments:
    • newRateFeedIDs an array of oracle feed ids. In this case, the array only contains the id of GBP/USD
      • GBP/USD - address(uint160(uint256(keccak256("relayed:GBPUSD"))))

TX#41 - Enable the MedianDeltaBreaker for the GBP/USD rate feed

  • Verify the BreakerBox address
  • Verify the arguments:
    • breakerAddress should be the MedianDeltaBreaker
    • rateFeedID should be the identifier for the GBP/USD rate, i.e. address(uint160(uint256(keccak256("relayed:GBPUSD"))))
    • enable true

Configure the MedianDeltaBreaker

These transactions configure the MedianDeltaBreaker with the appropriate values needed to determine when the breaker should trip for the GBP/USD rate feed and how much time should pass before the breakers can be reset.

TX#42 - Set the cooldown time on the MedianDeltaBreaker for the GBP/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedIDs: the array of rate feeds to configure:
      • GBP/USD: address(uint160(uint256(keccak256("relayed:GBPUSD"))))
    • cooldownTimes: the array of cooldown times to be configured per rate feed. The values are in seconds.
      • GBP/USD: 15 minutes = 900s

TX#43 - Set the rate change thresholds on the MedianDeltaBreaker for the GBP/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedIDs: the array of rate feeds to configure:
      • GBP/USD: address(uint160(uint256(keccak256("relayed:GBPUSD"))))
    • 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.
      • GBP/USD: 4e22 = 4%d

TX#44 - Set the smoothing factor on the MedianDeltaBreaker for the GBP/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedID: The id of the rate feed to be updated
      • GBP/USD: address(uint160(uint256(keccak256("relayedGBPUSD"))))
    • newSmoothingFactor: The new smoothing factor value. The value is a fixed point number, similar to the rate change threshold.
      • GBP/USD: 5e19 = 0.005%

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(”relayed:GHSUSD”)))
    • 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?
  • Is it a Core Celo contract?
    • Yes:
      • It’s probably GoldToken or FeeCurrencyDirectory which can be both verified in the Celo Registry
5 Likes

Hey Bayo, regarding the smoothing factor:

from what I see, the on-chain proposal has these set to 5e21 instead of 5e19, a difference of two orders of magnitude. Which is the intended value?

1 Like

Hey Martin, thank you for taking the time to verify the proposal and for pointing this out!

You are correct, there’s an error in the written verification steps and the expected threshold should be 5e21 or 0.5% instead, so the on-chain value is correct in this case. This is consistent with the value used in recent token launches (see PUSO, tx#14, GHS tx#14, cCOP tx#14).

This can also be verified on-chain by querying the MedianDeltaBreaker contract, for example on the COP/USD ratefeed:

> cast call 0x49349F92D2B17d491e42C8fdB02D19f072F9B5D9 "getSmoothingFactor(address)(uint256)" "0x0196D1F4FdA21fA442e53EaF18Bf31282F6139F1" --rpc-url https://forno.celo.org

5000000000000000000000 [5e21]
2 Likes

That’s cool.

Alos @celomexico has strong presence in Mexico, with thausand of possible corridors to generate volume.

Hey folks, we will soon put out a proposal to enable the needed oracles for the launch of two new Mento stablecoins ($cJPY and $cNGN). This is a follow-up to CGP 177 (Adding oracles to support GBP, AUD, CAD, CHF, ZAR stablecoins) as the Chainlink feeds for JPY and NGN were not available at the time.

This post provides an overview of the transactions to whitelist the Chainlink relayer contracts that will report the rates needed for these new stablecoins. As with previous proposals, the Celo community will have the opportunity to verify these transactions. In preparation for the upcoming CGP, we have completed the following steps:

  • Tested the proposal on Alfajores
  • Simulated the proposal on a fork of Celo Mainnet and conducted tests against this fork

Before we dive into the details of each section and transaction, we recommend setting up celocli to follow along. For those technically inclined, you can find everything described here in our deployment tooling repository. For verifying the addresses included in the proposal, please refer to the Appendix at the bottom for helpful tips.

1. Whitelisting and configuring of Chainlink Relayers in SortedOracles

The ChainlinkRelayer transmits rate feed data from a single Chainlink price feed or an aggregation of multiple Chainlink price feeds to the SortedOracles contract.

This contract can aggregate multiple price feeds to provide derived rate feeds to the protocol. This is necessary because it is more efficient for Oracle providers to report FX rates against the dollar and crypto-asset rates against the dollar, rather than providing all possible combinations.

For example, Chainlink reports the JPY/USD rate, but it does not directly provide the CELO/JPY rate, which is needed for gas payments in a JPY stable token. However, by using both the JPY/USD and CELO/USD rates, the following path can be derived:

CELO/USD * inverse(JPY/USD) = CELO/JPY.

A separate ChainlinkRelayer instance will be deployed for each required rate feed.

Each token requires a total of 5 transactions, and they all follow the same pattern:

  1. Whitelist the CELO/XXX relayer (for gas payments purposes)
  2. Set the report expiry time for the CELO/XXX pair
  3. Whistelist the XXX/USD relayer
  4. Set the report expiry time for the XXX/USD pair
  5. Set the equivalent token for XXX to be CELO/XXX (for gas payments purposes)

JPY Relayers

TX#0 - whitelist CELO/JPY Relayer in SortedOracles - addOracle()

  • Verify the SortedOracleProxy address
  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELOJPY")))));
    • Should match: 0xd5800BbeC4Fb58b549C8de50635654E919c3Cd5D
  • Verify the ChainlinkRelayer address: 0x522D100Ce28b150fBfcB90551d8822789ff53886

TX#1 - configure CELO/JPY token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xd5800BbeC4Fb58b549C8de50635654E919c3Cd5D
  • Verify Report Expiry Seconds: 360 (6 minutes)

TX#2 - whitelist JPY/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:JPYUSD")))));
    • Should match: 0xFDE35B45cBd2504FB5dC514F007bC2DE27034274
  • Verify the ChainlinkRelayer address: 0x1327A32fA7e3a0C3c0a5828D4f3ff16CE9E13Ee9

TX#3 - Configure JPY/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xFDE35B45cBd2504FB5dC514F007bC2DE27034274
  • Verify Report Expiry Seconds: 360

TX#4 - Configure equivalent token for JPY rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0xc45eCF20f3CD864B32D9794d6f76814aE8892e20 (StableTokenJPYProxy)
  • Verify the Rate Feed Identifier: 0xd5800BbeC4Fb58b549C8de50635654E919c3Cd5D

NGN Relayers

TX#5 - whitelist CELO/NGN Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:CELONGN")))));
    • Should match: 0xd9D1A7FAA5deFe7E0301Ac5363E6ca18eB78c9D7
  • Verify the ChainlinkRelayer address: 0x75Ba8f6855e54F36282067b185f2b9c0baC8A588

TX#6 - Configure CELO/NGN token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xd9D1A7FAA5deFe7E0301Ac5363E6ca18eB78c9D7
  • Verify Report Expiry Seconds: 360

TX#7 - whitelist NGN/USD Relayer in SortedOracles - addOracle()

  • Verify the Rate Feed Identifier
    • address(uint160(uint256(keccak256(abi.encodePacked("relayed:NGNUSD")))));
    • Should match: 0xC13D42556f1baeab4a8600C735afcd5344048d3C
  • Verify the ChainlinkRelayer address: 0xce35D1F69523a0672b9281dF1675D5b5D4004feF

TX#8 - Configure NGN/USD token report expiry time in SortedOracles - setTokenReportExpiry()

  • Verify the Rate Feed Identifier: 0xC13D42556f1baeab4a8600C735afcd5344048d3C
  • Verify Report Expiry Seconds: 360

TX#9 - Configure equivalent token for NGN rate in SortedOracles - setEquivalentToken()

  • Verify the token address: 0xE2702Bd97ee33c88c8f6f92DA3B733608aa76F71 (StableTokenNGNProxy)
  • Verify the Rate Feed Identifier: 0xd9D1A7FAA5deFe7E0301Ac5363E6ca18eB78c9D7

Appendix: Verifying an address

Verifying that an address in the CGP is correct requires a few different strategies depending on what the address is (see the mento docs for some additional information). Here are common situations:

1 Like

Hey folks, the first proposal to enable the oracles needed for the launch of cCHF, cNGN, and cJPY stable coins is currently in the voting phase: CGP180

A second proposal will be submitted for voting to officially launch the following stable tokens:

  • cCHF (Swiss Franc)
  • cNGN (Nigerian Naira)
  • cJPY (Japanese Yen)

This post serves as a guide to help verify the transactions that will activate these new stable tokens and integrate them into the Mento protocol.

We have prepared for this launch by:

  • Testing the proposals on Alfajores
  • Simulating the proposals on a fork of Celo Mainnet
  • Running comprehensive tests against the fork

The transactions for the CGP will be broken down into the following sections, with transaction indices in brackets:

  1. Initialization of Stable Tokens (0-20)
  2. Configure Exchanges & Trading Limits (21-32)
  3. Configure BreakerBox (33-35)
  4. Configure MedianDeltaBreaker (36-38)

Before diving into each section and transaction, it would be useful to have the following available so you can follow along:

For the tech-savvy, you can find everything we are describing here in our deployment tooling repository as code, in the file FX03.sol. To verify that the addresses included in the proposal are correct, please refer to the Appendix at the bottom for guidance.

1. Initialization of the Stable Tokens

For each stable token (cCHF, cNGN, cJPY), the initialization process includes:

  • Initializing the proxy token contract & setting its implementation address
  • Calling the StableTokenV2 initializer
  • Configuring constitution parameters for various token functions
  • Adding the token to the Reserve
  • Whitelisting as a gas currency

Specific verification steps are included for each transaction, such as:

  • Verifying proxy and implementation contract addresses
  • Checking initialization parameters
  • Confirming governance and function thresholds

The table below details the initialization transactions for the new stable tokens, including their symbols, full names, contract names, and the corresponding transaction number ranges.

Symbol Name Contract Name Initialization Txs
cCHF Celo Swiss Franc StableTokenCHFProxy 0 - 6
cNGN Celo Nigerian Naira StableTokenNGNProxy 7 - 13
cJPY Celo Japanese Yen StableTokenJPYProxy 14 - 20

Below is a breakdown of the initialization transactions for cCHF, the same verification steps apply to all other stable tokens.

TX#0 - initialize the stable token proxy contract and set its implementation address

The proxy contract has been deployed and verified on Celo mainnet. Immediately after, ownership of the proxy contract was transferred to governance. This transaction has two arguments, the first being the address of the StableTokenV2 implementation and the second being the initialisation parameters.

  • Verify the StableTokenCHFProxy address: 0xb55a79f398e759e43c95b979163f30ec87ee131d
  • Verify the StableTokenV2 implementation address
  • Verify the initialization parameters for the new stable token:
    • name: Celo Swiss Franc
    • symbol: cCHF
    • initialBalanceAddresses: [] (no pre-mint)
    • initialBalanceValues: [] (no pre-mint)

TX#1 - Call StableTokenV2 initializer

  • Verify the StableTokenCHFProxy address
  • Verify the StableTokenV2 implementation address
  • Verify the initialization parameters:
    • broker: - Verify the broker proxy address
    • validators: - Verify the validators address
    • exchange: address(0) - Exchanges have been deprecated and replaced by the broker

TX#2 - set constitution parameters for transfer(address,uint256)

  • Verify the GovernanceProxy address
  • Verify the StableTokenCHFProxy address
  • Verify the function selector: bytes4(keccak256(bytes("transfer(address,uint256)")))
  • Verify the threshold: 0.9 * 1e24 (same as existing stables)

TX#3 - set constitution parameters for transferWithComment(address,uint256,string)

  • Verify the GovernanceProxy address
  • Verify the StableTokenCHFProxy address
  • Verify the function selector: bytes4(keccak256(bytes("transferWithComment(address,uint256,string)")))
  • Verify the threshold: 0.6 * 1e24 (same as existing stables)

TX#4 - set constitution parameters for approve(address,uint256)

  • Verify the GovernanceProxy address
  • Verify the StableTokenCHFProxy address
  • Verify the function selector: bytes4(keccak256(bytes("approve(address,uint256)")))
  • Verify the threshold: 0.6 * 1e24 (same as existing stables)

TX#5 - add cCHF as a stable token to the Reserve

  • Verify the ReserveProxy address
  • Verify the StableTokenCHFProxy address

TX#6 - whitelist cCHF as a gas currency

  • Verify the FeeCurrencyWhitelistProxy address
  • Verify the StableTokenCHFProxy address

2. Configure the new exchanges and trading limits

:warning: 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 may make the payload look confusing.

The table below details the transactions for the exchange creation and trading limit configuration

Exchange Txs
cCUSD < - > cCHF 21 - 23
cCUSD < - > cNGN 24 - 26
cCUSD < - > cJPY 27 - 29

Below is a breakdown of the trading limit configuration and exchange creation for the cCUSD/cCHF exchange, the same verification steps apply to all other exchanges.

Create & configure exchange

The pool configuration structures are documented here: https://docs.mento.org/mento/developers/smart-contracts/bipoolmanager

TX#21 - Create the cUSD/cCHF exchange

  • Verify the BiPoolManagerProxy address
  • Verify the exchange configuration

Configure trading limits

The TradingLimit.Config structure is documented here: Mento Trading Limits

TX#22 - Configure trading limits on cUSD for the cUSD/cCHF exchange

  • Verify BrokerProxy address
  • Verify the arguments:
    • exchangeId: the deterministic id of the cUSD/cCHF exchange
    • token: which asset in the pair to target, in this case, the cUSD token address
    • config: the trading limit configuration

TX#23 - Configure trading limits on cCHF for the cUSD/cCHF exchange

  • Verify Broker address
  • Verify the arguments:
    • exchangeId: the deterministic id of the cUSD/cCHF exchange
    • token: which asset in the pair to target, in this case, the cCHF token address
    • config: the trading limit configuration

3. Configure CircuitBreaker

Each new exchange requires a rate feed that must be monitored for stability. The circuit breaker system performs this monitoring through two configuration steps:

  1. Configure the breaker box to track rate changes
  2. Set up monitoring thresholds through the median delta breaker

The table below details the ratefeed name, its ID and the corresponding transactions for the BreakerBox configuration.

Rate Feed Rate Feed Id Txs
relayed:CHFUSD 0x0f61BA9c30ef7CaEE7E5CC1F96BFFCb0f52ccD64 30 - 34
relayed:NGNUSD 0xC13D42556f1baeab4a8600C735afcd5344048d3C 35 - 39
relayed:JPYUSD 0xFDE35B45cBd2504FB5dC514F007bC2DE27034274 40 - 44

Below is a breakdown of the circuit breaker configuration for the CHF/USD rate feed. The same verification steps apply to all other rate feeds.

Configure the BreakerBox

Here we configure the BreakerBox to ensure it monitors changes to the new CHF/USD rate.

TX#30 - Add rate feed to the BreakerBox:

  • Verify the BreakerBox address
  • Verify the arguments:
    • newRateFeedIDs an array of oracle feed ids. In this case, the array only contains the id of CHF/USD
      • CHF/USD - address(uint160(uint256(keccak256("relayed:CHFUSD"))))

TX#31 - Enable the MedianDeltaBreaker for the CHF/USD rate feed

  • Verify the BreakerBox address
  • Verify the arguments:
    • breakerAddress should be the MedianDeltaBreaker
    • rateFeedID should be the identifier for the CHF/USD rate, i.e. address(uint160(uint256(keccak256("relayed:CHFUSD"))))
    • enable true

Configure the MedianDeltaBreaker

These transactions configure the MedianDeltaBreaker with the appropriate values needed to determine when the breaker should trip for the CHF/USD rate feed and how much time should pass before the breakers can be reset.

TX#32 - Set the cooldown time on the MedianDeltaBreaker for the CHF/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedIDs: the array of rate feeds to configure:
      • CHF/USD: address(uint160(uint256(keccak256("relayed:CHFUSD"))))
    • cooldownTimes: the array of cooldown times to be configured per rate feed. The values are in seconds.
      • CHF/USD: 15 minutes = 900s

TX#33 - Set the rate change thresholds on the MedianDeltaBreaker for the CHF/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedIDs: the array of rate feeds to configure:
      • CHF/USD: address(uint160(uint256(keccak256("relayed:CHFUSD"))))
    • 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.
      • CHF/USD: 4e22 = 4%

TX#34 - Set the smoothing factor on the MedianDeltaBreaker for the CHF/USD rate feed

  • Verify the MedianDeltaBreaker address
  • Verify the arguments:
    • rateFeedID: The id of the rate feed to be updated
      • CHF/USD: address(uint160(uint256(keccak256("relayed:CHFUSD"))))
    • newSmoothingFactor: The new smoothing factor value. The value is a fixed point number, similar to the rate change threshold.
      • CHF/USD: 5e21 = 0.5%

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("relayed:CHFUSD")))
    • 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?
  • Is it a Core Celo contract?
    • Yes:
      • It’s probably GoldToken or FeeCurrencyDirectory which can be both verified in the Celo Registry
2 Likes

Hey all! The launch proposal of cCHF, cNGN and cJPY is now live
https://mondo.celo.org/governance/cgp-182

1 Like