Skip to content

Latest commit

 

History

History
187 lines (133 loc) · 9.13 KB

File metadata and controls

187 lines (133 loc) · 9.13 KB

Contributing

Thank you for your interest in improving the Chainlink External Adapter codebase! The steps below support development of original adapters, but you are more than welcome to contribute to existing adapters as well. When opening a PR, please invite smartcontractkit/solutions-engineering to review the changes.

Important

If you're thinking of building your own for a production use case, please contact chainlink_data_feeds@smartcontract.com first to discuss.

Table of Contents

  1. Creating A New Adapter
  2. Input
  3. Output
  4. Adding Provider API Rate Limits
  5. Mock Integration Testing
  6. Running Integration Tests
  7. Generating Changesets
  8. Common Patterns
  9. Logging Censorship
  10. Framework Development

Creating A New Adapter

To get started use the new script with one argument:

$ yarn new [template-type]

This will start interactive command line interface, where you can provide additional information.

Parameter Description Options Default
template-type the name of the template to use composite, source, target source

For example

$ yarn new source

You can open a PR with the New EA PR Template by replacing <branch> in this URL: https://github.com/smartcontractkit/external-adapters-js/compare/main...?quick_pull=1&template=new_ea_pr_template.md

Please refer to this guide that explains in more detail how to create a new adapter.

Input

When flux monitor or OCR jobs from the core Chainlink node post to external adapters, the request body looks as follows:

{
  "id": "2cae6a10e5184aa685c3428964b02418",
  "data": { "from": "ETH", "to": "USD" },
  "meta": {
    "latestAnswer": 39307000000,
    "updatedAt": 1616448197
  }
}

The updatedAt field is a unix timestamp representing when the latestAnswer was computed.

Output

The External Adapter will do some processing, often request data from an API, and return the following response structure:

  {
    "jobRunID": "2cae6a10e5184aa685c3428964b02418",
    "statusCode": 200,
    "data": {
      "result": 3000 // Result for Flux Monitor jobs.
      // ... Response data
    },
    "result": 3000, // Result for OCR jobs.
    "maxAge": 222, // [OPTIONAL] metadata for how long the request is cached for
    "debug": { // [OPTIONAL]
      // Additional metadata from the EA framework used for tracking metrics
    }
  }

Adding Provider API Rate Limits

When adding a new adapter, the tiers from that provider will need to be added to the Adapter class as a parameter.

Mock Integration Testing

We use Nock for intercepting HTTP requests in integration tests and returning mock data. To create a fixture, you'll need to make a real request to the data provider's API.

Testing HTTP Requests

  1. Setup the test, see the coingecko test for a code sample.
  2. Make a request to the Data Provider and copy the response. For HTTP requests, you can use tools like curl. Change/mock any sensitive data that it contains.
  3. Using the mock data from step 2, write a fixtures.ts file to return the mock data instead.
  4. Run the tests. API requests should now be intercepted and mocked using the fixture data.

Testing WebSocket Messages

  1. Setup the test, see the ncfx test for a code sample.
  2. Connect and send an authentication message request to Data Provider. Change/mock any sensitive data in the response and copy it. For websocket requests, you can use tools like wscat.
  3. Make a request to the Data Provider to fetch actual data, and copy the websocket messages. Change/mock any sensitive data that it contains.
  4. Using the copied messages in step 2 and 3, write a fixtures.ts file. Create mock function in fixtures.ts that will return mocked authentication and data messages. See ncfx test fixtures example.
  5. Run the tests. Websocket requests should now be intercepted and mocked using the fixture data.

For more information on Jest, see the Jest docs.

For more information on how to write integration tests, see ea-framework-js docs.

Testing on-chain contracts

There are generally two ways to mock the request and response from ether.js

  1. You can mock the library itself, see example
  2. Or you can mock the actual underlying request/response. You do that by logging the request and response of a real call and use them as your mock objects, see example

You would need to replace ethers.providers.JsonRpcProvider with

class LoggingProvider extends ethers.providers.JsonRpcProvider {
    send(method: string, parameters: any): Promise<any> {
        console.log(">>>", method, parameters);
        return super.send(method, parameters).then((result) => {
            console.log("<<<", result);
            return result;
        });
    }
}

Then you can spin up the EA with real request and get the request and response objects.

Generating Changesets

When making changes to an adapter, a changeset should be added for each feature or bug fix introduced. For each "unit" of changes, follow these steps to determine the packages affected, the version upgrade, and finally the changelog text. If you make a mistake, simply delete the files created in .changeset/ and try again:

  1. Run yarn changeset (from the root directory) to open a list of packages. You can filter packages by typing a string to match part of a package name (ex. type coinm to match @chainlink/coinmarketcap-adapter and @chainlink/coinmetrics-adapter). Use the up and down arrows to traverse the list and use space to select and unselect packages.
  2. After selecting all packages affected by your change, press enter to determine the version level change for each package. Use up, down, and space to select the packages for each level of change, using enter to move through each level. This starts with Major, then goes to Minor, then any remaining unselected packages will have Patch applied.
  3. In the final step, add a text summary that will be added to the CHANGELOG.md for every package when changesets are consumed.
  4. Once the files in .changeset/ have been created, add them to your branch to include them in the final PR.

Common Patterns

  • Use BigNumber when operating on large integers
  • Use Decimal.js for all floating point operations. Decimal.js uses a precision of 20 by default, but we may lose some precision with really large numbers, so please update to a higher precision before usage:
Decimal.set({ precision: 100 })

Framework Development

Any new EAs should be developed using the EA Framework for base classes. Follow these steps if you would like to test changes to the EA Framework:

  1. Clone external-adapters-js and ea-framework-js:
~ git clone https://github.com/smartcontractkit/external-adapters-js.git
~ git clone https://github.com/smartcontractkit/ea-framework-js.git
  1. Create the portal:
~ cd ea-framework-js/ && yarn portal-path
  1. Replace the current version of the framework in package.json of a given adapter with the portal path printed in the previous step. For example:
    "@chainlink/external-adapter-framework": "1.0.0",
    // -->
    "@chainlink/external-adapter-framework": "portal:/Users/username/ea-framework-js/dist/src",
  1. Make the desired changes to ea-framework-js and rebuild:
~/ea-framework-js yarn build
  1. Run the desired adapter to test:
~/ea-framework-js cd ../external-adapters-js/ && yarn && yarn setup && cd packages/sources/<adapter>
~/external-adapters-js/packages/sources/<adapter> yarn start