Oracle Client Interfaces in Solidity

Using Oraclize to update the ETH/USD exchange rate from an external source is a Solidity example demonstrating how Oraclize can be used to continuously poll for the ETH/USD price from an API and store the result in a usable manner.

Example 1. Using Oraclize to update the ETH/USD exchange rate from an external source

  1. /*
  2. ETH/USD price ticker leveraging CryptoCompare API
  3. This contract keeps in storage an updated ETH/USD price,
  4. which is updated every 10 minutes.
  5. */
  6. pragma solidity ^0.4.1;
  7. import "github.com/oraclize/ethereum-api/oraclizeAPI.sol";
  8. /*
  9. "oraclize_" prepended methods indicate inheritance from "usingOraclize"
  10. */
  11. contract EthUsdPriceTicker is usingOraclize {
  12. uint public ethUsd;
  13. event newOraclizeQuery(string description);
  14. event newCallbackResult(string result);
  15. function EthUsdPriceTicker() payable {
  16. // signals TLSN proof generation and storage on IPFS
  17. oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
  18. // requests query
  19. queryTicker();
  20. }
  21. function __callback(bytes32 _queryId, string _result, bytes _proof) public {
  22. if (msg.sender != oraclize_cbAddress()) throw;
  23. newCallbackResult(_result);
  24. /*
  25. * Parse the result string into an unsigned integer for on-chain use.
  26. * Uses inherited "parseInt" helper from "usingOraclize", allowing for
  27. * a string result such as "123.45" to be converted to uint 12345.
  28. */
  29. ethUsd = parseInt(_result, 2);
  30. // called from callback since we're polling the price
  31. queryTicker();
  32. }
  33. function queryTicker() external payable {
  34. if (oraclize_getPrice("URL") > this.balance) {
  35. newOraclizeQuery("Oraclize query was NOT sent, please add some ETH
  36. to cover for the query fee");
  37. } else {
  38. newOraclizeQuery("Oraclize query was sent, standing by for the
  39. answer...");
  40. // query params are (delay in seconds, datasource type,
  41. // datasource argument)
  42. // specifies JSONPath, to fetch specific portion of JSON API result
  43. oraclize_query(60 * 10, "URL",
  44. "json(https://min-api.cryptocompare.com/data/price?\
  45. fsym=ETH&tsyms=USD,EUR,GBP).USD");
  46. }
  47. }
  48. }

To integrate with Oraclize, the contract EthUsdPriceTicker must be a child of usingOraclize; the usingOraclize contract is defined in the oraclizeAPI file. The data request is made using the oraclize_query function, which is inherited from the usingOraclize contract. This is an overloaded function that expects at least two arguments:

  • The supported data source to use, such as URL, WolframAlpha, IPFS, or computation

  • The argument for the given data source, which may include the use of JSON or XML parsing helpers

The price query is performed in the queryTicker function. In order to perform the query, Oraclize requires the payment of a small fee in ether, covering the gas cost for processing the result and transmitting it to the __callback function and an accompanying surcharge for the service. This amount is dependent on the data source and, where specified, the type of authenticity proof that is required. Once the data has been retrieved, the __callback function is called by an Oraclize-controlled account permissioned to do the callback; it passes in the response value and a unique queryId argument, which, for example, can be used to handle and track multiple pending callbacks from Oraclize.

Financial data provider Thomson Reuters also provides an oracle service for Ethereum, called BlockOne IQ, allowing market and reference data to be requested by smart contracts running on private or permissioned networks. Contract calling the BlockOne IQ service for market data shows the interface for the oracle, and a client contract that will make the request.

Example 2. Contract calling the BlockOne IQ service for market data

  1. pragma solidity ^0.4.11;
  2. contract Oracle {
  3. uint256 public divisor;
  4. function initRequest(
  5. uint256 queryType, function(uint256) external onSuccess,
  6. function(uint256
  7. ) external onFailure) public returns (uint256 id);
  8. function addArgumentToRequestUint(uint256 id, bytes32 name, uint256 arg) public;
  9. function addArgumentToRequestString(uint256 id, bytes32 name, bytes32 arg)
  10. public;
  11. function executeRequest(uint256 id) public;
  12. function getResponseUint(uint256 id, bytes32 name) public constant
  13. returns(uint256);
  14. function getResponseString(uint256 id, bytes32 name) public constant
  15. returns(bytes32);
  16. function getResponseError(uint256 id) public constant returns(bytes32);
  17. function deleteResponse(uint256 id) public constant;
  18. }
  19. contract OracleB1IQClient {
  20. Oracle private oracle;
  21. event LogError(bytes32 description);
  22. function OracleB1IQClient(address addr) external payable {
  23. oracle = Oracle(addr);
  24. getIntraday("IBM", now);
  25. }
  26. function getIntraday(bytes32 ric, uint256 timestamp) public {
  27. uint256 id = oracle.initRequest(0, this.handleSuccess, this.handleFailure);
  28. oracle.addArgumentToRequestString(id, "symbol", ric);
  29. oracle.addArgumentToRequestUint(id, "timestamp", timestamp);
  30. oracle.executeRequest(id);
  31. }
  32. function handleSuccess(uint256 id) public {
  33. assert(msg.sender == address(oracle));
  34. bytes32 ric = oracle.getResponseString(id, "symbol");
  35. uint256 open = oracle.getResponseUint(id, "open");
  36. uint256 high = oracle.getResponseUint(id, "high");
  37. uint256 low = oracle.getResponseUint(id, "low");
  38. uint256 close = oracle.getResponseUint(id, "close");
  39. uint256 bid = oracle.getResponseUint(id, "bid");
  40. uint256 ask = oracle.getResponseUint(id, "ask");
  41. uint256 timestamp = oracle.getResponseUint(id, "timestamp");
  42. oracle.deleteResponse(id);
  43. // Do something with the price data
  44. }
  45. function handleFailure(uint256 id) public {
  46. assert(msg.sender == address(oracle));
  47. bytes32 error = oracle.getResponseError(id);
  48. oracle.deleteResponse(id);
  49. emit LogError(error);
  50. }
  51. }

The data request is initiated using the initRequest function, which allows the query type (in this example, a request for an intraday price) to be specified, in addition to two callback functions. This returns a uint256 identifier that can then be used to provide additional arguments. The addArgumentToRequestString function is used to specify the Reuters Instrument Code (RIC), here for IBM stock, and addArgumentToRequestUint allows the timestamp to be specified. Now, passing in an alias for block.timestamp will retrieve the current price for IBM. The request is then executed by the executeRequest function. Once the request has been processed, the oracle contract will call the onSuccess callback function with the query identifier, allowing the resulting data to be retrieved; in the event of retrieval failure, the onFailure callback will return an error code instead. The available fields that can be retrieved on success include open, high, low, close (OHLC), and bid/ask prices.