Crypto Compare - Pricing Index

CryptoCompare is a global cryptocurrency market data provider, giving institutional and retail investors access to real-time, high-quality, reliable market and pricing data on 5,300+ coins and 240,000+ currency pairs. By aggregating and analyzing this data from globally recognized exchanges, and seamlessly integrating different datasets in the cryptocurrency price, CryptoCompare provides a comprehensive, holistic overview of the market

Peer to Peer payment system, DEFI applications mostly deal with currency pair value. It is important to understand, how it works before you offer the services to end users.

As a node operator/data feed provider, you should be able to set up a crypto-compare adapter which can bring the currency pair value to end customers easily. All it takes, sign-up, generate API key and setup adapter

Before you continue to read the article, please ensure you have "Running" plugin node, initiators.

Sign-up in Crypto-compare and get your API-Key. Choose FREE PLAN and it is most sufficient as they are offering 100,000 hits/month

Step1 - Deploy Oracle

Steps to be accomplished:

1) Go to Remix and open the Oracle.sol smart contract. The contents of this file will be very minimal. pragma solidity 0.4.24;

pragma solidity 0.4.24;
import "@goplugin/contracts/src/v0.4/Oracle.sol";

2) On the Compile tab, click the Compile button for Oracle.sol. Remix automatically selects the compiler version and language from the pragma line unless you select a specific version manually.

3) On the Deploy and Run tab, configure the following settings:

  • Select "Injected Web3" as your Environment. The JavaScript VM environment cannot access your oracle node.

  • Select the "Oracle" contract from the Contract menu.

  • Copy the PLI token contract address for the network(like Mainnet, Apothem as given below) you are using and paste it into the address_link field next to the Deploy button:

0xff7412ea7c8445c46a8254dfb557ac1e48094391

Compile the program and you should see "green tick"

Click Deploy. XDCPay prompts you to confirm the transaction.

If the transaction is successful, a new address displays in the Deployed Contracts section. Keep a note of the Oracle contract address (let's remember this address as OCA to refer). You need it later for your contract consumption.

Step2 - Fulfilment node address

Steps to be accomplished:

Find the address for your Plugin node and add it to the Oracle contract.

1) In the Plugin Operator GUI for your node, find and copy the address at the bottom of the Keys page in the Account addresses section.

Copy this address and pass it in “setFulfilmentPermission” method, with Boolean value “true” like below (without quotes) and click "setFulFillmentPermission" to initiate the transaction

This address basically talks to Oracle contract.

You should see a new transaction is created and successful.

Step3 - Create External-Adapter Bridge

For the demo purpose, we will setup our external adapter to pull the data from cryptocompare API. Follow along this tutorial to understand how this works

Setting up external initiator requires following steps to be performed

  • Git clone the repository

  • cd & npm install

  • Include the API endpoint & Parse the payload

  • Run the server

Steps to be accomplished:

Step 1 -

git clone https://github.com/GoPlugin/external-adapter-template.git

Step 2 -

cd external-adapter-template/cryptocompare_adapter

Step 3 -

npm install

Step 4 -

map the api endpoint

Description:

Requesting Data

When an external adapter receives a request from the Plugin node, the JSON payload will include the following objects:

  • data (Guaranteed to be present but may be empty)

  • meta (Optional, depends on job type)

  • responseURL (Optional, will be supplied if job supports asynchronous callbacks)

  • id (Optional, can be nice to use for EA logging to help debug job runs)

Returning Data

When the external adapter wants to return data immediately, it must include data in the returned JSON.

An example of the response data can look like:

{
  data: {
    result: '0.07261'
  }
}

Note - the external adapter that you are running, should be the same URL endpoint mapped in Bridge.

const { Requester, Validator } = require('@goplugin/external-adapter')
require("dotenv").config();

const customError = (data) => {
  if (data.Response === 'Error') return true
  return false
}

const customParams = {
  endpoint: ['endpoint']
}

const createRequest = (input, callback) => {

  const url = `https://min-api.cryptocompare.com/data/pricemulti?fsyms=${input.data.fromsystem}&tsyms=${input.data.tosystem}`

  const config = {
    url
  }

  if (process.env.API_KEY) {
    config.headers = {
      "api_key": process.env.API_KEY
    }
  }
  Requester.request(config, customError)
    .then(response => {
      
      //console.log("response value is ",response.data[input.data.fromsystem][input.data.tosystem]);
      
      const res = {
        data: {
          "result": response.data[input.data.fromsystem][input.data.tosystem].toString()
        }
      }
      callback(response.status, Requester.success(input.id, res));
    })
    .catch(error => {
      callback(500, Requester.errored(input.id, error))
    })
}

module.exports.createRequest = createRequest

Let's see what's in "index.js"

  • We are importing 'external-adapter' from goplugin npm repo and importing two methods such as Requester, Validator.

  • Importing "dotenv" module to access the .env file for sensitive info (API_KEY)

  • "customError" is a function to return either true or false if error occurs

  • "customParams" is a variable to declare custom parameters to pass on in URL - this can be customized accordingly

  • "createRequest" is the method actually doing heavy lifting.

    • URL is getting defined, since for cryptocompare node the endpoint is getting passed via smart contract the source & destination token is accessed via "input.data.fromsystem","input.data.tosystem"

    • Next, we are checking APIKEY if exists in .env parm, else it will not be applied.. For authenticated API, you have to pass APIKEY

    • Requester.request will scan for the payload from API endpoint and customized the output. In this example, we are checking "input.data.fromsystem","input.data.tosystem"

When you run PM2 start server.js, the server will by default run in port 5002 and just make sure the bridge is correctly pointed out to this port number.. For instance, this should be http://localhost:5002/

Define Bridge

You can add external adapters to a Plugin node by creating a bridge in the Node Operators Interface. Each bridge must have a unique name and a URL for the external adapter. If a job has a Bridge Task, the node searches for a bridge by name and uses that bridge as your external adapter. Bridge names are case insensitive.

To create a bridge on the node, go to the Create Bridge tab in the Node Operators Interface. Specify a name for the bridge, the URL for your external adapter, and optionally specify the minimum contract payment and number of confirmations for the bridge.

Bridge Name - Must be unique to your plugin node and it is user defined. For instance, if your external-adapter brings your temperature value. Name it as "Temperature" so you can easily refer back what does the bridge does

Bridge URL - It should be the URL where the external-adapter is running. Mostly try to set this up in your server where plugin node is running.

Minimum Contract Payment - Is a fee paid in PLI for the Plugin node making a call to the external adapter via the bridge. This fee is in addition to the fee specified at the global node level for processing job requests.

Confirmation - Can be kept as 0.

Note : If external-adapter is running in same server as your plugin node. Keeping the 'Bridge URL' as 'http://localhost:5002'.

Step4 - CryptoCompare login & API generation

You need API key from crypto compare site to interact with the API of cryptocompare site. To get your own customized API key follow the steps mentioned below.

Steps to be accomplished:

  1. Click on 'Log In/Sign Up' at the top right corner.

  2. You will get a Single sign on page as mentioned below, you can sign in using your Gmail id or Facebook id. Click on the 'SIGNUP' button to get registered with your Gmail/Facebook ID.

4. Once you signed up, check your mailbox for the activation link and click on the activation link.

5. After activating your account with cryptocompare go to the 'home' page and click on the 'API Keys' in the drop down under your profile.

6. Now click on the 'Create an API Key' button

7. Give a suitable name related to the task, click on the option as mentioned in the image below and press button 'Add'.

8. Select the 'other' option from the dropdown and hit 'Save' button.

9. Now your API key is generated, copy the API key and store the key in '.env' file as API_KEY=<your_copied_apikey> under cryptocompare_adapter folder

10. Run 'node server.js' inside cryptocompare_adapter folder

Step5 - Create a Job

Add Bridge to Job Spec

Bridge is an another task, which takes the control to external-adapter and perform the defined requirements and give back the results to the task services..

For instance, this job spec has set of task

  • cryptocompare(This is the name of your bridge which you set up)

  • copy

  • multiply

  • ethuin256

  • EthTx

For most of the job spec's, we ge the Multiply, ethUin256 & etHTX in place. But the other two(cryptocompare, copy) will be changed job to job.

in this example, the task is to call the bridge which is cryptocompare, then cryptocompare will in turn call the server(which runs exernal-adapter in http://localhost:5000/ endpoint) and bring the results.

Next task, will copy the data from the payload and send it to next task which is "multiply".

Multiply then remove the fractions by multiplying with the value given in the contract and ethuin256 task will convert the results into blockchain understandable format.

Finally, ethTx task will write the data onto blockchain.

NOTE: Change oracle_address in below mentioned JSON to your Orace address generated as the result of executing Oracle.sol in the beginning.

{
   "initiators": [{
	"type": "external",
	"params": {
		"name": "xdc",
		"body": {
			"endpoint": "xinfin-mainnet",
			"addresses": ["oracle_address"]
			}
	}
     }],
     "tasks": 
         [   {
	     "type": "cryptocompare"
	     },
	     {
	     "type": "copy",
		 "params": {
		    "copyPath": [
			"result"
			]
		 }
	     },
	     {
		"type": "multiply"
	     },
	     {
		"type": "ethuint256"
	     },
	     {
		"type": "EthTx"
	     }
	 ]
}

Similarly, bridge can perform any tasks and bring the value to prescribed format. Implementing the external-adapter - custom logic is with user.

Step6 - Run consumer.sol

Copy the below mentioned contract, below-mentioned in remix and paste the contents.

Consumer.sol

pragma solidity 0.4.24;

import "@goplugin/contracts/src/v0.4/vendor/Ownable.sol";
import "@goplugin/contracts/src/v0.4/PluginClient.sol";

contract Consumer is PluginClient, Ownable {
    
  //Initialize Oracle Payment     
  uint256 constant private ORACLE_PAYMENT = 0.1 * 10**18;
  uint256 public currentValue;

  //Initialize event RequestFulfilled   
  event RequestFulfilled(
    bytes32 indexed requestId,
    uint256 indexed currentVal
  );

  //Initialize event requestCreated   
  event requestCreated(address indexed requester,bytes32 indexed jobId, bytes32 indexed requestId);

  //Constructor to pass Pli Token Address during deployment
  constructor(address _pli) public Ownable() {
    setPluginToken(_pli);
  }

  //_fsysm should be the name of your source token from which you want the comparison 
  //_tsysm should be the name of your destinaiton token to which you need the comparison
  //_jobID should be tagged in Oracle
  //_oracle should be fulfiled with your plugin node address

  function requestData(address _oracle, string _jobId,string _fsysm,string _tsysm)
    public
    onlyOwner
    returns (bytes32 requestId)
  {
    Plugin.Request memory req = buildPluginRequest(stringToBytes32(_jobId), this, this.fulfill.selector);
    req.add("fromsystem",_fsysm);
    req.add("tosystem",_tsysm);
    req.addInt("times", 100);
    requestId = sendPluginRequestTo(_oracle, req, ORACLE_PAYMENT);
    emit requestCreated(msg.sender, stringToBytes32(_jobId), requestId);
  }

  //callBack function
  function fulfill(bytes32 _requestId, uint256 _currentval)
    public
    recordPluginFulfillment(_requestId)
  {
    emit RequestFulfilled(_requestId, _currentval);
    currentValue = _currentval;
  }

  function getPluginToken() public view returns (address) {
    return pluginTokenAddress();
  }

  //With draw pli can be invoked only by owner
  function withdrawPli() public onlyOwner {
    PliTokenInterface pli = PliTokenInterface(pluginTokenAddress());
    require(pli.transfer(msg.sender, pli.balanceOf(address(this))), "Unable to transfer");
  }

  //Cancel the existing request
  function cancelRequest(
    bytes32 _requestId,
    uint256 _payment,
    bytes4 _callbackFunctionId,
    uint256 _expiration
  )
    public
    onlyOwner
  {
    cancelPluginRequest(_requestId, _payment, _callbackFunctionId, _expiration);
  }

  //String to bytes to convert jobid to bytest32
  function stringToBytes32(string memory source) private pure returns (bytes32 result) {
    bytes memory tempEmptyStringTest = bytes(source);
    if (tempEmptyStringTest.length == 0) {
      return 0x0;
    }
    assembly { 
      result := mload(add(source, 32))
    }
  }

}

NOTE: For Demo purposes, we used ETH, and BTC as the source and destination tokens. Node operators are requested to give different and unique token pairs so that we can have many different varieties of pairs to be processed in our platforms, thereby we can get more job requests spanning across different token pairs.

Steps to fetch the comparison detail from cryptocompare API

Steps to be accomplished:

  1. In the compiler, choose the 0.4.24 version for the compiler option and compile, once the contract is compiled successfully you should deploy the contract.

2. While deploying select the 'ENVIRONMENT' as 'Injected Web3', select 'Consumer - contracts/Consumer.sol', paste '0xff7412ea7c8445c46a8254dfb557ac1e48094391' next to Deploy button and click on Deploy button.

3. After successful deployment copy the contract address of the deployment and paste it in 'At Address' and click on 'At Addressthe ' button.

4. Now copy the consumer contract address and fund the address with 0.1 PLI.

5. Once the funding is completed form a string with the respective details you stored "oracle_address", "job_spec_id", "SRC_Token". "DEST_Token" (eg: "0xaa9F8EF6e0dcf6C3F503075F38f13D10a8C5b1E9","a6c6ae51-4950-40a0-94ba-e52365e71ce1","ETH","BTC") then click on requestData.

6. After the transaction is done successfully, you can check it up by clicking 'currentValue' you can get the conversion value between your source and destination token.

Last updated