[DRAFT] Proposal: On-Chain Report Price Filter

This is a draft proposal for a change that aims to improve the functionality of the stability mechanism. The change will update the SortedOracles contract and will introduce two new contracts to the mento protocol.

Simple Summary
  1. Design of an oracle report filter with modular filter conditions.
  2. Design of an oracle report price deviation filter condition.

The Celo oracle client is currently responsible for running checks against new price reports before submitting them to the SortedOracles contract. One of these checks determines if prices from data sources have a deviation greater than a specified threshold. If a report fails any of the checks, the oracle will ignore the report and will not send an update to the contract. This proposal is to move this functionality on-chain to allow new checks to be added easily in the future.

A new contract, the ReportFilter, will be created and injected into the SortedOracles contract. The new contract will act as a simple interface to allow the SortedOracles to filter incoming reports without knowing the filter conditions, their dependencies or how they are checked. Filter conditions are created as separate modules to allow new ones to be added or removed independently in future updates.


The Mento protocol relies on price reports from centralized oracle clients run by cLabs. Whilst cLabs is a trusted entity, as long as the protocol depends on the centralized oracle setup, it will remain a single point of failure for Mento. This change aims to move one of the current responsibilities, in addition to providing price reports, held by the oracle clients on-chain.

In addition to providing price reports, the oracle clients also perform additional checks before sending reports to the SortedOracles contract:

  1. Before submitting a report, the oracle client pulls prices from all configured exchange data sources. The first of multiple checks verify that the prices received do not deviate more than a configurable value, the maxPercentageDeviation. If the deviation is too large, the oracle will not submit an update.
  2. The next check determines if the current report price change relative to the last report price is greater than a calculated threshold. If the difference exceeds the threshold, the oracle circuit breaker will be opened, stopping the oracle client from reporting prices indefinitely.
  3. The last check determines if the current report price change is significant enough to trigger a new report. If the difference is too small, the oracle client will not send the report to the SortedOracles contract.

The last check will not be moved, as this is just an optimization for the oracle client, and authorized accounts should be able to report as often as they like. The first two checks will be moved on-chain to be performed in a trustless and transparent manner. Moving these checks on-chain will support a move to a decentralized oracle setup which eliminates the single failure point and contributes to providing Mento stablecoin users with a strong guarantee of price data availability, among other advantages.

This proposal, however, will focus only on moving the first check for price deviation on-chain. An on-chain circuit breaker will be implemented in a follow-up proposal which will cover the second check.


The SortedOracles contract will be amended to include a new modifier, checkReport(address token, uint256 value). The modifier will be added to the report function. A reference to a new contract, ReportFilter, will be added to SortedOracles. The responsibility of the new contract will be to check a specified price report meets all conditions before the SortedOracles contract processes the report. When an oracle client calls the report function, the ReportFilter will return a bool indicating whether or not the specified report meets all conditions. If the report doesn’t meet a condition, it will not be taken up by the SortedOracles contract. FilterCondition contracts each have a single responsibility, the same interface and will be manageable through the ReportFilter. A new filter condition contract, PriceDeviationFilter, will be created to check that a given report does not deviate more than the maxPercentageDeviation when compared with the existing reports. The maxPercentageDeviation will initially be set to the value currently used in the oracle clients but can be changed by governance.


The design follows a modular approach to give us flexibility with the option to add additional filter conditions at a later date without redeploying SortedOracles or ReportFilter. The ReportFilter only needs to store a mapping of enabled filters and their addresses. The logic for executing the checks should not need to change as all filters will implement the same interface.


This implementation will increase the gas cost for each report submitted by oracle clients. With each new condition added, the gas cost will increase further. This increase is an unfortunate effect but necessary.

Technical Specification

IFilterCondition interface:

IReportFilter interface:



Summary of Changes:
  • Create IReportFilter & IFilterCondition interfaces
  • Create PriceDeviationFilter contract
  • Create ReportFilter contract
  • Add modifier to SortedOracles
  • Update SortedOracles.report()
Test Cases

Will be included with implementation.

Security Considerations

Enabling and disabling filter conditions will be left to governance. However, this potentially leaves the system vulnerable to a governance attack. In this case, it would be possible for an attacker to disable all filters to allow a bad price to be taken up by SortedOracles. This is a low risk as cLabs still run the oracle clients. Before moving to a decentralized oracle setup, there will be other layers in place to protect the system’s integrity should this occur.


Earlier this year when celo price pumped to double on that Korean exchange listing, if that happened again, what should the behavior be?

It was a huge instantaneous price change and all oracles saw the same pump and decided to stop reporting.

It’s a little dangerous for official oracle to keep the old price and not report new price. Moola for example had a few minutes where people could have borrowed based on stale price… luckily nobody exploited this, but it was possible due to all oracle clients circuit breaking

If that happens again, the first oracle reporting the new price will be filtered out. Once more oracles start reporting a similar price, the mean will get closer to this price, and any new oracle reports close to the new price will no longer be filtered. The same check is implemented in the oracle client here, and the behaviour should be the same.

Currently, if the price deviation check fails, the oracle client will not send a report for that assigned block but should not stop reporting completely. The oracle client will stop reporting if the new price trips the circuit breaker.

There’s also another proposed change for an on-chain circuit breaker that would have halted mento trading in this situation. Maybe Moola would benefit from this also, but I think all protocols should have protections in place when using oracles.

Okay we should run some simulation scenarios to see how this behaves, there was also CEX disagreement for a few minutes during the pump, but soon it stabilized.

Having different CEX disagree for a brief while might have been what caused the circuit to get tripped that night.

We did fix up moola oracle but we still wait for the official oracle price to go stale first before moving to fallback twap oracle. During this period the protocol is slightly at risk.

1 Like