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.
- Creating A New Adapter
- Input
- Output
- Adding Provider API Rate Limits
- Mock Integration Testing
- Running Integration Tests
- Generating Changesets
- Common Patterns
- Logging Censorship
- Framework Development
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 sourceYou 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.
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.
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
}
}When adding a new adapter, the tiers from that provider will need to be added to the Adapter class as a parameter.
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.
- Setup the test, see the coingecko test for a code sample.
- 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. - Using the mock data from step 2, write a
fixtures.tsfile to return the mock data instead. - Run the tests. API requests should now be intercepted and mocked using the fixture data.
- Setup the test, see the ncfx test for a code sample.
- 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.
- Make a request to the Data Provider to fetch actual data, and copy the websocket messages. Change/mock any sensitive data that it contains.
- Using the copied messages in step 2 and 3, write a
fixtures.tsfile. Create mock function infixtures.tsthat will return mocked authentication and data messages. See ncfx test fixtures example. - 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.
There are generally two ways to mock the request and response from ether.js
- You can mock the library itself, see example
- 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.
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:
- 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. typecoinmto match@chainlink/coinmarketcap-adapterand@chainlink/coinmetrics-adapter). Use theupanddownarrows to traverse the list and usespaceto select and unselect packages. - After selecting all packages affected by your change, press
enterto determine the version level change for each package. Useup,down, andspaceto select the packages for each level of change, usingenterto move through each level. This starts withMajor, then goes toMinor, then any remaining unselected packages will havePatchapplied. - In the final step, add a text summary that will be added to the
CHANGELOG.mdfor every package when changesets are consumed. - Once the files in
.changeset/have been created, add them to your branch to include them in the final PR.
- Use BigNumber when operating on large integers
- Use Decimal.js for all floating point operations.
Decimal.jsuses 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 })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:
- 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- Create the portal:
~ cd ea-framework-js/ && yarn portal-path- Replace the current version of the framework in
package.jsonof 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",- Make the desired changes to
ea-framework-jsand rebuild:
~/ea-framework-js yarn build- 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