SIP 88: ExchangeRates patch - Chainlink aggregator V2V3 Source

AuthorClement Balestrat

Simple Summary

During the Formalhaut release, an update was made to the ExchangeRates contract to be using Chainlink’s aggregator V2V3 (SIP-86).

Just after the change was made, we found an edge case where transfers are reverting if a price has not been updated after a user exchanges into a Synth. The issue relates to how fee reclamation is calculated.


This SIP will fix the issue by using low level calls to the Chainlink aggregator’s latestRoundData and getRoundData functions in order to avoid any reverts in case the requested round ID does not exist.

This will act as a try/catch method which will allow the ExchangeRates contract to only update a rate if the previous call to the aggregator was successful.


In the previous version of Chainlink’s aggregator interface, the function getRoundData(uint roundId) was returning 0 in case roundId was not found.

This was helpful for the ExchangeRates contract to know if a new round ID existed or not during the calculation of fee reclamation, by calling getRoundData(roundId + 1). If the result was returning 0, the current round ID was kept for the next steps.

However, this logic was changed in the latest aggregator interface. Calling getRoundData(roundId) is now reverting if roundId cannot be found, which makes the current logic obsolete.



This SIP will implement low-level calls to Chainlink aggregator’s getRoundData() and getLatestRoundData() functions in order to suppress any reverts.

    bytes memory payload = abi.encodeWithSignature("getRoundData(uint80)", roundId);
    (bool success, bytes memory returnData) = address(aggregator).staticcall(payload);

As shown above, staticcall is used here to avoid getRoundData() from reverting, returning success and returnData.

If success is true, ExchangeRates will then update the rates with returnData. If success is false, we do nothing.


Technical Specification

Test Cases

A production test will need to be added with the following scenario:

  • Exchange a synth from sUSD to sETH
  • Wait the required amount of time for a transaction to be allowed to settle (SystemSettings.waitingPeriodsSecs())
  • Settle sETH

This scenario will only pass if the patch described in this SIP is implemented. It will fail otherwise.

Configurable Values (Via SCCP)


Copyright and related rights waived via CC0.