diff --git a/example/src/endpoint/example.ts b/example/src/endpoint/example.ts index 52d3da607f..3337c5c9d3 100644 --- a/example/src/endpoint/example.ts +++ b/example/src/endpoint/example.ts @@ -8,6 +8,7 @@ const customError = (data: any) => data.Response === 'Error' const customParams = { base: ['base', 'from', 'coin'], quote: ['quote', 'to', 'market'], + field: false, } export const execute: ExecuteWithConfig = async (request, config) => { @@ -17,6 +18,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const jobRunID = validator.validated.id const base = validator.validated.data.base const quote = validator.validated.data.quote + const field = validator.validated.data.field || 'price' const url = `price` const params = { @@ -24,13 +26,13 @@ export const execute: ExecuteWithConfig = async (request, config) => { quote, } - const reqConfig = { ...config.api, params, url } + const options = { ...config.api, params, url } - const response = await Requester.request(reqConfig, customError) - const result = Requester.validateResultNumber(response.data, ['price']) + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [field]) return Requester.success(jobRunID, { - data: { result }, + data: config.verbose ? { ...response.data, result } : { result }, result, status: 200, }) diff --git a/trueusd/.eslintrc.js b/trueusd/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/trueusd/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/trueusd/README.md b/trueusd/README.md index 0bc7bd7d2e..5ab5f1f6a2 100644 --- a/trueusd/README.md +++ b/trueusd/README.md @@ -1,14 +1,35 @@ # Chainlink External Adapter for TrueUSD -## Total Supply API +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [trueusd](#TrueUSD-Endpoint) | `trueusd` | + +--- + +## TrueUSD Endpoint https://core-api.real-time-attest.trustexplorer.io/trusttoken/TrueUSD -## Input Params +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-------------------------------------------------: | :------------------------: | :----------: | +| | `field` | The data point to return from the API response data | `totalTrust`, `totalToken` | `totalTrust` | + +### Sample Input -- `field`: The data field to return. (defaults to `totalTrust`, one of `totalToken`, `totalTrust`) +```json +{ + "id": "1", + "data": { + "field": "totalToken" + } +} +``` -## Output +### Output ```json { diff --git a/trueusd/adapter.js b/trueusd/adapter.js deleted file mode 100644 index 68aa0642f6..0000000000 --- a/trueusd/adapter.js +++ /dev/null @@ -1,28 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customError = (data) => { - if (data.Response === 'Error') return true - return false -} - -const supplyParams = { - field: false, -} - -const execute = (input, callback) => { - const validator = new Validator(input, supplyParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - const url = 'https://core-api.real-time-attest.trustexplorer.io/trusttoken/TrueUSD' - const field = validator.validated.data.field || 'totalTrust' - - Requester.request(url, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['responseData', field]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/trueusd/index.js b/trueusd/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/trueusd/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const { expose } = require('@chainlink/ea-bootstrap') -const { execute } = require('./adapter') - -module.exports = expose(execute) diff --git a/trueusd/package.json b/trueusd/package.json index ab92392004..e4fd1a66b8 100644 --- a/trueusd/package.json +++ b/trueusd/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/trueusd-adapter", "version": "0.0.3", + "description": "Chainlink TrueUSD adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/trueusd/src/adapter.ts b/trueusd/src/adapter.ts new file mode 100644 index 0000000000..5c2e05b305 --- /dev/null +++ b/trueusd/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { trueusd } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case trueusd.NAME: { + return await trueusd.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/trueusd/src/config.ts b/trueusd/src/config.ts new file mode 100644 index 0000000000..022c293c2c --- /dev/null +++ b/trueusd/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'trueusd' +export const DEFAULT_BASE_URL = 'https://core-api.real-time-attest.trustexplorer.io' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/trueusd/src/endpoint/index.ts b/trueusd/src/endpoint/index.ts new file mode 100644 index 0000000000..a0f6f6754a --- /dev/null +++ b/trueusd/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as trueusd from './trueusd' diff --git a/trueusd/src/endpoint/trueusd.ts b/trueusd/src/endpoint/trueusd.ts new file mode 100644 index 0000000000..68a23d4d9a --- /dev/null +++ b/trueusd/src/endpoint/trueusd.ts @@ -0,0 +1,30 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'trueusd' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const field = validator.validated.data.field || 'totalTrust' + const url = '/trusttoken/TrueUSD' + + const options = { ...config.api, url } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['responseData', field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/trueusd/src/index.ts b/trueusd/src/index.ts new file mode 100644 index 0000000000..f0291472f0 --- /dev/null +++ b/trueusd/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'TRUEUSD' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/trueusd/test/adapter_test.js b/trueusd/test/adapter_test.js deleted file mode 100644 index ca05ce4055..0000000000 --- a/trueusd/test/adapter_test.js +++ /dev/null @@ -1,53 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { field: 'totalTrust' } }, - }, - { - name: 'id is supplied', - testData: { id: jobID, data: { field: 'totalTrust' } }, - }, - { - name: 'trust supply', - testData: { id: jobID, data: { field: 'totalToken' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown field', - testData: { id: jobID, data: { field: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/trueusd/test/trueusd.test.ts b/trueusd/test/trueusd.test.ts new file mode 100644 index 0000000000..ca02f2f3c6 --- /dev/null +++ b/trueusd/test/trueusd.test.ts @@ -0,0 +1,56 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: {} }, + }, + { + name: 'id is supplied', + testData: { id: jobID, data: { field: 'totalTrust' } }, + }, + { + name: 'trust supply', + testData: { id: jobID, data: { field: 'totalToken' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.isAbove(data.result, 0) + assert.isAbove(data.data.result, 0) + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown field', + testData: { id: jobID, data: { field: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/trueusd/tsconfig.json b/trueusd/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/trueusd/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "typeRoots": ["../node_modules/@types", "../typings", "./typings"] + }, + "include": ["src/**/*"], + "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] +}