Difference between revisions of "Exchange rate canister"

From Internet Computer Wiki
Jump to: navigation, search
Line 32: Line 32:
 
For every request, 10B cycles need to be sent along, otherwise an <code>ExchangeRateError::NotEnoughCycles</code> error is returned. The actual cost of the call depends two factors, the requested asset types and the state of the internal exchange rate cache, as follows:
 
For every request, 10B cycles need to be sent along, otherwise an <code>ExchangeRateError::NotEnoughCycles</code> error is returned. The actual cost of the call depends two factors, the requested asset types and the state of the internal exchange rate cache, as follows:
  
* If the request can be served from the cache, the actual cost is only 200M cycles.
+
* If the request can be served from the cache, the actual cost is 200M cycles.
 
* If both assets are fiat currencies, the cost is 200M cycles as well.
 
* If both assets are fiat currencies, the cost is 200M cycles as well.
 
* If one of the assets is a fiat currency, the cost is 2.6B cycles.
 
* If one of the assets is a fiat currency, the cost is 2.6B cycles.

Revision as of 13:29, 13 January 2023

Overview

The exchange rate canister (XRC) is a canister running on the uzr34 system subnet that provides exchange rates to requesting canisters. A request comprises a base asset, a quote asset, and an optional (UNIX epoch) timestamp. The base and quote asset can be any combination of cryptocurrency and fiat currency assets, for example, BTC/ICP, ICP/USD, or USD/EUR. The timestamp parameter makes it possible to request historic rates. If no timestamp is provided in the request, the rate for the current time is returned.

The XRC constitutes an on-chain oracle for exchange rates, which is particularly useful for DeFi applications but can further add value to any application that requires exchange rate information.

The cycle minting canister of the NNS will make use of the XRC to obtain up-to-date ICP/XDR rates, which it requires for the conversion of ICP to cycles.

Usage

The canister ID of the XRC is uf6dk-hyaaa-aaaaq-qaaaq-cai. A request of the form

type GetExchangeRateRequest = record {
   base_asset: Asset;
   quote_asset: Asset;
   timestamp: opt nat64;
}; 

can be sent to the XRC, which replies with the following result:

type GetExchangeRateResult = variant {
   Ok: ExchangeRate;
   Err: ExchangeRateError;
};

The full candid file can be found here.

For every request, 10B cycles need to be sent along, otherwise an ExchangeRateError::NotEnoughCycles error is returned. The actual cost of the call depends two factors, the requested asset types and the state of the internal exchange rate cache, as follows:

  • If the request can be served from the cache, the actual cost is 200M cycles.
  • If both assets are fiat currencies, the cost is 200M cycles as well.
  • If one of the assets is a fiat currency, the cost is 2.6B cycles.
  • If both assets are cryptocurrencies, the cost is 5B cycles.

The remaining cycles are returned to the requesting canister. Note that at least 10M cycles are charged even in case of an error in order to mitigate the risk of a denial-of-service attack.

Technical Details

The following figure depicts the work flow when receiving a request.

Overview of the exchange rate canister work flow (bank icon from flaticon.com).

After receiving a request (step 1), the exchange rate for each cryptocurrency asset in the request with respect to the quote asset USDT is queried (for the timestamp in the request) from all supported exchanges using HTTPS outcalls if this rate is not already cached (step 2). If a rate can be computed based on the query results received from the exchanges, it is inserted in the cache and returned to the requesting canister (step 3). The median rate of all received rates is returned as it is not susceptible to outliers (unlike the average rate).

If a cryptocurrency/cryptocurrency base-quote pair B/Q was requested, the B/Q rate is derived from the queried B/USDT and Q/USDT rates.

The XRC queries daily foreign exchange (forex) rates from forex data providers automatically on a fixed schedule. Furthermore, the XRC queries multiple stablecoin rates automatically to derive the USD/USDT rate as follows. Given SC1/USDT, SC2/USDT, ... rates for a set of stablecoins SC1, SC2, ..., it uses the median of these rates as the USD/USDT rate. This rule is based on the assumption that at least half of the stablecoins in the set keep their peg to USD at any time, in which case the median rate is an adequate estimate for the USD/USDT rate. Given the USD/USDT rate and the forex rates for fiat currencies other than USD, the requested rate can be computed for the case when one or more assets in the request are fiat currencies.

Since more requests to exchanges are required for cryptocurrency/cryptocurrency pairs, more cycles are charged for such requests.

As indicated in the figure above, the response to a successful request contains metadata in addition to the rate. The metadata contains the following fields:

  • decimals: The rate is returned as a scaled 64-bit integer. The scaling factor is 10 to the power of decimals.
  • base_asset_num_received_rates: The number of received rates for the base asset from all queried exchanges.
  • base_asset_num_queried_sources: The number of queried exchanges for the base asset.
  • quote_asset_num_received_rates: The number of received rates for the quote asset from all queried exchanges.
  • quote_asset_num_queried_sources: The number of queried exchanges for the quote asset.
  • standard_deviation: The standard deviation of all received rates for this request. Note that the standard deviation is scaled by the same factor as the rate itself.
  • forex_timestamp: The timestamp of the beginning of the day for which the forex rates were retrieved, if any.

This additional information can be used to determine the trustworthiness of the received rate, for example by checking the number of rates that went into the computation of the rate and the standard deviation. If the XRC receives largely inconsistent rates from exchanges, it returns an ExchangeRateError::InconsistentRatesReceived itself.