Skip to content

ExchangeRates

This contract stores the latest Synth exchange rates. These rates are set by an oracle, which updates this contract every three minutes with any prices that have moved sufficiently. Once set, these prices are available for any contract in the Oikos system to query. Prices which have not been updated recently enough are considered stale; Oikos functionality using stale prices does not operate. All rates are denominated in terms of sUSD, so the price of sUSD is always $1.00, and is never stale.

The ExchangeRates contract is also responsible for computing the prices of various derived synths. In particular, the behaviour of inverse synths is defined here. These are derivative synths whose price varies inversely with the price of an underlying asset.

This contract interacts with the oracle's frontrunning protection, which is partially described in SIP-6 and SIP-7.

This does not turn off any functionality in the exchange rate contract, but is used by Oikos to disable currency exchanges while prices are being updated to protect against oracle front running. The lock is released when rate updates have completed.

Source: ExchangeRates.sol

Architecture


Inheritance Graph

ExchangeRates inheritance graph


ExchangeRates architecture graph

Details
  • oracle: This address is not actually a contract, but it is the source of prices for this contract.
  • Aggregators: These are a collection of decentralized pricing networks that collect and aggregate results from a network of oracles.
  • PurgeableSynth: exchange rates are used to determine if the total token value is below the purge threshold.
  • Oikos: the value of tokens is used to in order to facilitate exchange between them, and to ensure exchanges cannot occur while price updates and being made or if a particular exchange rate is stale.

Libraries


External References

  • AggregatorInterface - Each of these interfaces correspond to a decentralized pricing network facilitated by Chainlink that collect and aggregatate results from a network of oracles. See Aggregator.sol for the implementation.s

Structs


InversePricing

Holds necessary information for computing the price of inverse Synths.

Field Type Description
entryPoint uint (18 dp) The underlying asset's price at the time the inverse index was set up. Must be strictly greater than 0.
upperLimit uint (18 dp) The upper limit of the inverse price. Must lie strictly between entryPoint and twice entryPoint.
lowerLimit uint (18 dp) The lower limit of the inverse price. Must lie strictly between 0 and entryPoint.
frozen bool True if an inverse Synth has breached one of its limits.

Constants


ORACLE_FUTURE_LIMIT

The maximum time in the future (10 minutes) that rates are allowed to be set for.

Type: uint constant

Value: 10 minutes


Variables


aggregators

For each currency with a decentralized aggregated pricing network, return the Aggregation contract address.

Type: mapping(bytes32 => AggregatorInterface) public


aggregatorKeys

A list of the keys of currencies with a decentralized aggregated pricing network.

Type: bytes32[] public


inversePricing

For each currency with an inverse index, keep the necessary InversePricing information to maintain the index.

Type: mapping(bytes32 => InversePricing) public


invertedKeys

A list of the keys of currencies with an inverted index.

Type: bytes32[] public


oracle

The address which is permitted to push rate updates to the contract.

Type: address public


rateStalePeriod

The duration after which a rate will be considered out of date. Synth exchange and other price-sensitive transactions in the Oikos contract will not operate if a relevant rate is stale. Initialised to 3 hours.

Type: uint public


Constructor

Initialises the oracle address and initial currency prices, along with the inherited SelfDestructible instance.

Details

Signature

constructor(address _owner, address _oracle, bytes32[] _currencyKeys, uint[] _newRates) public

Superconstructors

Preconditions

  • _currencyKeys and _newRates must be the same length.
  • "sUSD" must not appear in _currencyKeys.
  • 0 must not appear in _newRates.

Views


anyRateIsStale

Loop over the given array of currencies and return true if any of them is stale. sUSD's rate is never stale. Rates for nonexistent currencies are always stale.

Details

Signature

anyRateIsStale(bytes32[] currencyKeys) external view returns (bool)


effectiveValue

Given a quantity of a source currency, returns a quantity of a destination currency that is of equivalent value at current exchange rates, if those rates are fresh.

The effective value is computed as a simple ratio of the prices of the currencies concerned. That is, to convert a quantity Q_A of currency A to currency B at prices \pi_A and \pi_B, the quantity Q_B received is:

Q_B = Q_A \frac{\pi_A}{\pi_B}

This computation is simple because all fractional quantities in the Oikos system except for the debt ledger are 18 decimal fixed point numbers.

Details

Signature

effectiveValue(bytes32 sourceCurrencyKey, uint sourceAmount, bytes32 destinationCurrencyKey) public view returns (uint)

Modifiers

Preconditions

  • Neither the source nor destination currency prices may be stale.

lastRateUpdateTimes

Retrieves the timestamp the given rate was last updated. Accessed by the same keys as rates is.

Details

Signature

lastRateUpdateTimes(bytes32 code) public view returns(uint256)


lastRateUpdateTimesForCurrencies

Maps lastRateUpdateTimes over an array of keys.

Details

Signature

lastRateUpdateTimesForCurrencies(bytes32[] currencyKeys) public view returns(uint[])


ratesForCurrencies

Maps rateForCurrency over an array of keys.

Details

Signature

ratesForCurrencies(bytes32[] currencyKeys) public view returns (uint[])


rateForCurrency

Returns the last recorded rate for the given currency. This is just an alias to the public mapping rates, so it could probably be eliminated.

Details

Signature

rateForCurrency(bytes32 currencyKey) public view returns (uint)


rateIsFrozen

Returns true if the inverse price for the given currency is frozen. This is simply an alias to inversePricing[currencyKey].frozen. Currencies without an inverse price will naturally return false.

Details

Signature

rateIsFrozen(bytes32 currencyKey) external view returns (bool)


rateIsStale

The rate for a given currency is stale if its last update occurred more than rateStalePeriod seconds ago.

sUSD is a special case; since its rate is fixed at 1.0, it is never stale. The rates of nonexistent currencies are always stale.

Details

Signature

rateIsStale(bytes32 currencyKey) public view returns (bool)


rates

Retrieves the exchange rate (sUSD per unit) for a given currency key (sUSD, OKS, et cetera). These prices are stored as 18 decimal place fixed point numbers.

Details

Signature

rates(bytes32 code) public view returns(uint256)


Restricted Functions (Oracle)


deleteRate

Deletes a currency's price and its update time from the ExchangeRates contract.

Details

Signature

deleteRate(bytes32 currencyKey) external

Modifiers

Preconditions

  • The specified currency must not already have been deleted.

Emits


updateRates

Allows the oracle to update exchange rates in the contract. Otherwise this is just an alias to internalUpdateRates.

Details

Signature

updateRates(bytes32[] currencyKeys, uint[] newRates, uint timeSent) external returns (bool)

Modifiers


Restricted Functions (Owner)


removeInversePricing

Allows the owner to remove an inverse index for a particular currency.

Details

Signature

removeInversePricing(bytes32 currencyKey) external

Modifiers

Emits


setInversePricing

Allows the owner to set up an inverse index for a particular currency. See rateOrInverted for computation details. New inverse indexes begin unfrozen.

Details

Signature

setInversePricing(bytes32 currencyKey, uint entryPoint, uint upperLimit, uint lowerLimit, bool freeze, bool freezeAtUpperLimit) external

Modifiers

Preconditions

  • entryPoint must be greater than zero.
  • lowerLimit must be greater than zero.
  • entryPoint must be less than upperLimit.
  • upperLimit must be less than twice entryPoint.
  • lowerLimit must be less than entryPoint.

Info

Together these entail that 0 \lt \text{lowerLimit} \lt \text{entryPoint} \lt \text{upperLimit} \lt 2 \times \text{entryPoint}.

Observe that the first precondition here is redundant, as two of the others imply it.

Emits


setOracle

Allows the owner to set the address which is permitted to send prices to this contract.

Details

Signature

setOracle(address _oracle) external

Modifiers

Emits


setRateStalePeriod

Allows the owner to set the time after which rates will be considered stale.

Details

Signature

setRateStalePeriod(uint _time) external

Modifiers

Emits


Internal Functions


getRateAndUpdatedTime

Helper function gets a RateAndUpdatedTime struct for the given currency key.

Details

Signature

getRateAndUpdatedTime(bytes32 code) internal returns (RateAndUpdatedTime)


internalUpdateRates

Record the set of provided rates and the timestamp, handling any inverse indexes with rateOrInverted. At this stage inverse indexes which escaped their bounds are frozen. Any rate with a more recent update time is skipped.

Finally, the price update lock is reset, reenabling Synth exchange functionality.

The timeSent argument is useful for maintaining the exact age of the data points even as transactions can take a variable amount of time to confirm. Without this, earlier updates could possibly overwrite later ones.

Returns true if no exception was thrown.

Details

Signature

internalUpdateRates(bytes32[] currencyKeys, uint[] newRates, uint timeSent) internal returns (bool)

Preconditions

  • currencyKeys and newRates must be the same length.
  • timeSent must be less than ORACLE_FUTURE_LIMIT seconds in the future.
  • "sUSD" must not appear in currencyKeys.
  • 0 must not appear in newRates.

Emits


rateOrInverted

Returns the current price for a specified currency key.

If a currency is not an inverted index, then just return the rate that was passed in. If the currency is an inverted index, return the inverted rate. If the inverted price reaches one of its limits, freeze its rate at the limit it breached. Future calls to a frozen inverted index will return the last recorded rate. That is, frozen rates can no longer be updated.

An inverted rate moves exactly inverse to the underlying price; if the underlying price moves up a dollar, the inverted price moves down a dollar. The price \bar{p} of an inverse index c with base price p, entry point e, and lower and upper limits l and u respectively, is computed as:

\bar{p} = \text{clamp(}2e - p, \ l, \ u\text{)}

With 0 \lt l \lt e \lt u \lt 2e enforced by setInversePricing.1

So if p moves from e to e + \delta, then \bar{p} moves to e - \delta, if it would not be frozen. \bar{p} is frozen whenever \bar{p} \in \{l,u\}; that is, when 2e - l \le p or p \le 2e - u. This implies that p can never exceed twice its entry point without \bar{p} being frozen, but in principle it could reach almost to zero.

Details

Signature

rateOrInverted(bytes32 currencyKey, uint rate) internal returns (uint)

Emits


removeFromArray


Helper function that removes an entry from an existing array in storage. Returns true if found and removed, false otherwise.

Details

Signature

removeFromArray(bytes32 entry, bytes32[] storage array) internal returns (bool)


_setRate

Updates the rate and timestamp for the individual rate using an internal struct.

Details

Signature

_setRate(bytes32 code, uint256 rate, uint256 time) internal


Modifiers


onlyOracle

Reverts the transaction if msg.sender is not the oracle.


rateNotStale

Reverts the transaction if the given currency's rate is stale.

Signature: rateNotStale(bytes32 currencyKey)


Events


AggregatorAdded

Records that an Aggregator pricing network was added

Signature: AggregatorAdded(bytes32 currencyKey, address aggregator)


AggregatorRemoved

Records that an Aggregator pricing network was removed

Signature: AggregatorRemoved(bytes32 currencyKey, address aggregator)


InversePriceConfigured

Records that an inverse price index was set up or deleted. As there is no distinct event for deletion, this is signaled by providing zero values to all arguments barring currencyKey.

Signature: InversePriceConfigured(bytes32 currencyKey, uint entryPoint, uint upperLimit, uint lowerLimit)


InversePriceFrozen

Records that an inverse price breached a limit and was frozen.

Signature: InversePriceFrozen(bytes32 currencyKey)


OracleUpdated

Records that the anointed oracle was updated.

Signature: OracleUpdated(address newOracle)


RateStalePeriodUpdated

Records that the stale period was altered.

Signature: RateStalePeriodUpdated(uint rateStalePeriod)


RatesUpdated

Records that a set of currency prices were updated.

Signature: RatesUpdated(bytes32[] currencyKeys, uint[] newRates)


RatesDeleted

Records that the price for a particular currency was deleted.

Signature: RateDeleted(bytes32 currencyKey)



  1. The clamp function can be defined thus: clamp(v, l, u) = min(max(v, l), u)