From 1f161ae8606e1c6b78bca410d1284da47293850f Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Wed, 6 Jan 2021 17:34:24 +0200 Subject: [PATCH 01/23] Added height(latest block number) as endpoint parameter on cryptoapis --- coinapi/test/adapter_test.js | 14 +++++++ cryptoapis/README.md | 78 +++--------------------------------- nomics/adapter.js | 43 ++++++++++++++++++++ 3 files changed, 62 insertions(+), 73 deletions(-) diff --git a/coinapi/test/adapter_test.js b/coinapi/test/adapter_test.js index c709757c12..97ce68e8e7 100644 --- a/coinapi/test/adapter_test.js +++ b/coinapi/test/adapter_test.js @@ -27,6 +27,20 @@ describe('execute', () => { name: 'coin/market lowercase', testData: { id: jobID, data: { coin: 'eth', market: 'usd' } }, }, + { + name: 'BTC testnet difficulty', + testData: { + id: jobID, + data: { blockchain: 'BTC', network: 'testnet', endpoint: 'difficulty' }, + }, + }, + { + name: 'BTC mainnet height', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'height' }, + }, + }, ] requests.forEach((req) => { diff --git a/cryptoapis/README.md b/cryptoapis/README.md index ebbab3c006..ce310e88fa 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -21,6 +21,11 @@ The adapter takes the following environment variables: - `blockchain` or `coin`: The blockchain to get difficulty from - `network`: The network of the blockchain to get difficulty from. Default: "mainnet" +### Height + +- `blockchain` or `coin`: The blockchain to get latest block number from +- `network`: The network of the blockchain to get latest block number from. Default: "mainnet" + ## Output ```json @@ -41,76 +46,3 @@ The adapter takes the following environment variables: "statusCode": 200 } ``` - -### Balance endpoint - -https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/btc/index#btc-address-info-endpoint - -- `dataPath`: Optional path where to find the addresses array, defaults to `result` -- `confirmations`: Optional confirmations param, defaults to `6` - -- `addresses`: Addresses to query - - { - - - `address`: Address to query - - `coin`: Optional currency to query, defaults to `btc`, one of `(btc|eth|etc|bch|ltc|dash|doge|btcv|zil)` - - `chain`: Optional chain to query, defaults to `mainnet`, one of `(mainnet|testnet)` - - } - -```json -{ - "id": "1", - "data": { - "addresses": [ - { - "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", - "chain": "testnet" - } - ], - "dataPath": "addresses" - } -} -``` - -### Output - -```json -{ - "jobRunID": "1", - "data": { - "responses": [ - { - "payload": { - "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", - "totalSpent": "0.0498", - "totalReceived": "131.40923575", - "balance": "131.35943575", - "txi": 1, - "txo": 1590, - "txsCount": 1587, - "addresses": ["n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF"] - } - } - ], - "result": [ - { - "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", - "chain": "testnet", - "coin": "btc", - "balance": "131.35943575" - } - ] - }, - "result": [ - { - "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", - "chain": "testnet", - "coin": "btc", - "balance": "13135943575" - } - ], - "statusCode": 200 -} -``` diff --git a/nomics/adapter.js b/nomics/adapter.js index c983369655..7ed3292970 100644 --- a/nomics/adapter.js +++ b/nomics/adapter.js @@ -2,7 +2,12 @@ const { Requester, Validator } = require('@chainlink/external-adapter') const { util } = require('@chainlink/ea-bootstrap') const ENDPOINT_PRICE = 'price' +<<<<<<< HEAD:nomics/adapter.js const ENDPOINT_MKTCAP = 'globalmarketcap' +======= +const ENDPOINT_DIFFICULTY = 'difficulty' +const ENDPOINT_HEIGHT = 'height' +>>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js const DEFAULT_ENDPOINT = ENDPOINT_PRICE @@ -50,6 +55,7 @@ const price = (jobRunID, input, callback) => { response.data.result = Requester.validateResultNumber(response.data, ['price']) callback(response.status, Requester.success(jobRunID, response)) }) +<<<<<<< HEAD:nomics/adapter.js .catch((error) => callback(500, Requester.errored(jobRunID, error))) } @@ -62,15 +68,45 @@ const globalMarketCap = (jobRunID, input, callback) => { const params = { key: util.getRandomRequiredEnv('API_KEY'), } +======= + .catch((error) => { + callback(500, Requester.errored(jobRunID, error)) + }) +} + +const latestBlockParams = { + blockchain: ['blockchain', 'coin'], + endpoint: false, + network: false, +} + +const latestBlock = (jobRunID, input, callback) => { + const validator = new Validator(input, latestBlockParams) + if (validator.error) return callback(validator.error.statusCode, validator.errored) + + const blockchain = validator.validated.data.blockchain + const network = validator.validated.data.network || 'mainnet' + const endpoint = validator.validated.data.endpoint + const url = `https://api.cryptoapis.io/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/blocks/latest` +>>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js const config = { url, params, } +<<<<<<< HEAD:nomics/adapter.js Requester.request(config, customError) .then((response) => { response.data.result = Requester.validateResultNumber(response.data, ['market_cap']) +======= + Requester.request(config) + .then((response) => { + response.data.result = Requester.validateResultNumber(response.data, [ + 'payload', + endpoint + ]) +>>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js callback(response.status, Requester.success(jobRunID, response)) }) .catch((error) => callback(500, Requester.errored(jobRunID, error))) @@ -81,6 +117,7 @@ const customParams = { } const execute = (input, callback) => { + // console.log(input) const validator = new Validator(input, customParams) if (validator.error) return callback(validator.error.statusCode, validator.errored) @@ -89,8 +126,14 @@ const execute = (input, callback) => { switch (endpoint.toLowerCase()) { case ENDPOINT_PRICE: return price(jobRunID, input, callback) +<<<<<<< HEAD:nomics/adapter.js case ENDPOINT_MKTCAP: return globalMarketCap(jobRunID, input, callback) +======= + case ENDPOINT_DIFFICULTY: + case ENDPOINT_HEIGHT: + return latestBlock(jobRunID, input, callback) +>>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js default: callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) } From 82403e886d500ee74a29bde643b24a837f7f8781 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Thu, 7 Jan 2021 12:53:15 +0200 Subject: [PATCH 02/23] Added height endpoint for blockchair --- cryptoid/test/adapter_test.js | 18 ++++++++++++++++++ nomics/adapter.js | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cryptoid/test/adapter_test.js b/cryptoid/test/adapter_test.js index 8521770914..effae21f6d 100644 --- a/cryptoid/test/adapter_test.js +++ b/cryptoid/test/adapter_test.js @@ -19,6 +19,24 @@ describe('execute', () => { name: 'coin', testData: { id: jobID, data: { coin: 'BTC' } }, }, + { + name: 'coin', + testData: { id: jobID, data: { coin: 'BTC' } }, + }, + { + name: 'BTC testnet difficulty', + testData: { + id: jobID, + data: { blockchain: 'BTC', network: 'testnet', endpoint: 'difficulty' }, + }, + }, + { + name: 'BTC mainnet height', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'height' }, + }, + }, ] requests.forEach((req) => { diff --git a/nomics/adapter.js b/nomics/adapter.js index 7ed3292970..55d8f954e3 100644 --- a/nomics/adapter.js +++ b/nomics/adapter.js @@ -76,7 +76,7 @@ const globalMarketCap = (jobRunID, input, callback) => { const latestBlockParams = { blockchain: ['blockchain', 'coin'], - endpoint: false, + endpoint: true, network: false, } From bcc9b3d5c1ee7e226459bdef7a63eec7d88f5a6e Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Thu, 7 Jan 2021 13:46:52 +0200 Subject: [PATCH 03/23] Added block height on endpoint on cryptoid --- blockchair/adapter-old.js | 46 +++++++++++++++++++++++++++++++++++ cryptoid/adapter.js | 10 ++++++-- cryptoid/test/adapter_test.js | 4 --- 3 files changed, 54 insertions(+), 6 deletions(-) create mode 100644 blockchair/adapter-old.js diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js new file mode 100644 index 0000000000..1ae242d8d8 --- /dev/null +++ b/blockchair/adapter-old.js @@ -0,0 +1,46 @@ +const { Requester, Validator } = require('@chainlink/external-adapter') +const { util } = require('@chainlink/ea-bootstrap') + +const customParams = { + blockchain: ['blockchain', 'coin'], + endpoint: false, +} + +const convertBlockchain = { + BTC: 'bitcoin', + BCH: 'bitcoin-cash', + BSV: 'bitcoin-sv', + ETH: 'ethereum', + LTC: 'litecoin', +} + +const convertEndpoint = { + height: 'blocks', +} + +const execute = (input, callback) => { + const validator = new Validator(input, customParams) + if (validator.error) return callback(validator.error.statusCode, validator.errored) + + const jobRunID = validator.validated.id + let blockchain = validator.validated.data.blockchain + if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] + let endpoint = validator.validated.data.endpoint || 'difficulty' + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + + const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` + const key = util.getRandomRequiredEnv('API_KEY') + + const params = {} + if (key.length > 0) params.key = key + + const config = { url, params } + Requester.request(config) + .then((response) => { + response.data.result = Requester.validateResultNumber(response.data, ['data', endpoint]) + callback(response.status, Requester.success(jobRunID, response)) + }) + .catch((error) => callback(500, Requester.errored(jobRunID, error))) +} + +module.exports.execute = execute diff --git a/cryptoid/adapter.js b/cryptoid/adapter.js index 26f50f070c..60fa3c8754 100644 --- a/cryptoid/adapter.js +++ b/cryptoid/adapter.js @@ -3,6 +3,12 @@ const { util } = require('@chainlink/ea-bootstrap') const customParams = { blockchain: ['blockchain', 'coin'], + endpoint: false +} + +const endpointToQ = { + difficulty : 'getdifficulty', + height : 'getblockcount' } const execute = (input, callback) => { @@ -11,10 +17,10 @@ const execute = (input, callback) => { const jobRunID = validator.validated.id const blockchain = validator.validated.data.blockchain.toLowerCase() + const endpoint = validator.validated.data.endpoint || 'difficulty' const url = `https://${blockchain}.cryptoid.info/${blockchain}/api.dws` const key = util.getRandomRequiredEnv('API_KEY') - const q = 'getdifficulty' - + const q = endpointToQ[endpoint] const params = { key, q } const config = { url, params } diff --git a/cryptoid/test/adapter_test.js b/cryptoid/test/adapter_test.js index effae21f6d..8ea9d2b035 100644 --- a/cryptoid/test/adapter_test.js +++ b/cryptoid/test/adapter_test.js @@ -19,10 +19,6 @@ describe('execute', () => { name: 'coin', testData: { id: jobID, data: { coin: 'BTC' } }, }, - { - name: 'coin', - testData: { id: jobID, data: { coin: 'BTC' } }, - }, { name: 'BTC testnet difficulty', testData: { From d318c3909b4cdbda4a8003c03862d86fd2d014fb Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Thu, 7 Jan 2021 21:32:05 +0200 Subject: [PATCH 04/23] Add similar (to other blockchain stats querying adapters) input params to json rpc adapter --- blockchair/adapter-old.js | 33 ++++++++++++++++++++++++++++++++- json-rpc/src/adapter.ts | 24 +++++++++++++++++++++++- json-rpc/test/btc.test.ts | 15 +++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js index 1ae242d8d8..0ab568a8da 100644 --- a/blockchair/adapter-old.js +++ b/blockchair/adapter-old.js @@ -18,16 +18,47 @@ const convertEndpoint = { height: 'blocks', } + +const stats = (jobRunID, input, callback) => { + const validator = new Validator(input, latestBlockParams) + if (validator.error) return callback(validator.error.statusCode, validator.errored) + + const blockchain = validator.validated.data.blockchain + const network = validator.validated.data.network || 'mainnet' + const endpoint = validator.validated.data.endpoint + const url = `https://api.cryptoapis.io/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/blocks/latest` + + const config = { + url, + headers: { + 'X-API-Key': process.env.API_KEY, + }, + } + + Requester.request(config) + .then((response) => { + response.data.result = Requester.validateResultNumber(response.data, [ + 'payload', + endpoint + ]) + callback(response.status, Requester.success(jobRunID, response)) + }) + .catch((error) => { + callback(500, Requester.errored(jobRunID, error)) + }) +} const execute = (input, callback) => { const validator = new Validator(input, customParams) if (validator.error) return callback(validator.error.statusCode, validator.errored) const jobRunID = validator.validated.id let blockchain = validator.validated.data.blockchain - if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] let endpoint = validator.validated.data.endpoint || 'difficulty' + + if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` const key = util.getRandomRequiredEnv('API_KEY') diff --git a/json-rpc/src/adapter.ts b/json-rpc/src/adapter.ts index 0acb205338..6445075345 100644 --- a/json-rpc/src/adapter.ts +++ b/json-rpc/src/adapter.ts @@ -5,16 +5,33 @@ const inputParams = { url: false, method: false, params: false, + blockchain: false, + coin: false, + endpoint: false, +} + +const convertEndpoint: {[key: string]: string} = { + 'height' : 'headers' } // Export function to integrate with Chainlink node export const execute: Execute = async (request) => { + console.log(request) const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error const url = process.env.RPC_URL || validator.validated.data.url || 'http://localhost:8545' - const method = validator.validated.data.method || '' + let method = validator.validated.data.method || '' const params = validator.validated.data.params + const blockchain = validator.validated.data.blockchain || validator.validated.data.coin + + let endpoint = validator.validated.data.endpoint + + if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { + if (!endpoint) endpoint = 'difficulty' + method = 'getblockchaininfo' + } const data = { id: request.id, @@ -36,5 +53,10 @@ export const execute: Execute = async (request) => { const response = await Requester.request(options) if (response.statusCode >= 400) throw response.data.error + if (endpoint) { + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + response.data.result = Requester.validateResultNumber(response.data, ["result", endpoint]) + } + return Requester.success(request.id, response) } diff --git a/json-rpc/test/btc.test.ts b/json-rpc/test/btc.test.ts index 6347fb7085..1f82893941 100644 --- a/json-rpc/test/btc.test.ts +++ b/json-rpc/test/btc.test.ts @@ -37,4 +37,19 @@ describe('Bitcoin client @integration', function () { assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) }) }) + + context('get height of btc blockchain', () => { + const req = { + id: jobID, + data: { + blockchain: 'btc', + endpoint: 'height' + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) }) From 516decf9ae3f25a8459711fe2f72efda2a5dc6fb Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 8 Jan 2021 14:31:28 +0200 Subject: [PATCH 05/23] Renamed endpoint to q. Added READMEs. Cleaned json-rpc. --- blockchair/README.md | 7 ++----- blockchair/adapter-old.js | 38 ++++------------------------------- coinapi/test/adapter_test.js | 14 +++++++++++++ cryptoapis/README.md | 12 ++++------- cryptoid/README.md | 15 +++++++------- cryptoid/adapter.js | 14 ++++++------- cryptoid/test/adapter_test.js | 6 +++--- json-rpc/README.md | 5 +++++ json-rpc/src/adapter.ts | 19 +++++++++--------- json-rpc/test/btc.test.ts | 6 +++--- nomics/adapter.js | 25 ++++++++++++++++++++--- 11 files changed, 81 insertions(+), 80 deletions(-) diff --git a/blockchair/README.md b/blockchair/README.md index d4088ab9aa..cde96a4c8b 100644 --- a/blockchair/README.md +++ b/blockchair/README.md @@ -9,11 +9,8 @@ The adapter takes the following environment variables: ## Input Params -- `endpoint`: The endpoint to use, one of (difficulty|balance). Defaults to `difficulty` - -### Difficulty endpoint - -- `blockchain` or `coin`: The blockchain to get difficulty from +- `blockchain` or `coin`: The blockchain to get stats from +- `endpoint`: The parameter to query for. Default: "difficulty" ### Output diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js index 0ab568a8da..092eb3421b 100644 --- a/blockchair/adapter-old.js +++ b/blockchair/adapter-old.js @@ -18,49 +18,19 @@ const convertEndpoint = { height: 'blocks', } - -const stats = (jobRunID, input, callback) => { - const validator = new Validator(input, latestBlockParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const blockchain = validator.validated.data.blockchain - const network = validator.validated.data.network || 'mainnet' - const endpoint = validator.validated.data.endpoint - const url = `https://api.cryptoapis.io/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/blocks/latest` - - const config = { - url, - headers: { - 'X-API-Key': process.env.API_KEY, - }, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'payload', - endpoint - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => { - callback(500, Requester.errored(jobRunID, error)) - }) -} const execute = (input, callback) => { const validator = new Validator(input, customParams) if (validator.error) return callback(validator.error.statusCode, validator.errored) const jobRunID = validator.validated.id let blockchain = validator.validated.data.blockchain - let endpoint = validator.validated.data.endpoint || 'difficulty' + let q = validator.validated.data.q || 'difficulty' if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + if (q in convertEndpoint) q = convertEndpoint[q] - const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` - const key = util.getRandomRequiredEnv('API_KEY') + const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') const params = {} if (key.length > 0) params.key = key @@ -68,7 +38,7 @@ const execute = (input, callback) => { const config = { url, params } Requester.request(config) .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['data', endpoint]) + response.data.result = Requester.validateResultNumber(response.data, ['data', q]) callback(response.status, Requester.success(jobRunID, response)) }) .catch((error) => callback(500, Requester.errored(jobRunID, error))) diff --git a/coinapi/test/adapter_test.js b/coinapi/test/adapter_test.js index 97ce68e8e7..ba3ad47380 100644 --- a/coinapi/test/adapter_test.js +++ b/coinapi/test/adapter_test.js @@ -41,6 +41,20 @@ describe('execute', () => { data: { blockchain: 'BTC', endpoint: 'height' }, }, }, + { + name: 'BTC mainnet height with q param', + testData: { + id: jobID, + data: { blockchain: 'BTC', q: 'height' }, + }, + }, + { + name: 'BTC mainnet difficulty with q param', + testData: { + id: jobID, + data: { blockchain: 'BTC', q: 'difficulty' }, + }, + }, ] requests.forEach((req) => { diff --git a/cryptoapis/README.md b/cryptoapis/README.md index ce310e88fa..11ec80ad52 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -16,15 +16,11 @@ The adapter takes the following environment variables: - `base`, `from`, or `coin`: The symbol or ID of the coin to query - `quote`, `to`, or `market`: The symbol or ID of the market to convert to -### Difficulty endpoint +### Blockchain stats -- `blockchain` or `coin`: The blockchain to get difficulty from -- `network`: The network of the blockchain to get difficulty from. Default: "mainnet" - -### Height - -- `blockchain` or `coin`: The blockchain to get latest block number from -- `network`: The network of the blockchain to get latest block number from. Default: "mainnet" +- `blockchain` or `coin`: The blockchain to get stats from +- `network`: The network of the blockchain to get stats from. Default: "mainnet" +- `endpoint`: The parameter to query for. Default: "difficulty" ## Output diff --git a/cryptoid/README.md b/cryptoid/README.md index e785f87aeb..fb112d67e6 100644 --- a/cryptoid/README.md +++ b/cryptoid/README.md @@ -2,17 +2,18 @@ ## Input Params -- `blockchain` or `coin`: The blockchain to get difficulty from +- `blockchain` or `coin`: The blockchain to get stats from +- `q`: The parameter to query for. Default: "difficulty" ## Output ```json { - "jobRunID":"1", - "data":{ - "result":19298087186262.6 - }, - "result":19298087186262.6, - "statusCode":200 + "jobRunID": "1", + "data": { + "result": 19298087186262.6 + }, + "result": 19298087186262.6, + "statusCode": 200 } ``` diff --git a/cryptoid/adapter.js b/cryptoid/adapter.js index 60fa3c8754..9242267e8c 100644 --- a/cryptoid/adapter.js +++ b/cryptoid/adapter.js @@ -3,12 +3,12 @@ const { util } = require('@chainlink/ea-bootstrap') const customParams = { blockchain: ['blockchain', 'coin'], - endpoint: false + q: false, } -const endpointToQ = { - difficulty : 'getdifficulty', - height : 'getblockcount' +const convertQ = { + difficulty: 'getdifficulty', + height: 'getblockcount', } const execute = (input, callback) => { @@ -17,10 +17,10 @@ const execute = (input, callback) => { const jobRunID = validator.validated.id const blockchain = validator.validated.data.blockchain.toLowerCase() - const endpoint = validator.validated.data.endpoint || 'difficulty' + let q = validator.validated.data.q || 'difficulty' const url = `https://${blockchain}.cryptoid.info/${blockchain}/api.dws` - const key = util.getRandomRequiredEnv('API_KEY') - const q = endpointToQ[endpoint] + const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') + if (q in convertQ) q = convertQ[q] const params = { key, q } const config = { url, params } diff --git a/cryptoid/test/adapter_test.js b/cryptoid/test/adapter_test.js index 8ea9d2b035..1414fea8d0 100644 --- a/cryptoid/test/adapter_test.js +++ b/cryptoid/test/adapter_test.js @@ -20,17 +20,17 @@ describe('execute', () => { testData: { id: jobID, data: { coin: 'BTC' } }, }, { - name: 'BTC testnet difficulty', + name: 'BTC mainnet difficulty', testData: { id: jobID, - data: { blockchain: 'BTC', network: 'testnet', endpoint: 'difficulty' }, + data: { blockchain: 'BTC' }, }, }, { name: 'BTC mainnet height', testData: { id: jobID, - data: { blockchain: 'BTC', endpoint: 'height' }, + data: { blockchain: 'BTC', q: 'height' }, }, }, ] diff --git a/json-rpc/README.md b/json-rpc/README.md index 8fa04e89f9..bab81c7d8b 100644 --- a/json-rpc/README.md +++ b/json-rpc/README.md @@ -46,6 +46,11 @@ function getBalanceExternalChain(string _account) } ``` +## Blockchain stats + +- `blockchain` or `coin`: The blockchain to get stats from +- `q`: The parameter to query for. Default: "difficulty" + ## Install Install dependencies diff --git a/json-rpc/src/adapter.ts b/json-rpc/src/adapter.ts index 6445075345..cabb10c262 100644 --- a/json-rpc/src/adapter.ts +++ b/json-rpc/src/adapter.ts @@ -7,18 +7,17 @@ const inputParams = { params: false, blockchain: false, coin: false, - endpoint: false, + q: false, } -const convertEndpoint: {[key: string]: string} = { - 'height' : 'headers' +const convertQ: { [key: string]: string } = { + height: 'headers', } // Export function to integrate with Chainlink node export const execute: Execute = async (request) => { - console.log(request) const validator = new Validator(request, inputParams) - + if (validator.error) throw validator.error const url = process.env.RPC_URL || validator.validated.data.url || 'http://localhost:8545' @@ -26,10 +25,10 @@ export const execute: Execute = async (request) => { const params = validator.validated.data.params const blockchain = validator.validated.data.blockchain || validator.validated.data.coin - let endpoint = validator.validated.data.endpoint + let q = validator.validated.data.q if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { - if (!endpoint) endpoint = 'difficulty' + if (!q) q = 'difficulty' method = 'getblockchaininfo' } @@ -53,9 +52,9 @@ export const execute: Execute = async (request) => { const response = await Requester.request(options) if (response.statusCode >= 400) throw response.data.error - if (endpoint) { - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] - response.data.result = Requester.validateResultNumber(response.data, ["result", endpoint]) + if (q) { + if (q in convertQ) q = convertQ[q] + response.data.result = Requester.validateResultNumber(response.data, ['result', q]) } return Requester.success(request.id, response) diff --git a/json-rpc/test/btc.test.ts b/json-rpc/test/btc.test.ts index 1f82893941..90c2f0412c 100644 --- a/json-rpc/test/btc.test.ts +++ b/json-rpc/test/btc.test.ts @@ -24,11 +24,11 @@ describe('Bitcoin client @integration', function () { }) }) - context('getinfo', () => { + context('getblockchaininfo', () => { const req = { id: jobID, data: { - method: 'getinfo', + method: 'getblockchaininfo', }, } @@ -43,7 +43,7 @@ describe('Bitcoin client @integration', function () { id: jobID, data: { blockchain: 'btc', - endpoint: 'height' + q: 'height', }, } diff --git a/nomics/adapter.js b/nomics/adapter.js index 55d8f954e3..985d717ba5 100644 --- a/nomics/adapter.js +++ b/nomics/adapter.js @@ -46,7 +46,13 @@ const price = (jobRunID, input, callback) => { const config = { url, +<<<<<<< HEAD:nomics/adapter.js params, +======= + headers: { + 'X-API-Key': process.env.API_KEY || util.getRandomRequiredEnv('API_KEY'), + }, +>>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js } Requester.request(config, customError) @@ -76,7 +82,8 @@ const globalMarketCap = (jobRunID, input, callback) => { const latestBlockParams = { blockchain: ['blockchain', 'coin'], - endpoint: true, + endpoint: false, + q: false, network: false, } @@ -86,13 +93,19 @@ const latestBlock = (jobRunID, input, callback) => { const blockchain = validator.validated.data.blockchain const network = validator.validated.data.network || 'mainnet' - const endpoint = validator.validated.data.endpoint + const q = validator.validated.data.q || validator.validated.data.endpoint const url = `https://api.cryptoapis.io/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/blocks/latest` >>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js const config = { url, +<<<<<<< HEAD:nomics/adapter.js params, +======= + headers: { + 'X-API-Key': process.env.API_KEY || util.getRandomRequiredEnv('API_KEY'), + }, +>>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js } <<<<<<< HEAD:nomics/adapter.js @@ -102,11 +115,15 @@ const latestBlock = (jobRunID, input, callback) => { ======= Requester.request(config) .then((response) => { +<<<<<<< HEAD:nomics/adapter.js response.data.result = Requester.validateResultNumber(response.data, [ 'payload', endpoint ]) >>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js +======= + response.data.result = Requester.validateResultNumber(response.data, ['payload', q]) +>>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js callback(response.status, Requester.success(jobRunID, response)) }) .catch((error) => callback(500, Requester.errored(jobRunID, error))) @@ -114,6 +131,7 @@ const latestBlock = (jobRunID, input, callback) => { const customParams = { endpoint: false, + q: false, } const execute = (input, callback) => { @@ -122,7 +140,8 @@ const execute = (input, callback) => { if (validator.error) return callback(validator.error.statusCode, validator.errored) const jobRunID = validator.validated.id - const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + const endpoint = + validator.validated.data.endpoint || validator.validated.data.q || DEFAULT_ENDPOINT switch (endpoint.toLowerCase()) { case ENDPOINT_PRICE: return price(jobRunID, input, callback) From e368308e2cb6caab8af93a22c5e7c673711f393e Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 8 Jan 2021 19:47:25 +0200 Subject: [PATCH 06/23] reverted q to endpoint --- blockchair/adapter-old.js | 6 ++-- coinapi/test/adapter_test.js | 14 -------- cryptoid/adapter.js | 8 ++--- cryptoid/test/adapter_test.js | 6 ++-- metalsapi/test/adapter_test.js | 18 ++++++++++ nomics/adapter.js | 64 +--------------------------------- 6 files changed, 29 insertions(+), 87 deletions(-) diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js index 092eb3421b..a8551d0de1 100644 --- a/blockchair/adapter-old.js +++ b/blockchair/adapter-old.js @@ -24,10 +24,10 @@ const execute = (input, callback) => { const jobRunID = validator.validated.id let blockchain = validator.validated.data.blockchain - let q = validator.validated.data.q || 'difficulty' + let endpoint = validator.validated.data.endpoint || 'difficulty' if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] - if (q in convertEndpoint) q = convertEndpoint[q] + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') @@ -38,7 +38,7 @@ const execute = (input, callback) => { const config = { url, params } Requester.request(config) .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['data', q]) + response.data.result = Requester.validateResultNumber(response.data, ['data', endpoint]) callback(response.status, Requester.success(jobRunID, response)) }) .catch((error) => callback(500, Requester.errored(jobRunID, error))) diff --git a/coinapi/test/adapter_test.js b/coinapi/test/adapter_test.js index ba3ad47380..97ce68e8e7 100644 --- a/coinapi/test/adapter_test.js +++ b/coinapi/test/adapter_test.js @@ -41,20 +41,6 @@ describe('execute', () => { data: { blockchain: 'BTC', endpoint: 'height' }, }, }, - { - name: 'BTC mainnet height with q param', - testData: { - id: jobID, - data: { blockchain: 'BTC', q: 'height' }, - }, - }, - { - name: 'BTC mainnet difficulty with q param', - testData: { - id: jobID, - data: { blockchain: 'BTC', q: 'difficulty' }, - }, - }, ] requests.forEach((req) => { diff --git a/cryptoid/adapter.js b/cryptoid/adapter.js index 9242267e8c..62991a2bf4 100644 --- a/cryptoid/adapter.js +++ b/cryptoid/adapter.js @@ -3,10 +3,10 @@ const { util } = require('@chainlink/ea-bootstrap') const customParams = { blockchain: ['blockchain', 'coin'], - q: false, + endpoint: false, } -const convertQ = { +const endpointToQ = { difficulty: 'getdifficulty', height: 'getblockcount', } @@ -17,10 +17,10 @@ const execute = (input, callback) => { const jobRunID = validator.validated.id const blockchain = validator.validated.data.blockchain.toLowerCase() - let q = validator.validated.data.q || 'difficulty' + const endpoint = validator.validated.data.endpoint || 'difficulty' const url = `https://${blockchain}.cryptoid.info/${blockchain}/api.dws` const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') - if (q in convertQ) q = convertQ[q] + const q = endpointToQ[endpoint] const params = { key, q } const config = { url, params } diff --git a/cryptoid/test/adapter_test.js b/cryptoid/test/adapter_test.js index 1414fea8d0..5d0cbebfc3 100644 --- a/cryptoid/test/adapter_test.js +++ b/cryptoid/test/adapter_test.js @@ -20,17 +20,17 @@ describe('execute', () => { testData: { id: jobID, data: { coin: 'BTC' } }, }, { - name: 'BTC mainnet difficulty', + name: 'BTC difficulty', testData: { id: jobID, data: { blockchain: 'BTC' }, }, }, { - name: 'BTC mainnet height', + name: 'BTC height', testData: { id: jobID, - data: { blockchain: 'BTC', q: 'height' }, + data: { blockchain: 'BTC', endpoint: 'height' }, }, }, ] diff --git a/metalsapi/test/adapter_test.js b/metalsapi/test/adapter_test.js index 0751ac7893..6c40840fc7 100644 --- a/metalsapi/test/adapter_test.js +++ b/metalsapi/test/adapter_test.js @@ -19,6 +19,24 @@ describe('execute', () => { name: 'from/to', testData: { id: jobID, data: { from: 'CHF', to: 'USD' } }, }, + { + name: 'coin', + testData: { id: jobID, data: { coin: 'BTC' } }, + }, + { + name: 'BTC difficulty', + testData: { + id: jobID, + data: { blockchain: 'BTC' }, + }, + }, + { + name: 'BTC height', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'height' }, + }, + }, ] requests.forEach((req) => { diff --git a/nomics/adapter.js b/nomics/adapter.js index 985d717ba5..c983369655 100644 --- a/nomics/adapter.js +++ b/nomics/adapter.js @@ -2,12 +2,7 @@ const { Requester, Validator } = require('@chainlink/external-adapter') const { util } = require('@chainlink/ea-bootstrap') const ENDPOINT_PRICE = 'price' -<<<<<<< HEAD:nomics/adapter.js const ENDPOINT_MKTCAP = 'globalmarketcap' -======= -const ENDPOINT_DIFFICULTY = 'difficulty' -const ENDPOINT_HEIGHT = 'height' ->>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js const DEFAULT_ENDPOINT = ENDPOINT_PRICE @@ -46,13 +41,7 @@ const price = (jobRunID, input, callback) => { const config = { url, -<<<<<<< HEAD:nomics/adapter.js params, -======= - headers: { - 'X-API-Key': process.env.API_KEY || util.getRandomRequiredEnv('API_KEY'), - }, ->>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js } Requester.request(config, customError) @@ -61,7 +50,6 @@ const price = (jobRunID, input, callback) => { response.data.result = Requester.validateResultNumber(response.data, ['price']) callback(response.status, Requester.success(jobRunID, response)) }) -<<<<<<< HEAD:nomics/adapter.js .catch((error) => callback(500, Requester.errored(jobRunID, error))) } @@ -74,56 +62,15 @@ const globalMarketCap = (jobRunID, input, callback) => { const params = { key: util.getRandomRequiredEnv('API_KEY'), } -======= - .catch((error) => { - callback(500, Requester.errored(jobRunID, error)) - }) -} - -const latestBlockParams = { - blockchain: ['blockchain', 'coin'], - endpoint: false, - q: false, - network: false, -} - -const latestBlock = (jobRunID, input, callback) => { - const validator = new Validator(input, latestBlockParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const blockchain = validator.validated.data.blockchain - const network = validator.validated.data.network || 'mainnet' - const q = validator.validated.data.q || validator.validated.data.endpoint - const url = `https://api.cryptoapis.io/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/blocks/latest` ->>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js const config = { url, -<<<<<<< HEAD:nomics/adapter.js params, -======= - headers: { - 'X-API-Key': process.env.API_KEY || util.getRandomRequiredEnv('API_KEY'), - }, ->>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js } -<<<<<<< HEAD:nomics/adapter.js Requester.request(config, customError) .then((response) => { response.data.result = Requester.validateResultNumber(response.data, ['market_cap']) -======= - Requester.request(config) - .then((response) => { -<<<<<<< HEAD:nomics/adapter.js - response.data.result = Requester.validateResultNumber(response.data, [ - 'payload', - endpoint - ]) ->>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js -======= - response.data.result = Requester.validateResultNumber(response.data, ['payload', q]) ->>>>>>> Renamed endpoint to q. Added READMEs. Cleaned json-rpc.:cryptoapis/adapter.js callback(response.status, Requester.success(jobRunID, response)) }) .catch((error) => callback(500, Requester.errored(jobRunID, error))) @@ -131,28 +78,19 @@ const latestBlock = (jobRunID, input, callback) => { const customParams = { endpoint: false, - q: false, } const execute = (input, callback) => { - // console.log(input) const validator = new Validator(input, customParams) if (validator.error) return callback(validator.error.statusCode, validator.errored) const jobRunID = validator.validated.id - const endpoint = - validator.validated.data.endpoint || validator.validated.data.q || DEFAULT_ENDPOINT + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint.toLowerCase()) { case ENDPOINT_PRICE: return price(jobRunID, input, callback) -<<<<<<< HEAD:nomics/adapter.js case ENDPOINT_MKTCAP: return globalMarketCap(jobRunID, input, callback) -======= - case ENDPOINT_DIFFICULTY: - case ENDPOINT_HEIGHT: - return latestBlock(jobRunID, input, callback) ->>>>>>> Added height(latest block number) as endpoint parameter on cryptoapis:cryptoapis/adapter.js default: callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) } From ee7f4454e523638bd38a81f085d26b93d5c94fef Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Mon, 11 Jan 2021 08:47:25 +0200 Subject: [PATCH 07/23] removed process.env.API_KEY --- blockchair/adapter-old.js | 2 +- cryptoid/adapter.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js index a8551d0de1..b4cd3b10a5 100644 --- a/blockchair/adapter-old.js +++ b/blockchair/adapter-old.js @@ -30,7 +30,7 @@ const execute = (input, callback) => { if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` - const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') + const key = util.getRandomRequiredEnv('API_KEY') const params = {} if (key.length > 0) params.key = key diff --git a/cryptoid/adapter.js b/cryptoid/adapter.js index 62991a2bf4..89cd61992e 100644 --- a/cryptoid/adapter.js +++ b/cryptoid/adapter.js @@ -19,7 +19,7 @@ const execute = (input, callback) => { const blockchain = validator.validated.data.blockchain.toLowerCase() const endpoint = validator.validated.data.endpoint || 'difficulty' const url = `https://${blockchain}.cryptoid.info/${blockchain}/api.dws` - const key = process.env.API_KEY || util.getRandomRequiredEnv('API_KEY') + const key = util.getRandomRequiredEnv('API_KEY') const q = endpointToQ[endpoint] const params = { key, q } const config = { url, params } From bff06696270a508eb3389ceb6c872e9aca0b13e5 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Mon, 11 Jan 2021 09:12:46 +0200 Subject: [PATCH 08/23] reverted json-rpc, kept the directly changed implementation for reference --- bitcoin-json-rpc-old/.eslintrc.js | 3 + bitcoin-json-rpc-old/README.md | 97 +++++++++ bitcoin-json-rpc-old/package.json | 39 ++++ bitcoin-json-rpc-old/src/adapter.ts | 61 ++++++ bitcoin-json-rpc-old/src/index.ts | 6 + bitcoin-json-rpc-old/test/aion.test.ts | 113 ++++++++++ bitcoin-json-rpc-old/test/btc.test.ts | 55 +++++ bitcoin-json-rpc-old/test/eth.test.ts | 244 ++++++++++++++++++++++ bitcoin-json-rpc-old/test/zilliqa.test.ts | 86 ++++++++ bitcoin-json-rpc-old/tsconfig.json | 10 + json-rpc/README.md | 5 - json-rpc/src/adapter.ts | 23 +- json-rpc/test/btc.test.ts | 19 +- 13 files changed, 717 insertions(+), 44 deletions(-) create mode 100644 bitcoin-json-rpc-old/.eslintrc.js create mode 100644 bitcoin-json-rpc-old/README.md create mode 100644 bitcoin-json-rpc-old/package.json create mode 100644 bitcoin-json-rpc-old/src/adapter.ts create mode 100644 bitcoin-json-rpc-old/src/index.ts create mode 100644 bitcoin-json-rpc-old/test/aion.test.ts create mode 100644 bitcoin-json-rpc-old/test/btc.test.ts create mode 100644 bitcoin-json-rpc-old/test/eth.test.ts create mode 100644 bitcoin-json-rpc-old/test/zilliqa.test.ts create mode 100644 bitcoin-json-rpc-old/tsconfig.json diff --git a/bitcoin-json-rpc-old/.eslintrc.js b/bitcoin-json-rpc-old/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/bitcoin-json-rpc-old/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/bitcoin-json-rpc-old/README.md b/bitcoin-json-rpc-old/README.md new file mode 100644 index 0000000000..bab81c7d8b --- /dev/null +++ b/bitcoin-json-rpc-old/README.md @@ -0,0 +1,97 @@ +# General JSON-RPC External Adapter for Chainlink + +- Should work for any JSON RPC supported endpoint (includes tests for a few major projects which support JSON RPC commands) +- Supports AWS Lambda and GCP Functions +- Blockchain clients can sign and send transactions if wallet is unlocked +- Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) + +A JSON-RPC request is typically formatted like this: + +```JSON +{ + "jsonrpc": "2.0", + "method": "some_method", + "params": [ + "some_param", + "another_param" + ], + "id": 1 +} +``` + +What this adapter does is allow you to specify the `"method"` and `"params"` values in a Chainlink request and receive the result (format example below) back to the Chainlink node for further processing. + +```JSON +{ + "id":1, + "jsonrpc": "2.0", + "result": "some_result" +} +``` + +In Solidity, a Chainlink request can be made to an external chain for the balance of a given account with the following example: + +```javascript +function getBalanceExternalChain(string _account) + public + onlyOwner +{ + Chainlink.Request memory req = newRequest(JOB_ID, this, this.fulfillRPCCall.selector); + req.add("method", "eth_getBalance"); + string[] memory params = new string[](2); + path[0] = _account; + path[1] = "latest"; + req.addStringArray("params", params); + chainlinkRequest(req, ORACLE_PAYMENT); +} +``` + +## Blockchain stats + +- `blockchain` or `coin`: The blockchain to get stats from +- `q`: The parameter to query for. Default: "difficulty" + +## Install + +Install dependencies + +```bash +npm install +``` + +Set the `RPC_URL` environment variable to your client URL. + +## Testing + +Testing is dependent on the type of node you're connecting to. You can set a local environment variable `RPC_URL` to point to an RPC connection. Otherwise, the adapter will default to `"http://localhost:8545"`. + +RPC Address and Port Defaults: + +- Ethereum: http://localhost:8545 +- AION: http://localhost:8545 +- BTC: (bitcoind) http://localhost:8332 (btcd) http://localhost:8334 +- Zilliqa: http://localhost:4201 + +For Ethereum and any Geth clone (should work with Parity as well): + +```bash +npm run test:eth +``` + +For Bitcoin: + +```bash +npm run test:btc +``` + +For AION: + +```bash +npm run test:aion +``` + +For Zilliqa: + +```bash +npm run test:zilliqa +``` diff --git a/bitcoin-json-rpc-old/package.json b/bitcoin-json-rpc-old/package.json new file mode 100644 index 0000000000..bd3b459818 --- /dev/null +++ b/bitcoin-json-rpc-old/package.json @@ -0,0 +1,39 @@ +{ + "name": "@chainlink/json-rpc-adapter", + "version": "0.0.1", + "description": "", + "author": "Thomas Hodges (thomas@smartcontract.com)", + "license": "MIT", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "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": "mocha --exit -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 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "test:eth": "mocha --exit -r ts-node/register 'test/eth.test.ts'", + "test:btc": "mocha --exit -r ts-node/register 'test/btc.test.ts'", + "test:aion": "mocha --exit -r ts-node/register 'test/aion.test.ts'", + "test:zilliqa": "RPC_URL=https://dev-api.zilliqa.com mocha --exit -r ts-node/register 'test/zilliqa.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/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/bitcoin-json-rpc-old/src/adapter.ts b/bitcoin-json-rpc-old/src/adapter.ts new file mode 100644 index 0000000000..cabb10c262 --- /dev/null +++ b/bitcoin-json-rpc-old/src/adapter.ts @@ -0,0 +1,61 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { Execute } from '@chainlink/types' + +const inputParams = { + url: false, + method: false, + params: false, + blockchain: false, + coin: false, + q: false, +} + +const convertQ: { [key: string]: string } = { + height: 'headers', +} + +// Export function to integrate with Chainlink node +export const execute: Execute = async (request) => { + const validator = new Validator(request, inputParams) + + if (validator.error) throw validator.error + + const url = process.env.RPC_URL || validator.validated.data.url || 'http://localhost:8545' + let method = validator.validated.data.method || '' + const params = validator.validated.data.params + const blockchain = validator.validated.data.blockchain || validator.validated.data.coin + + let q = validator.validated.data.q + + if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { + if (!q) q = 'difficulty' + method = 'getblockchaininfo' + } + + const data = { + id: request.id, + jsonrpc: '2.0', + method, + params, + } + + const options = { + url, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + // Remove undefined values + data: JSON.parse(JSON.stringify(data)), + } + + const response = await Requester.request(options) + if (response.statusCode >= 400) throw response.data.error + + if (q) { + if (q in convertQ) q = convertQ[q] + response.data.result = Requester.validateResultNumber(response.data, ['result', q]) + } + + return Requester.success(request.id, response) +} diff --git a/bitcoin-json-rpc-old/src/index.ts b/bitcoin-json-rpc-old/src/index.ts new file mode 100644 index 0000000000..ff34a1db3b --- /dev/null +++ b/bitcoin-json-rpc-old/src/index.ts @@ -0,0 +1,6 @@ +import { expose, util } from '@chainlink/ea-bootstrap' +import { execute } from './adapter' + +const NAME = 'JSON-RPC' + +export = { NAME, execute, ...expose(util.wrapExecute(execute)) } diff --git a/bitcoin-json-rpc-old/test/aion.test.ts b/bitcoin-json-rpc-old/test/aion.test.ts new file mode 100644 index 0000000000..fcb33b089e --- /dev/null +++ b/bitcoin-json-rpc-old/test/aion.test.ts @@ -0,0 +1,113 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to an AION client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('AION client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0xa00983f07c11ee9160a64dd3ba3dc3d1f88332a2869f25725f56cbd0be32ef7a', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_syncing', () => { + const req = { + id: jobID, + data: { + method: 'eth_syncing', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_gasPrice', () => { + const req = { + id: jobID, + data: { + method: 'eth_gasPrice', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_blockNumber', () => { + const req = { + id: jobID, + data: { + method: 'eth_blockNumber', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionByHash', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionByHash', + params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionReceipt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionReceipt', + params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/bitcoin-json-rpc-old/test/btc.test.ts b/bitcoin-json-rpc-old/test/btc.test.ts new file mode 100644 index 0000000000..90c2f0412c --- /dev/null +++ b/bitcoin-json-rpc-old/test/btc.test.ts @@ -0,0 +1,55 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to a Bitcoin client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Bitcoin client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('getblockchaininfo', () => { + const req = { + id: jobID, + data: { + method: 'getblockchaininfo', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('get height of btc blockchain', () => { + const req = { + id: jobID, + data: { + blockchain: 'btc', + q: 'height', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/bitcoin-json-rpc-old/test/eth.test.ts b/bitcoin-json-rpc-old/test/eth.test.ts new file mode 100644 index 0000000000..98e45fcacc --- /dev/null +++ b/bitcoin-json-rpc-old/test/eth.test.ts @@ -0,0 +1,244 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to an Ethereum (or equivalent) client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Ethereum client @integration', async function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_syncing', () => { + const req = { + id: jobID, + data: { + method: 'eth_syncing', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_gasPrice', () => { + const req = { + id: jobID, + data: { + method: 'eth_gasPrice', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_blockNumber', () => { + const req = { + id: jobID, + data: { + method: 'eth_blockNumber', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getStorageAt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getStorageAt', + params: ['0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', '0x0', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_call', () => { + const req = { + id: jobID, + data: { + method: 'eth_call', + params: [ + { + to: '0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', + data: '0x8da5cb5b', + }, + 'latest', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionByHash', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionByHash', + params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionReceipt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionReceipt', + params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + /** + * These functions only work with a connection to an unlocked client and have been + * disabled for the standard test suite. + */ + + context('eth_sign', () => { + const req = { + id: jobID, + data: { + method: 'eth_sign', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', '0x54686973206973206120746573742E'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getProof', () => { + const req = { + id: jobID, + data: { + method: 'eth_getProof', + params: [ + '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ], + 'latest', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_sendTransaction', () => { + const req = { + id: jobID, + data: { + method: 'eth_sendTransaction', + params: [ + { + from: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + to: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + data: '0x54686973206973206120746573742E', + }, + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_sendRawTransaction', () => { + const req = { + id: jobID, + data: { + method: 'eth_sendRawTransaction', + params: [ + '0xf87582065c85012a05f2008284d09487002564f1c7b8f51e96ca7d545e43402bf0b4ab808f54686973206973206120746573742e29a01ee35bf6feecdd2597d578c656bac327e4aace48270bbc2fcbf8ed63f1232dcca0113e7131a2e133fd014a33f2de985914ad0eb98a594e20a132b559ec94716da4', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/bitcoin-json-rpc-old/test/zilliqa.test.ts b/bitcoin-json-rpc-old/test/zilliqa.test.ts new file mode 100644 index 0000000000..3f35c10660 --- /dev/null +++ b/bitcoin-json-rpc-old/test/zilliqa.test.ts @@ -0,0 +1,86 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to a Zilliqa client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Zilliqa client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetNetworkId', () => { + const req = { + id: jobID, + data: { + method: 'GetNetworkId', + params: [''], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetBalance', () => { + const req = { + id: jobID, + data: { + method: 'GetBalance', + params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11936'], + }, + } + + it('Get balance should return some address balance', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetBalance', () => { + const req = { + id: jobID, + data: { + method: 'GetBalance', + params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11937'], + }, + } + + it('Get balance should return error as address is not created.', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetSmartContractState', () => { + const req = { + id: jobID, + data: { + method: 'GetSmartContractState', + params: ['5865337a32F48a04F5B52507442f47FC558d9C2b'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/bitcoin-json-rpc-old/tsconfig.json b/bitcoin-json-rpc-old/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/bitcoin-json-rpc-old/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"] +} diff --git a/json-rpc/README.md b/json-rpc/README.md index bab81c7d8b..8fa04e89f9 100644 --- a/json-rpc/README.md +++ b/json-rpc/README.md @@ -46,11 +46,6 @@ function getBalanceExternalChain(string _account) } ``` -## Blockchain stats - -- `blockchain` or `coin`: The blockchain to get stats from -- `q`: The parameter to query for. Default: "difficulty" - ## Install Install dependencies diff --git a/json-rpc/src/adapter.ts b/json-rpc/src/adapter.ts index cabb10c262..0acb205338 100644 --- a/json-rpc/src/adapter.ts +++ b/json-rpc/src/adapter.ts @@ -5,32 +5,16 @@ const inputParams = { url: false, method: false, params: false, - blockchain: false, - coin: false, - q: false, -} - -const convertQ: { [key: string]: string } = { - height: 'headers', } // Export function to integrate with Chainlink node export const execute: Execute = async (request) => { const validator = new Validator(request, inputParams) - if (validator.error) throw validator.error const url = process.env.RPC_URL || validator.validated.data.url || 'http://localhost:8545' - let method = validator.validated.data.method || '' + const method = validator.validated.data.method || '' const params = validator.validated.data.params - const blockchain = validator.validated.data.blockchain || validator.validated.data.coin - - let q = validator.validated.data.q - - if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { - if (!q) q = 'difficulty' - method = 'getblockchaininfo' - } const data = { id: request.id, @@ -52,10 +36,5 @@ export const execute: Execute = async (request) => { const response = await Requester.request(options) if (response.statusCode >= 400) throw response.data.error - if (q) { - if (q in convertQ) q = convertQ[q] - response.data.result = Requester.validateResultNumber(response.data, ['result', q]) - } - return Requester.success(request.id, response) } diff --git a/json-rpc/test/btc.test.ts b/json-rpc/test/btc.test.ts index 90c2f0412c..6347fb7085 100644 --- a/json-rpc/test/btc.test.ts +++ b/json-rpc/test/btc.test.ts @@ -24,26 +24,11 @@ describe('Bitcoin client @integration', function () { }) }) - context('getblockchaininfo', () => { + context('getinfo', () => { const req = { id: jobID, data: { - method: 'getblockchaininfo', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('get height of btc blockchain', () => { - const req = { - id: jobID, - data: { - blockchain: 'btc', - q: 'height', + method: 'getinfo', }, } From b48da1b4707c8d272a84ddaaf00fd613e966240d Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Mon, 11 Jan 2021 10:30:01 +0200 Subject: [PATCH 09/23] created composite adapter, no functionality added --- composite/bitcoin-json-rpc/.eslintrc.js | 3 + composite/bitcoin-json-rpc/README.md | 92 +++++++ composite/bitcoin-json-rpc/package-lock.json | 11 + composite/bitcoin-json-rpc/package.json | 41 +++ composite/bitcoin-json-rpc/src/adapter.ts | 14 + composite/bitcoin-json-rpc/src/index.ts | 6 + composite/bitcoin-json-rpc/test/aion.test.ts | 113 ++++++++ composite/bitcoin-json-rpc/test/btc.test.ts | 40 +++ composite/bitcoin-json-rpc/test/eth.test.ts | 244 ++++++++++++++++++ .../bitcoin-json-rpc/test/zilliqa.test.ts | 86 ++++++ composite/bitcoin-json-rpc/tsconfig.json | 10 + json-rpc/src/adapter.ts | 3 + yarn.lock | 10 + 13 files changed, 673 insertions(+) create mode 100644 composite/bitcoin-json-rpc/.eslintrc.js create mode 100644 composite/bitcoin-json-rpc/README.md create mode 100644 composite/bitcoin-json-rpc/package-lock.json create mode 100644 composite/bitcoin-json-rpc/package.json create mode 100644 composite/bitcoin-json-rpc/src/adapter.ts create mode 100644 composite/bitcoin-json-rpc/src/index.ts create mode 100644 composite/bitcoin-json-rpc/test/aion.test.ts create mode 100644 composite/bitcoin-json-rpc/test/btc.test.ts create mode 100644 composite/bitcoin-json-rpc/test/eth.test.ts create mode 100644 composite/bitcoin-json-rpc/test/zilliqa.test.ts create mode 100644 composite/bitcoin-json-rpc/tsconfig.json diff --git a/composite/bitcoin-json-rpc/.eslintrc.js b/composite/bitcoin-json-rpc/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/composite/bitcoin-json-rpc/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/composite/bitcoin-json-rpc/README.md b/composite/bitcoin-json-rpc/README.md new file mode 100644 index 0000000000..8fa04e89f9 --- /dev/null +++ b/composite/bitcoin-json-rpc/README.md @@ -0,0 +1,92 @@ +# General JSON-RPC External Adapter for Chainlink + +- Should work for any JSON RPC supported endpoint (includes tests for a few major projects which support JSON RPC commands) +- Supports AWS Lambda and GCP Functions +- Blockchain clients can sign and send transactions if wallet is unlocked +- Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) + +A JSON-RPC request is typically formatted like this: + +```JSON +{ + "jsonrpc": "2.0", + "method": "some_method", + "params": [ + "some_param", + "another_param" + ], + "id": 1 +} +``` + +What this adapter does is allow you to specify the `"method"` and `"params"` values in a Chainlink request and receive the result (format example below) back to the Chainlink node for further processing. + +```JSON +{ + "id":1, + "jsonrpc": "2.0", + "result": "some_result" +} +``` + +In Solidity, a Chainlink request can be made to an external chain for the balance of a given account with the following example: + +```javascript +function getBalanceExternalChain(string _account) + public + onlyOwner +{ + Chainlink.Request memory req = newRequest(JOB_ID, this, this.fulfillRPCCall.selector); + req.add("method", "eth_getBalance"); + string[] memory params = new string[](2); + path[0] = _account; + path[1] = "latest"; + req.addStringArray("params", params); + chainlinkRequest(req, ORACLE_PAYMENT); +} +``` + +## Install + +Install dependencies + +```bash +npm install +``` + +Set the `RPC_URL` environment variable to your client URL. + +## Testing + +Testing is dependent on the type of node you're connecting to. You can set a local environment variable `RPC_URL` to point to an RPC connection. Otherwise, the adapter will default to `"http://localhost:8545"`. + +RPC Address and Port Defaults: + +- Ethereum: http://localhost:8545 +- AION: http://localhost:8545 +- BTC: (bitcoind) http://localhost:8332 (btcd) http://localhost:8334 +- Zilliqa: http://localhost:4201 + +For Ethereum and any Geth clone (should work with Parity as well): + +```bash +npm run test:eth +``` + +For Bitcoin: + +```bash +npm run test:btc +``` + +For AION: + +```bash +npm run test:aion +``` + +For Zilliqa: + +```bash +npm run test:zilliqa +``` diff --git a/composite/bitcoin-json-rpc/package-lock.json b/composite/bitcoin-json-rpc/package-lock.json new file mode 100644 index 0000000000..fefedfba53 --- /dev/null +++ b/composite/bitcoin-json-rpc/package-lock.json @@ -0,0 +1,11 @@ +{ + "name": "@chainlink/bitcoin-json-rpc-adapter", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@chainlink/json-rpc-adapter": { + "version": "file:../../json-rpc" + } + } +} diff --git a/composite/bitcoin-json-rpc/package.json b/composite/bitcoin-json-rpc/package.json new file mode 100644 index 0000000000..4cec868101 --- /dev/null +++ b/composite/bitcoin-json-rpc/package.json @@ -0,0 +1,41 @@ +{ + "name": "@chainlink/bitcoin-json-rpc-adapter", + "version": "0.0.1", + "description": "", + "author": "Evangelos Barakos (evangelos@smartcontract.com)", + "license": "MIT", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "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": "mocha --exit -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 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "test:eth": "mocha --exit -r ts-node/register 'test/eth.test.ts'", + "test:btc": "mocha --exit -r ts-node/register 'test/btc.test.ts'", + "test:aion": "mocha --exit -r ts-node/register 'test/aion.test.ts'", + "test:zilliqa": "RPC_URL=https://dev-api.zilliqa.com mocha --exit -r ts-node/register 'test/zilliqa.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/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": { + "@chainlink/json-rpc-adapter": "file:../../json-rpc" + } +} diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts new file mode 100644 index 0000000000..03f2517c4b --- /dev/null +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -0,0 +1,14 @@ +// import { Requester, Validator } from '@chainlink/external-adapter' +import JSONRPC from '@chainlink/json-rpc-adapter' +import { Execute } from '@chainlink/types' + +// const inputParams = { +// url: false, +// method: false, +// params: false, +// } + +// Export function to integrate with Chainlink node +export const execute: Execute = async (request: any) => { + return JSONRPC.execute(request) +} diff --git a/composite/bitcoin-json-rpc/src/index.ts b/composite/bitcoin-json-rpc/src/index.ts new file mode 100644 index 0000000000..ff34a1db3b --- /dev/null +++ b/composite/bitcoin-json-rpc/src/index.ts @@ -0,0 +1,6 @@ +import { expose, util } from '@chainlink/ea-bootstrap' +import { execute } from './adapter' + +const NAME = 'JSON-RPC' + +export = { NAME, execute, ...expose(util.wrapExecute(execute)) } diff --git a/composite/bitcoin-json-rpc/test/aion.test.ts b/composite/bitcoin-json-rpc/test/aion.test.ts new file mode 100644 index 0000000000..fcb33b089e --- /dev/null +++ b/composite/bitcoin-json-rpc/test/aion.test.ts @@ -0,0 +1,113 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to an AION client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('AION client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0xa00983f07c11ee9160a64dd3ba3dc3d1f88332a2869f25725f56cbd0be32ef7a', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_syncing', () => { + const req = { + id: jobID, + data: { + method: 'eth_syncing', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_gasPrice', () => { + const req = { + id: jobID, + data: { + method: 'eth_gasPrice', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_blockNumber', () => { + const req = { + id: jobID, + data: { + method: 'eth_blockNumber', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionByHash', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionByHash', + params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionReceipt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionReceipt', + params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/composite/bitcoin-json-rpc/test/btc.test.ts b/composite/bitcoin-json-rpc/test/btc.test.ts new file mode 100644 index 0000000000..6347fb7085 --- /dev/null +++ b/composite/bitcoin-json-rpc/test/btc.test.ts @@ -0,0 +1,40 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to a Bitcoin client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Bitcoin client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('getinfo', () => { + const req = { + id: jobID, + data: { + method: 'getinfo', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/composite/bitcoin-json-rpc/test/eth.test.ts b/composite/bitcoin-json-rpc/test/eth.test.ts new file mode 100644 index 0000000000..98e45fcacc --- /dev/null +++ b/composite/bitcoin-json-rpc/test/eth.test.ts @@ -0,0 +1,244 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to an Ethereum (or equivalent) client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Ethereum client @integration', async function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_syncing', () => { + const req = { + id: jobID, + data: { + method: 'eth_syncing', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_gasPrice', () => { + const req = { + id: jobID, + data: { + method: 'eth_gasPrice', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_blockNumber', () => { + const req = { + id: jobID, + data: { + method: 'eth_blockNumber', + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getBalance', () => { + const req = { + id: jobID, + data: { + method: 'eth_getBalance', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getStorageAt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getStorageAt', + params: ['0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', '0x0', 'latest'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_call', () => { + const req = { + id: jobID, + data: { + method: 'eth_call', + params: [ + { + to: '0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', + data: '0x8da5cb5b', + }, + 'latest', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionByHash', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionByHash', + params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getTransactionReceipt', () => { + const req = { + id: jobID, + data: { + method: 'eth_getTransactionReceipt', + params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + /** + * These functions only work with a connection to an unlocked client and have been + * disabled for the standard test suite. + */ + + context('eth_sign', () => { + const req = { + id: jobID, + data: { + method: 'eth_sign', + params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', '0x54686973206973206120746573742E'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_getProof', () => { + const req = { + id: jobID, + data: { + method: 'eth_getProof', + params: [ + '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + [ + '0x0000000000000000000000000000000000000000000000000000000000000000', + '0x0000000000000000000000000000000000000000000000000000000000000001', + ], + 'latest', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_sendTransaction', () => { + const req = { + id: jobID, + data: { + method: 'eth_sendTransaction', + params: [ + { + from: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + to: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', + data: '0x54686973206973206120746573742E', + }, + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('eth_sendRawTransaction', () => { + const req = { + id: jobID, + data: { + method: 'eth_sendRawTransaction', + params: [ + '0xf87582065c85012a05f2008284d09487002564f1c7b8f51e96ca7d545e43402bf0b4ab808f54686973206973206120746573742e29a01ee35bf6feecdd2597d578c656bac327e4aace48270bbc2fcbf8ed63f1232dcca0113e7131a2e133fd014a33f2de985914ad0eb98a594e20a132b559ec94716da4', + ], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/composite/bitcoin-json-rpc/test/zilliqa.test.ts b/composite/bitcoin-json-rpc/test/zilliqa.test.ts new file mode 100644 index 0000000000..3f35c10660 --- /dev/null +++ b/composite/bitcoin-json-rpc/test/zilliqa.test.ts @@ -0,0 +1,86 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' + +/** + * Running these tests requires a connection to a Zilliqa client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('Zilliqa client @integration', function () { + this.timeout(5000) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + + context('Unrecognized method', () => { + const req = { + id: jobID, + data: { + method: 'no_op', + }, + } + + it('returns error to the node', async () => { + const resp = await execute(req) + assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetNetworkId', () => { + const req = { + id: jobID, + data: { + method: 'GetNetworkId', + params: [''], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetBalance', () => { + const req = { + id: jobID, + data: { + method: 'GetBalance', + params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11936'], + }, + } + + it('Get balance should return some address balance', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetBalance', () => { + const req = { + id: jobID, + data: { + method: 'GetBalance', + params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11937'], + }, + } + + it('Get balance should return error as address is not created.', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) + + context('GetSmartContractState', () => { + const req = { + id: jobID, + data: { + method: 'GetSmartContractState', + params: ['5865337a32F48a04F5B52507442f47FC558d9C2b'], + }, + } + + it('returns data to the node', async () => { + const resp = await execute(req) + assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) + }) + }) +}) diff --git a/composite/bitcoin-json-rpc/tsconfig.json b/composite/bitcoin-json-rpc/tsconfig.json new file mode 100644 index 0000000000..f1f9a32efb --- /dev/null +++ b/composite/bitcoin-json-rpc/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"] +} diff --git a/json-rpc/src/adapter.ts b/json-rpc/src/adapter.ts index 0acb205338..515038973a 100644 --- a/json-rpc/src/adapter.ts +++ b/json-rpc/src/adapter.ts @@ -38,3 +38,6 @@ export const execute: Execute = async (request) => { return Requester.success(request.id, response) } + +// export const makeExecute: ExecuteFactory = (config?: Config) => (input) => +// execute(input, config || makeConfig()) diff --git a/yarn.lock b/yarn.lock index 01503faa59..fe06f8a2b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,6 +68,16 @@ "@truffle/contract" "^4.2.6" ethers "^4.0.45" +"@chainlink/json-rpc-adapter@file:json-rpc": + version "0.0.1" + +"@chainlink/reduce@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@chainlink/reduce/-/reduce-0.0.3.tgz#6d26f5a0d299fce3497c0539e3aede2aff71f7d9" + integrity sha512-uym+pqQBbp3lGNSTpJGGSp32UymgTSDZrqPjoft8/nm0yDeF3x7FpRfvc6y79ap0PiX6wpv8kGqOL9KR/tLI9A== + dependencies: + object-path "^0.11.4" + "@chainlink/reference-data-reader@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@chainlink/reference-data-reader/-/reference-data-reader-0.0.1.tgz#5dd0751d851fcc38ee1d08742ef6851661bcfe3e" From 44625e5d68faa19c973645a58eccca205fd9995e Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Mon, 11 Jan 2021 11:05:24 +0200 Subject: [PATCH 10/23] Working version of composite bitcoin adapter --- composite/bitcoin-json-rpc/src/adapter.ts | 44 +++++++++++++++++++---- json-rpc/src/adapter.ts | 3 -- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index 03f2517c4b..49b45d04b8 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -1,14 +1,46 @@ // import { Requester, Validator } from '@chainlink/external-adapter' import JSONRPC from '@chainlink/json-rpc-adapter' import { Execute } from '@chainlink/types' +import { Validator } from '@chainlink/external-adapter' +import { Requester } from '@chainlink/external-adapter' -// const inputParams = { -// url: false, -// method: false, -// params: false, -// } + +const inputParams = { + url: false, + method: false, + params: false, + blockchain: false, + coin: false, + endpoint: false +} + +const convertEndpoint: { [key: string]: string } = { + height: 'headers', +} // Export function to integrate with Chainlink node +// TODO: check the request type export const execute: Execute = async (request: any) => { - return JSONRPC.execute(request) + const validator = new Validator(request, inputParams) + const blockchain = validator.validated.data.blockchain || validator.validated.data.coin + + let endpoint = validator.validated.data.endpoint + + if (validator.error) throw validator.error + if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + if (!endpoint) endpoint = 'difficulty' + request.data.method = 'getblockchaininfo' + } + + const response = await JSONRPC.execute(request) + + console.log(response.data.result) + if (endpoint) { + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + response.result = Requester.validateResultNumber(response.data, ['result', endpoint]) + } + response.data = response.data.result + return response } + diff --git a/json-rpc/src/adapter.ts b/json-rpc/src/adapter.ts index 515038973a..0acb205338 100644 --- a/json-rpc/src/adapter.ts +++ b/json-rpc/src/adapter.ts @@ -38,6 +38,3 @@ export const execute: Execute = async (request) => { return Requester.success(request.id, response) } - -// export const makeExecute: ExecuteFactory = (config?: Config) => (input) => -// execute(input, config || makeConfig()) From c96cc1d420c18151249261c7d88f8e54a2c1aa5c Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Tue, 12 Jan 2021 20:49:38 +0200 Subject: [PATCH 11/23] Removed old adapter, fixed dependencies and issues --- .github/strategy/adapters.json | 3 +- CHANGELOG.md | 1 + bitcoin-json-rpc-old/.eslintrc.js | 3 - bitcoin-json-rpc-old/README.md | 97 ------- bitcoin-json-rpc-old/package.json | 39 --- bitcoin-json-rpc-old/src/adapter.ts | 61 ----- bitcoin-json-rpc-old/src/index.ts | 6 - bitcoin-json-rpc-old/test/aion.test.ts | 113 -------- bitcoin-json-rpc-old/test/btc.test.ts | 55 ---- bitcoin-json-rpc-old/test/eth.test.ts | 244 ------------------ bitcoin-json-rpc-old/test/zilliqa.test.ts | 86 ------ bitcoin-json-rpc-old/tsconfig.json | 10 - composite/bitcoin-json-rpc/.eslintrc.js | 2 +- composite/bitcoin-json-rpc/README.md | 115 +++++---- composite/bitcoin-json-rpc/package-lock.json | 11 - composite/bitcoin-json-rpc/package.json | 13 +- composite/bitcoin-json-rpc/src/adapter.ts | 22 +- composite/bitcoin-json-rpc/src/index.ts | 2 +- .../bitcoin-json-rpc/test/adapter.test.ts | 82 ++++++ composite/bitcoin-json-rpc/test/aion.test.ts | 113 -------- composite/bitcoin-json-rpc/test/btc.test.ts | 40 --- composite/bitcoin-json-rpc/test/eth.test.ts | 244 ------------------ .../bitcoin-json-rpc/test/zilliqa.test.ts | 86 ------ composite/bitcoin-json-rpc/tsconfig.json | 18 +- cryptoid/README.md | 2 +- yarn.lock | 3 - 26 files changed, 186 insertions(+), 1285 deletions(-) delete mode 100644 bitcoin-json-rpc-old/.eslintrc.js delete mode 100644 bitcoin-json-rpc-old/README.md delete mode 100644 bitcoin-json-rpc-old/package.json delete mode 100644 bitcoin-json-rpc-old/src/adapter.ts delete mode 100644 bitcoin-json-rpc-old/src/index.ts delete mode 100644 bitcoin-json-rpc-old/test/aion.test.ts delete mode 100644 bitcoin-json-rpc-old/test/btc.test.ts delete mode 100644 bitcoin-json-rpc-old/test/eth.test.ts delete mode 100644 bitcoin-json-rpc-old/test/zilliqa.test.ts delete mode 100644 bitcoin-json-rpc-old/tsconfig.json delete mode 100644 composite/bitcoin-json-rpc/package-lock.json create mode 100644 composite/bitcoin-json-rpc/test/adapter.test.ts delete mode 100644 composite/bitcoin-json-rpc/test/aion.test.ts delete mode 100644 composite/bitcoin-json-rpc/test/btc.test.ts delete mode 100644 composite/bitcoin-json-rpc/test/eth.test.ts delete mode 100644 composite/bitcoin-json-rpc/test/zilliqa.test.ts diff --git a/.github/strategy/adapters.json b/.github/strategy/adapters.json index f565d2e0f9..8236b10514 100644 --- a/.github/strategy/adapters.json +++ b/.github/strategy/adapters.json @@ -106,7 +106,8 @@ "defi-pulse", "dns-record-check", "outlier-detection", - "crypto-volatility-index" + "crypto-volatility-index", + "bitcoin-json-rpc" ] }, "synth-index": { diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d7ebfcc8..c155586e4d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `paxos` to get Paxos asset supply attestations - `outlier-detection`: composite adapter to check for outlier values between multiple sets of data providers - `dydx-stark` to sign the input price data with your private STARK key, and send it to the destination endpoint. + - `bitcoin-json-rpc`: composite adapter for querying bitcoin blockchain stats(difficulty, height) according to the existing convention - Added support for metadata in requests. This gives adapters access to the FM on-chain round state. - Moves re-usable test behaviors & testing utils to a new package - `@chainlink/adapter-test-helpers` - Added support for using query string parameters as input to adapters. diff --git a/bitcoin-json-rpc-old/.eslintrc.js b/bitcoin-json-rpc-old/.eslintrc.js deleted file mode 100644 index 11f16f9a15..0000000000 --- a/bitcoin-json-rpc-old/.eslintrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - ...require('../.eslintrc.ts.js'), -} diff --git a/bitcoin-json-rpc-old/README.md b/bitcoin-json-rpc-old/README.md deleted file mode 100644 index bab81c7d8b..0000000000 --- a/bitcoin-json-rpc-old/README.md +++ /dev/null @@ -1,97 +0,0 @@ -# General JSON-RPC External Adapter for Chainlink - -- Should work for any JSON RPC supported endpoint (includes tests for a few major projects which support JSON RPC commands) -- Supports AWS Lambda and GCP Functions -- Blockchain clients can sign and send transactions if wallet is unlocked -- Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) - -A JSON-RPC request is typically formatted like this: - -```JSON -{ - "jsonrpc": "2.0", - "method": "some_method", - "params": [ - "some_param", - "another_param" - ], - "id": 1 -} -``` - -What this adapter does is allow you to specify the `"method"` and `"params"` values in a Chainlink request and receive the result (format example below) back to the Chainlink node for further processing. - -```JSON -{ - "id":1, - "jsonrpc": "2.0", - "result": "some_result" -} -``` - -In Solidity, a Chainlink request can be made to an external chain for the balance of a given account with the following example: - -```javascript -function getBalanceExternalChain(string _account) - public - onlyOwner -{ - Chainlink.Request memory req = newRequest(JOB_ID, this, this.fulfillRPCCall.selector); - req.add("method", "eth_getBalance"); - string[] memory params = new string[](2); - path[0] = _account; - path[1] = "latest"; - req.addStringArray("params", params); - chainlinkRequest(req, ORACLE_PAYMENT); -} -``` - -## Blockchain stats - -- `blockchain` or `coin`: The blockchain to get stats from -- `q`: The parameter to query for. Default: "difficulty" - -## Install - -Install dependencies - -```bash -npm install -``` - -Set the `RPC_URL` environment variable to your client URL. - -## Testing - -Testing is dependent on the type of node you're connecting to. You can set a local environment variable `RPC_URL` to point to an RPC connection. Otherwise, the adapter will default to `"http://localhost:8545"`. - -RPC Address and Port Defaults: - -- Ethereum: http://localhost:8545 -- AION: http://localhost:8545 -- BTC: (bitcoind) http://localhost:8332 (btcd) http://localhost:8334 -- Zilliqa: http://localhost:4201 - -For Ethereum and any Geth clone (should work with Parity as well): - -```bash -npm run test:eth -``` - -For Bitcoin: - -```bash -npm run test:btc -``` - -For AION: - -```bash -npm run test:aion -``` - -For Zilliqa: - -```bash -npm run test:zilliqa -``` diff --git a/bitcoin-json-rpc-old/package.json b/bitcoin-json-rpc-old/package.json deleted file mode 100644 index bd3b459818..0000000000 --- a/bitcoin-json-rpc-old/package.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "name": "@chainlink/json-rpc-adapter", - "version": "0.0.1", - "description": "", - "author": "Thomas Hodges (thomas@smartcontract.com)", - "license": "MIT", - "main": "dist/index.js", - "types": "dist/index.d.ts", - "files": [ - "dist" - ], - "scripts": { - "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": "mocha --exit -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 --grep @integration -r ts-node/register 'test/**/*.test.ts'", - "test:eth": "mocha --exit -r ts-node/register 'test/eth.test.ts'", - "test:btc": "mocha --exit -r ts-node/register 'test/btc.test.ts'", - "test:aion": "mocha --exit -r ts-node/register 'test/aion.test.ts'", - "test:zilliqa": "RPC_URL=https://dev-api.zilliqa.com mocha --exit -r ts-node/register 'test/zilliqa.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/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/bitcoin-json-rpc-old/src/adapter.ts b/bitcoin-json-rpc-old/src/adapter.ts deleted file mode 100644 index cabb10c262..0000000000 --- a/bitcoin-json-rpc-old/src/adapter.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { Requester, Validator } from '@chainlink/external-adapter' -import { Execute } from '@chainlink/types' - -const inputParams = { - url: false, - method: false, - params: false, - blockchain: false, - coin: false, - q: false, -} - -const convertQ: { [key: string]: string } = { - height: 'headers', -} - -// Export function to integrate with Chainlink node -export const execute: Execute = async (request) => { - const validator = new Validator(request, inputParams) - - if (validator.error) throw validator.error - - const url = process.env.RPC_URL || validator.validated.data.url || 'http://localhost:8545' - let method = validator.validated.data.method || '' - const params = validator.validated.data.params - const blockchain = validator.validated.data.blockchain || validator.validated.data.coin - - let q = validator.validated.data.q - - if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { - if (!q) q = 'difficulty' - method = 'getblockchaininfo' - } - - const data = { - id: request.id, - jsonrpc: '2.0', - method, - params, - } - - const options = { - url, - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - // Remove undefined values - data: JSON.parse(JSON.stringify(data)), - } - - const response = await Requester.request(options) - if (response.statusCode >= 400) throw response.data.error - - if (q) { - if (q in convertQ) q = convertQ[q] - response.data.result = Requester.validateResultNumber(response.data, ['result', q]) - } - - return Requester.success(request.id, response) -} diff --git a/bitcoin-json-rpc-old/src/index.ts b/bitcoin-json-rpc-old/src/index.ts deleted file mode 100644 index ff34a1db3b..0000000000 --- a/bitcoin-json-rpc-old/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { expose, util } from '@chainlink/ea-bootstrap' -import { execute } from './adapter' - -const NAME = 'JSON-RPC' - -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } diff --git a/bitcoin-json-rpc-old/test/aion.test.ts b/bitcoin-json-rpc-old/test/aion.test.ts deleted file mode 100644 index fcb33b089e..0000000000 --- a/bitcoin-json-rpc-old/test/aion.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to an AION client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('AION client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0xa00983f07c11ee9160a64dd3ba3dc3d1f88332a2869f25725f56cbd0be32ef7a', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_syncing', () => { - const req = { - id: jobID, - data: { - method: 'eth_syncing', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_gasPrice', () => { - const req = { - id: jobID, - data: { - method: 'eth_gasPrice', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_blockNumber', () => { - const req = { - id: jobID, - data: { - method: 'eth_blockNumber', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionByHash', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionByHash', - params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionReceipt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionReceipt', - params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/bitcoin-json-rpc-old/test/btc.test.ts b/bitcoin-json-rpc-old/test/btc.test.ts deleted file mode 100644 index 90c2f0412c..0000000000 --- a/bitcoin-json-rpc-old/test/btc.test.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to a Bitcoin client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Bitcoin client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('getblockchaininfo', () => { - const req = { - id: jobID, - data: { - method: 'getblockchaininfo', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('get height of btc blockchain', () => { - const req = { - id: jobID, - data: { - blockchain: 'btc', - q: 'height', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/bitcoin-json-rpc-old/test/eth.test.ts b/bitcoin-json-rpc-old/test/eth.test.ts deleted file mode 100644 index 98e45fcacc..0000000000 --- a/bitcoin-json-rpc-old/test/eth.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to an Ethereum (or equivalent) client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Ethereum client @integration', async function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_syncing', () => { - const req = { - id: jobID, - data: { - method: 'eth_syncing', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_gasPrice', () => { - const req = { - id: jobID, - data: { - method: 'eth_gasPrice', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_blockNumber', () => { - const req = { - id: jobID, - data: { - method: 'eth_blockNumber', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getStorageAt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getStorageAt', - params: ['0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', '0x0', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_call', () => { - const req = { - id: jobID, - data: { - method: 'eth_call', - params: [ - { - to: '0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', - data: '0x8da5cb5b', - }, - 'latest', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionByHash', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionByHash', - params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionReceipt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionReceipt', - params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - /** - * These functions only work with a connection to an unlocked client and have been - * disabled for the standard test suite. - */ - - context('eth_sign', () => { - const req = { - id: jobID, - data: { - method: 'eth_sign', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', '0x54686973206973206120746573742E'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getProof', () => { - const req = { - id: jobID, - data: { - method: 'eth_getProof', - params: [ - '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000001', - ], - 'latest', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_sendTransaction', () => { - const req = { - id: jobID, - data: { - method: 'eth_sendTransaction', - params: [ - { - from: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - to: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - data: '0x54686973206973206120746573742E', - }, - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_sendRawTransaction', () => { - const req = { - id: jobID, - data: { - method: 'eth_sendRawTransaction', - params: [ - '0xf87582065c85012a05f2008284d09487002564f1c7b8f51e96ca7d545e43402bf0b4ab808f54686973206973206120746573742e29a01ee35bf6feecdd2597d578c656bac327e4aace48270bbc2fcbf8ed63f1232dcca0113e7131a2e133fd014a33f2de985914ad0eb98a594e20a132b559ec94716da4', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/bitcoin-json-rpc-old/test/zilliqa.test.ts b/bitcoin-json-rpc-old/test/zilliqa.test.ts deleted file mode 100644 index 3f35c10660..0000000000 --- a/bitcoin-json-rpc-old/test/zilliqa.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to a Zilliqa client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Zilliqa client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetNetworkId', () => { - const req = { - id: jobID, - data: { - method: 'GetNetworkId', - params: [''], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetBalance', () => { - const req = { - id: jobID, - data: { - method: 'GetBalance', - params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11936'], - }, - } - - it('Get balance should return some address balance', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetBalance', () => { - const req = { - id: jobID, - data: { - method: 'GetBalance', - params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11937'], - }, - } - - it('Get balance should return error as address is not created.', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetSmartContractState', () => { - const req = { - id: jobID, - data: { - method: 'GetSmartContractState', - params: ['5865337a32F48a04F5B52507442f47FC558d9C2b'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/bitcoin-json-rpc-old/tsconfig.json b/bitcoin-json-rpc-old/tsconfig.json deleted file mode 100644 index 3b4ccf41fa..0000000000 --- a/bitcoin-json-rpc-old/tsconfig.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "dist", - "rootDir": "src", - "typeRoots": ["../node_modules/@types", "../typings", "./typings"] - }, - "include": ["src/**/*"], - "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] -} diff --git a/composite/bitcoin-json-rpc/.eslintrc.js b/composite/bitcoin-json-rpc/.eslintrc.js index 11f16f9a15..30c00e34a1 100644 --- a/composite/bitcoin-json-rpc/.eslintrc.js +++ b/composite/bitcoin-json-rpc/.eslintrc.js @@ -1,3 +1,3 @@ module.exports = { - ...require('../.eslintrc.ts.js'), + ...require('../../.eslintrc.ts.js'), } diff --git a/composite/bitcoin-json-rpc/README.md b/composite/bitcoin-json-rpc/README.md index 8fa04e89f9..545399a601 100644 --- a/composite/bitcoin-json-rpc/README.md +++ b/composite/bitcoin-json-rpc/README.md @@ -1,11 +1,74 @@ -# General JSON-RPC External Adapter for Chainlink +# Bitcoin JSON-RPC External Adapter for Chainlink -- Should work for any JSON RPC supported endpoint (includes tests for a few major projects which support JSON RPC commands) +- Should work for any Bitcoin JSON RPC supported endpoint - Supports AWS Lambda and GCP Functions -- Blockchain clients can sign and send transactions if wallet is unlocked +- Bitcoin clients can sign and send transactions if wallet is unlocked - Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) -A JSON-RPC request is typically formatted like this: +## Input Params + +Returns blockchain info stats, by calling `"method": "getblockchainfo"` + +- `endpoint`: The parameter to query for. Default: "difficulty" + +## Output + +```json +{ + "jobRunID": "1", + "data": { + "chain": "main", + "blocks": 412022, + "headers": 665582, + "bestblockhash": "0000000000000000056482e60e14364c82903764eb88aef8fb0b1b60647334be", + "difficulty": 194254820283.444, + "mediantime": 1463406562, + "verificationprogress": 0.2162006436056612, + "initialblockdownload": true, + "chainwork": "0000000000000000000000000000000000000000001973393fcfc0215ecc9726", + "size_on_disk": 4758448869, + "pruned": true, + "pruneheight": 406538, + "automatic_pruning": true, + "prune_target_size": 5242880000, + "softforks": { + "bip34": { + "type": "buried", + "active": true, + "height": 227931 + }, + "bip66": { + "type": "buried", + "active": true, + "height": 363725 + }, + "bip65": { + "type": "buried", + "active": true, + "height": 388381 + }, + "csv": { + "type": "buried", + "active": false, + "height": 419328 + }, + "segwit": { + "type": "buried", + "active": false, + "height": 481824 + } + }, + "warnings": "", + "result": 665582 + }, + "result": 665582, + "statusCode": 200 +} +``` + +Also allows for general JSON-RPC requests, formatted like this: + +# Input ```JSON { @@ -19,7 +82,7 @@ A JSON-RPC request is typically formatted like this: } ``` -What this adapter does is allow you to specify the `"method"` and `"params"` values in a Chainlink request and receive the result (format example below) back to the Chainlink node for further processing. +# Output ```JSON { @@ -29,23 +92,6 @@ What this adapter does is allow you to specify the `"method"` and `"params"` val } ``` -In Solidity, a Chainlink request can be made to an external chain for the balance of a given account with the following example: - -```javascript -function getBalanceExternalChain(string _account) - public - onlyOwner -{ - Chainlink.Request memory req = newRequest(JOB_ID, this, this.fulfillRPCCall.selector); - req.add("method", "eth_getBalance"); - string[] memory params = new string[](2); - path[0] = _account; - path[1] = "latest"; - req.addStringArray("params", params); - chainlinkRequest(req, ORACLE_PAYMENT); -} -``` - ## Install Install dependencies @@ -62,31 +108,8 @@ Testing is dependent on the type of node you're connecting to. You can set a loc RPC Address and Port Defaults: -- Ethereum: http://localhost:8545 -- AION: http://localhost:8545 - BTC: (bitcoind) http://localhost:8332 (btcd) http://localhost:8334 -- Zilliqa: http://localhost:4201 - -For Ethereum and any Geth clone (should work with Parity as well): - -```bash -npm run test:eth -``` - -For Bitcoin: - -```bash -npm run test:btc -``` - -For AION: - -```bash -npm run test:aion -``` - -For Zilliqa: ```bash -npm run test:zilliqa +npm run test ``` diff --git a/composite/bitcoin-json-rpc/package-lock.json b/composite/bitcoin-json-rpc/package-lock.json deleted file mode 100644 index fefedfba53..0000000000 --- a/composite/bitcoin-json-rpc/package-lock.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@chainlink/bitcoin-json-rpc-adapter", - "version": "0.0.1", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@chainlink/json-rpc-adapter": { - "version": "file:../../json-rpc" - } - } -} diff --git a/composite/bitcoin-json-rpc/package.json b/composite/bitcoin-json-rpc/package.json index 4cec868101..413e3ec141 100644 --- a/composite/bitcoin-json-rpc/package.json +++ b/composite/bitcoin-json-rpc/package.json @@ -13,16 +13,11 @@ "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", + "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", + "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", "test": "mocha --exit -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 --grep @integration -r ts-node/register 'test/**/*.test.ts'", - "test:eth": "mocha --exit -r ts-node/register 'test/eth.test.ts'", - "test:btc": "mocha --exit -r ts-node/register 'test/btc.test.ts'", - "test:aion": "mocha --exit -r ts-node/register 'test/aion.test.ts'", - "test:zilliqa": "RPC_URL=https://dev-api.zilliqa.com mocha --exit -r ts-node/register 'test/zilliqa.test.ts'", - "server": "node -e 'require(\"./index.js\").server()'", + "test:integration": "mocha --exit --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" }, @@ -36,6 +31,6 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/json-rpc-adapter": "file:../../json-rpc" + "@chainlink/json-rpc-adapter": "^0.0.1" } } diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index 49b45d04b8..d38df8e1cc 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -1,17 +1,15 @@ -// import { Requester, Validator } from '@chainlink/external-adapter' import JSONRPC from '@chainlink/json-rpc-adapter' -import { Execute } from '@chainlink/types' +import { Execute, AdapterRequest } from '@chainlink/types' import { Validator } from '@chainlink/external-adapter' import { Requester } from '@chainlink/external-adapter' - const inputParams = { url: false, method: false, params: false, blockchain: false, coin: false, - endpoint: false + endpoint: false, } const convertEndpoint: { [key: string]: string } = { @@ -19,28 +17,28 @@ const convertEndpoint: { [key: string]: string } = { } // Export function to integrate with Chainlink node -// TODO: check the request type -export const execute: Execute = async (request: any) => { +export const execute: Execute = async (request: AdapterRequest) => { const validator = new Validator(request, inputParams) const blockchain = validator.validated.data.blockchain || validator.validated.data.coin - let endpoint = validator.validated.data.endpoint + let endpoint = validator.validated.data.endpoint if (validator.error) throw validator.error - if (blockchain != undefined && blockchain.toLowerCase() === 'btc') { + if (endpoint != undefined || blockchain != undefined) { if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] if (!endpoint) endpoint = 'difficulty' request.data.method = 'getblockchaininfo' } const response = await JSONRPC.execute(request) - - console.log(response.data.result) + if (endpoint) { if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] response.result = Requester.validateResultNumber(response.data, ['result', endpoint]) + // data are returned in result, due to the called adapter, needs to be moved to data object + response.data = response.data.result + response.data.result = response.result } - response.data = response.data.result + return response } - diff --git a/composite/bitcoin-json-rpc/src/index.ts b/composite/bitcoin-json-rpc/src/index.ts index ff34a1db3b..4d07246d09 100644 --- a/composite/bitcoin-json-rpc/src/index.ts +++ b/composite/bitcoin-json-rpc/src/index.ts @@ -1,6 +1,6 @@ import { expose, util } from '@chainlink/ea-bootstrap' import { execute } from './adapter' -const NAME = 'JSON-RPC' +const NAME = 'BITCOIN-JSON-RPC' export = { NAME, execute, ...expose(util.wrapExecute(execute)) } diff --git a/composite/bitcoin-json-rpc/test/adapter.test.ts b/composite/bitcoin-json-rpc/test/adapter.test.ts new file mode 100644 index 0000000000..69bcdad5ea --- /dev/null +++ b/composite/bitcoin-json-rpc/test/adapter.test.ts @@ -0,0 +1,82 @@ +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' +import { AdapterRequest } from '@chainlink/types' +import { Requester } from '@chainlink/external-adapter' + +/** + * Running these tests requires a connection to a Bitcoin client. + * Not all supported methods have a test case, just enough to display capability. + */ + +describe('execute', () => { + const jobID = '1' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'get blockchain info', + testData: { + id: jobID, + data: { + method: 'getblockchaininfo', + }, + }, + }, + { + name: 'get height with endpoint convention', + testData: { + id: jobID, + data: { endpoint: 'height' }, + }, + }, + { + name: 'get difficulty with blockchain convention and default endpoint', + testData: { + id: jobID, + data: { blockchain: 'BTC' }, + }, + }, + { + name: 'get height with common interface', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'height' }, + }, + }, + { + name: 'get difficulty with common interface', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'difficulty' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'endpoint not existing', + testData: { id: jobID, data: { endpoint: 'no_op' } }, + }, + ] + + 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/composite/bitcoin-json-rpc/test/aion.test.ts b/composite/bitcoin-json-rpc/test/aion.test.ts deleted file mode 100644 index fcb33b089e..0000000000 --- a/composite/bitcoin-json-rpc/test/aion.test.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to an AION client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('AION client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0xa00983f07c11ee9160a64dd3ba3dc3d1f88332a2869f25725f56cbd0be32ef7a', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_syncing', () => { - const req = { - id: jobID, - data: { - method: 'eth_syncing', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_gasPrice', () => { - const req = { - id: jobID, - data: { - method: 'eth_gasPrice', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_blockNumber', () => { - const req = { - id: jobID, - data: { - method: 'eth_blockNumber', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionByHash', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionByHash', - params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionReceipt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionReceipt', - params: ['0xe14a430e1a4131d32ddc1dd00f1b997ca2ba6812216af1f8e398d36bfd337d8e'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/composite/bitcoin-json-rpc/test/btc.test.ts b/composite/bitcoin-json-rpc/test/btc.test.ts deleted file mode 100644 index 6347fb7085..0000000000 --- a/composite/bitcoin-json-rpc/test/btc.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to a Bitcoin client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Bitcoin client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('getinfo', () => { - const req = { - id: jobID, - data: { - method: 'getinfo', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/composite/bitcoin-json-rpc/test/eth.test.ts b/composite/bitcoin-json-rpc/test/eth.test.ts deleted file mode 100644 index 98e45fcacc..0000000000 --- a/composite/bitcoin-json-rpc/test/eth.test.ts +++ /dev/null @@ -1,244 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to an Ethereum (or equivalent) client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Ethereum client @integration', async function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_syncing', () => { - const req = { - id: jobID, - data: { - method: 'eth_syncing', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_gasPrice', () => { - const req = { - id: jobID, - data: { - method: 'eth_gasPrice', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_blockNumber', () => { - const req = { - id: jobID, - data: { - method: 'eth_blockNumber', - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getBalance', () => { - const req = { - id: jobID, - data: { - method: 'eth_getBalance', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getStorageAt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getStorageAt', - params: ['0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', '0x0', 'latest'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_call', () => { - const req = { - id: jobID, - data: { - method: 'eth_call', - params: [ - { - to: '0x51DE85B0cD5B3684865ECfEedfBAF12777cd0Ff8', - data: '0x8da5cb5b', - }, - 'latest', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionByHash', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionByHash', - params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getTransactionReceipt', () => { - const req = { - id: jobID, - data: { - method: 'eth_getTransactionReceipt', - params: ['0xc0b989396d78277feb0a28de303652bc2c0b23f3f6fe76f67ff248ed481254f4'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - /** - * These functions only work with a connection to an unlocked client and have been - * disabled for the standard test suite. - */ - - context('eth_sign', () => { - const req = { - id: jobID, - data: { - method: 'eth_sign', - params: ['0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', '0x54686973206973206120746573742E'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_getProof', () => { - const req = { - id: jobID, - data: { - method: 'eth_getProof', - params: [ - '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - [ - '0x0000000000000000000000000000000000000000000000000000000000000000', - '0x0000000000000000000000000000000000000000000000000000000000000001', - ], - 'latest', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_sendTransaction', () => { - const req = { - id: jobID, - data: { - method: 'eth_sendTransaction', - params: [ - { - from: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - to: '0x87002564f1c7b8f51e96ca7d545e43402bf0b4ab', - data: '0x54686973206973206120746573742E', - }, - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('eth_sendRawTransaction', () => { - const req = { - id: jobID, - data: { - method: 'eth_sendRawTransaction', - params: [ - '0xf87582065c85012a05f2008284d09487002564f1c7b8f51e96ca7d545e43402bf0b4ab808f54686973206973206120746573742e29a01ee35bf6feecdd2597d578c656bac327e4aace48270bbc2fcbf8ed63f1232dcca0113e7131a2e133fd014a33f2de985914ad0eb98a594e20a132b559ec94716da4', - ], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/composite/bitcoin-json-rpc/test/zilliqa.test.ts b/composite/bitcoin-json-rpc/test/zilliqa.test.ts deleted file mode 100644 index 3f35c10660..0000000000 --- a/composite/bitcoin-json-rpc/test/zilliqa.test.ts +++ /dev/null @@ -1,86 +0,0 @@ -import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' - -/** - * Running these tests requires a connection to a Zilliqa client. - * Not all supported methods have a test case, just enough to display capability. - */ - -describe('Zilliqa client @integration', function () { - this.timeout(5000) - const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' - - context('Unrecognized method', () => { - const req = { - id: jobID, - data: { - method: 'no_op', - }, - } - - it('returns error to the node', async () => { - const resp = await execute(req) - assertError({ expected: 500, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetNetworkId', () => { - const req = { - id: jobID, - data: { - method: 'GetNetworkId', - params: [''], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetBalance', () => { - const req = { - id: jobID, - data: { - method: 'GetBalance', - params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11936'], - }, - } - - it('Get balance should return some address balance', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetBalance', () => { - const req = { - id: jobID, - data: { - method: 'GetBalance', - params: ['05fE66887AC5B6465f5aEda85E0557A29Ab11937'], - }, - } - - it('Get balance should return error as address is not created.', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) - - context('GetSmartContractState', () => { - const req = { - id: jobID, - data: { - method: 'GetSmartContractState', - params: ['5865337a32F48a04F5B52507442f47FC558d9C2b'], - }, - } - - it('returns data to the node', async () => { - const resp = await execute(req) - assertSuccess({ expected: 200, actual: resp.statusCode }, resp.data, jobID) - }) - }) -}) diff --git a/composite/bitcoin-json-rpc/tsconfig.json b/composite/bitcoin-json-rpc/tsconfig.json index f1f9a32efb..2e4c1b9ec1 100644 --- a/composite/bitcoin-json-rpc/tsconfig.json +++ b/composite/bitcoin-json-rpc/tsconfig.json @@ -3,8 +3,20 @@ "compilerOptions": { "outDir": "dist", "rootDir": "src", - "typeRoots": ["../../node_modules/@types", "../../typings", "../typings"] + "typeRoots": [ + "../../node_modules/@types", + "../../typings", + "./typings" + ], + "resolveJsonModule": true }, - "include": ["src/**/*"], - "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] + "include": [ + "src/**/*", + "src/**/*.json" + ], + "exclude": [ + "dist", + "**/*.spec.ts", + "**/*.test.ts" + ] } diff --git a/cryptoid/README.md b/cryptoid/README.md index fb112d67e6..354033f236 100644 --- a/cryptoid/README.md +++ b/cryptoid/README.md @@ -3,7 +3,7 @@ ## Input Params - `blockchain` or `coin`: The blockchain to get stats from -- `q`: The parameter to query for. Default: "difficulty" +- `endpoint`: The parameter to query for. Default: "difficulty" ## Output diff --git a/yarn.lock b/yarn.lock index fe06f8a2b4..3cec516bfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,9 +68,6 @@ "@truffle/contract" "^4.2.6" ethers "^4.0.45" -"@chainlink/json-rpc-adapter@file:json-rpc": - version "0.0.1" - "@chainlink/reduce@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@chainlink/reduce/-/reduce-0.0.3.tgz#6d26f5a0d299fce3497c0539e3aede2aff71f7d9" From 387db70fa5a1351c6939dbcc3de1e82882c69db2 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Tue, 12 Jan 2021 21:28:32 +0200 Subject: [PATCH 12/23] migrated cryptoid to TS --- cryptoid/.eslintrc.js | 3 ++ cryptoid/adapter.js | 33 ------------- cryptoid/index.js | 4 -- cryptoid/package.json | 41 ++++++++++++++-- cryptoid/src/adapter.ts | 47 +++++++++++++++++++ cryptoid/src/config.ts | 6 +++ cryptoid/src/index.ts | 7 +++ .../test/{adapter_test.js => adapter.test.ts} | 45 ++++++++++-------- cryptoid/tsconfig.json | 10 ++++ 9 files changed, 134 insertions(+), 62 deletions(-) create mode 100644 cryptoid/.eslintrc.js delete mode 100644 cryptoid/adapter.js delete mode 100644 cryptoid/index.js create mode 100644 cryptoid/src/adapter.ts create mode 100644 cryptoid/src/config.ts create mode 100644 cryptoid/src/index.ts rename cryptoid/test/{adapter_test.js => adapter.test.ts} (52%) create mode 100644 cryptoid/tsconfig.json diff --git a/cryptoid/.eslintrc.js b/cryptoid/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/cryptoid/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/cryptoid/adapter.js b/cryptoid/adapter.js deleted file mode 100644 index 89cd61992e..0000000000 --- a/cryptoid/adapter.js +++ /dev/null @@ -1,33 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customParams = { - blockchain: ['blockchain', 'coin'], - endpoint: false, -} - -const endpointToQ = { - difficulty: 'getdifficulty', - height: 'getblockcount', -} - -const execute = (input, callback) => { - const validator = new Validator(input, customParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - const blockchain = validator.validated.data.blockchain.toLowerCase() - const endpoint = validator.validated.data.endpoint || 'difficulty' - const url = `https://${blockchain}.cryptoid.info/${blockchain}/api.dws` - const key = util.getRandomRequiredEnv('API_KEY') - const q = endpointToQ[endpoint] - const params = { key, q } - const config = { url, params } - - Requester.request(config) - .then((response) => ({ ...response, data: { result: response.data } })) - .then((response) => callback(response.status, Requester.success(jobRunID, response))) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/cryptoid/index.js b/cryptoid/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/cryptoid/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/cryptoid/package.json b/cryptoid/package.json index 37aa646801..8011ea4a05 100644 --- a/cryptoid/package.json +++ b/cryptoid/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/cryptoid-adapter", "version": "0.0.1", + "description": "Chainlink cryptoid adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "cryptoid" + ], + "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 0 -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 0 --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/cryptoid/src/adapter.ts b/cryptoid/src/adapter.ts new file mode 100644 index 0000000000..d1d498acc9 --- /dev/null +++ b/cryptoid/src/adapter.ts @@ -0,0 +1,47 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { util } from '@chainlink/ea-bootstrap' + +const customParams = { + blockchain: ['blockchain', 'coin'], + endpoint: false, +} + +const endpointToQ: { [key: string]: string } = { + difficulty: 'getdifficulty', + height: 'getblockcount', +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + const blockchain = validator.validated.data.blockchain.toLowerCase() + + const key = util.getRandomRequiredEnv('API_KEY') + const q = endpointToQ[endpoint] + const params = { key, q } + + const reqConfig = { + ...config.api, + params, + baseURL: `https://${blockchain}.cryptoid.info/${blockchain}/api.dws`, + } + const response = await Requester.request(reqConfig) + const result = response.data + + return Requester.success(jobRunID, { + data: { result }, + result, + status: 200, + }) +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/cryptoid/src/config.ts b/cryptoid/src/config.ts new file mode 100644 index 0000000000..981d982259 --- /dev/null +++ b/cryptoid/src/config.ts @@ -0,0 +1,6 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'difficulty' + +export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) diff --git a/cryptoid/src/index.ts b/cryptoid/src/index.ts new file mode 100644 index 0000000000..41f8a8e898 --- /dev/null +++ b/cryptoid/src/index.ts @@ -0,0 +1,7 @@ +import { expose, util } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'EXAMPLE' + +export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } diff --git a/cryptoid/test/adapter_test.js b/cryptoid/test/adapter.test.ts similarity index 52% rename from cryptoid/test/adapter_test.js rename to cryptoid/test/adapter.test.ts index 5d0cbebfc3..9414a1ab57 100644 --- a/cryptoid/test/adapter_test.js +++ b/cryptoid/test/adapter.test.ts @@ -1,9 +1,12 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') +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 = [ @@ -36,13 +39,11 @@ describe('execute', () => { ] 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() - }) + 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) }) }) }) @@ -54,11 +55,13 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + } }) }) }) @@ -72,11 +75,13 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) + 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/cryptoid/tsconfig.json b/cryptoid/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/cryptoid/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"] +} From af23c5e6f3a075ce9312d4197bf938081f322c6f Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Wed, 13 Jan 2021 12:13:48 +0200 Subject: [PATCH 13/23] Changed blockchair according to the new changes. Created stats instead of diffuculty endpoint and added height support --- blockchair/adapter-old.js | 47 ------------------- blockchair/src/adapter.ts | 8 ++-- blockchair/src/endpoint/balance.ts | 2 +- blockchair/src/endpoint/index.ts | 2 +- .../src/endpoint/{difficulty.ts => stats.ts} | 12 ++++- .../{difficulty.test.ts => stats.test.ts} | 18 ++++++- yarn.lock | 7 --- 7 files changed, 34 insertions(+), 62 deletions(-) delete mode 100644 blockchair/adapter-old.js rename blockchair/src/endpoint/{difficulty.ts => stats.ts} (70%) rename blockchair/test/{difficulty.test.ts => stats.test.ts} (78%) diff --git a/blockchair/adapter-old.js b/blockchair/adapter-old.js deleted file mode 100644 index b4cd3b10a5..0000000000 --- a/blockchair/adapter-old.js +++ /dev/null @@ -1,47 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customParams = { - blockchain: ['blockchain', 'coin'], - endpoint: false, -} - -const convertBlockchain = { - BTC: 'bitcoin', - BCH: 'bitcoin-cash', - BSV: 'bitcoin-sv', - ETH: 'ethereum', - LTC: 'litecoin', -} - -const convertEndpoint = { - height: 'blocks', -} - -const execute = (input, callback) => { - const validator = new Validator(input, customParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - let blockchain = validator.validated.data.blockchain - let endpoint = validator.validated.data.endpoint || 'difficulty' - - if (blockchain in convertBlockchain) blockchain = convertBlockchain[blockchain] - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] - - const url = `https://api.blockchair.com/${blockchain.toLowerCase()}/stats` - const key = util.getRandomRequiredEnv('API_KEY') - - const params = {} - if (key.length > 0) params.key = key - - const config = { url, params } - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['data', endpoint]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/blockchair/src/adapter.ts b/blockchair/src/adapter.ts index 0e1cfccccb..a4e301f768 100644 --- a/blockchair/src/adapter.ts +++ b/blockchair/src/adapter.ts @@ -1,7 +1,7 @@ import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { difficulty, balance } from './endpoint' +import { stats, balance } from './endpoint' const inputParams = { endpoint: false, @@ -18,8 +18,10 @@ const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint) { - case difficulty.Name: { - return difficulty.execute(request, config) + // might be moved to validator or config + case 'difficulty': + case 'height': { + return stats.execute(request, config) } case balance.Name: { return balance.makeExecute(config)(request) diff --git a/blockchair/src/endpoint/balance.ts b/blockchair/src/endpoint/balance.ts index 0e9091b054..cf4abc6997 100644 --- a/blockchair/src/endpoint/balance.ts +++ b/blockchair/src/endpoint/balance.ts @@ -11,7 +11,7 @@ const getBalanceURI = (addresses: string[], coin: string, chain: string) => { return `/${coin}/addresses/balances?addresses=${addresses.join(',')}` } -const getBalances: balance.GetBalances = async (accounts, config) => { +const getBalances: balance.GetBalances = async (accounts, config: Config) => { const { coin, chain } = accounts[0] const addresses = accounts.map((a) => a.address) diff --git a/blockchair/src/endpoint/index.ts b/blockchair/src/endpoint/index.ts index f40602eb1d..8d7576c114 100644 --- a/blockchair/src/endpoint/index.ts +++ b/blockchair/src/endpoint/index.ts @@ -1,5 +1,5 @@ export * as balance from './balance' -export * as difficulty from './difficulty' +export * as stats from './stats' export const COIN_KEYS = ['btc', 'dash', 'doge', 'ltc', 'bch'] as const export type CoinType = typeof COIN_KEYS[number] diff --git a/blockchair/src/endpoint/difficulty.ts b/blockchair/src/endpoint/stats.ts similarity index 70% rename from blockchair/src/endpoint/difficulty.ts rename to blockchair/src/endpoint/stats.ts index ab19544719..84f0b04a20 100644 --- a/blockchair/src/endpoint/difficulty.ts +++ b/blockchair/src/endpoint/stats.ts @@ -1,17 +1,25 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' import { COINS } from '.' +import { DEFAULT_ENDPOINT } from '../config' -export const Name = 'difficulty' +export const Name = 'stats' const inputParams = { blockchain: ['blockchain', 'coin'], + endpoint: false, +} + +const convertEndpoint: { [key: string]: string } = { + height: 'blocks', } export const execute: ExecuteWithConfig = async (input, config) => { const validator = new Validator(input, inputParams) if (validator.error) throw validator.error const jobRunID = validator.validated.id + let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] const blockchain = Requester.toVendorName( validator.validated.data.blockchain.toLowerCase(), @@ -22,6 +30,6 @@ export const execute: ExecuteWithConfig = async (input, config) => { const reqConfig = { ...config.api, url } const response = await Requester.request(reqConfig) - response.data.result = Requester.validateResultNumber(response.data, ['data', 'difficulty']) + response.data.result = Requester.validateResultNumber(response.data, ['data', endpoint]) return Requester.success(jobRunID, response) } diff --git a/blockchair/test/difficulty.test.ts b/blockchair/test/stats.test.ts similarity index 78% rename from blockchair/test/difficulty.test.ts rename to blockchair/test/stats.test.ts index 46c014294c..289a48d969 100644 --- a/blockchair/test/difficulty.test.ts +++ b/blockchair/test/stats.test.ts @@ -4,7 +4,7 @@ import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' import { AdapterRequest } from '@chainlink/types' import { makeExecute } from '../src/adapter' -describe('difficulty endpoint', () => { +describe('stats endpoint', () => { const jobID = '1' const execute = makeExecute() @@ -22,6 +22,22 @@ describe('difficulty endpoint', () => { name: 'coin', testData: { id: jobID, data: { coin: 'BTC' } }, }, + { + name: 'blockchain difficulty with endpoint', + testData: { id: jobID, data: { blockchain: 'BTC', endpoint: 'difficulty' } }, + }, + { + name: 'coing difficulty with endpoint', + testData: { id: jobID, data: { coin: 'BTC', endpoint: 'difficulty' } }, + }, + { + name: 'blockchain height', + testData: { id: jobID, data: { blockchain: 'BTC', endpoint: 'height' } }, + }, + { + name: 'coin height', + testData: { id: jobID, data: { coin: 'BTC', endpoint: 'height' } }, + }, ] requests.forEach((req) => { diff --git a/yarn.lock b/yarn.lock index 3cec516bfa..01503faa59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -68,13 +68,6 @@ "@truffle/contract" "^4.2.6" ethers "^4.0.45" -"@chainlink/reduce@^0.0.3": - version "0.0.3" - resolved "https://registry.yarnpkg.com/@chainlink/reduce/-/reduce-0.0.3.tgz#6d26f5a0d299fce3497c0539e3aede2aff71f7d9" - integrity sha512-uym+pqQBbp3lGNSTpJGGSp32UymgTSDZrqPjoft8/nm0yDeF3x7FpRfvc6y79ap0PiX6wpv8kGqOL9KR/tLI9A== - dependencies: - object-path "^0.11.4" - "@chainlink/reference-data-reader@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@chainlink/reference-data-reader/-/reference-data-reader-0.0.1.tgz#5dd0751d851fcc38ee1d08742ef6851661bcfe3e" From 3d957cc423b11098bca47822b607a178c376d6e1 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Wed, 13 Jan 2021 12:55:20 +0200 Subject: [PATCH 14/23] Changed cryptoapis difficulty to stats and added height, after TS migration --- cryptoapis/package.json | 2 +- cryptoapis/src/adapter.ts | 7 ++++--- cryptoapis/src/endpoint/index.ts | 2 +- .../src/endpoint/{difficulty.ts => stats.ts} | 16 ++++++++++++---- .../test/{difficulty.test.ts => stats.test.ts} | 18 ++++++++++++++++-- 5 files changed, 34 insertions(+), 11 deletions(-) rename cryptoapis/src/endpoint/{difficulty.ts => stats.ts} (62%) rename cryptoapis/test/{difficulty.test.ts => stats.test.ts} (80%) diff --git a/cryptoapis/package.json b/cryptoapis/package.json index 34a1a459c8..a095b529b0 100644 --- a/cryptoapis/package.json +++ b/cryptoapis/package.json @@ -11,7 +11,7 @@ "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", "test": "yarn test:unit && yarn test:integration", "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", - "test:integration": "mocha --timeout 6000 --exit --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --timeout 0 --exit --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" diff --git a/cryptoapis/src/adapter.ts b/cryptoapis/src/adapter.ts index 531b89f19c..9bfcf39a90 100644 --- a/cryptoapis/src/adapter.ts +++ b/cryptoapis/src/adapter.ts @@ -1,7 +1,7 @@ import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { price, difficulty, balance } from './endpoint' +import { price, stats, balance } from './endpoint' const inputParams = { endpoint: false, @@ -23,8 +23,9 @@ export const execute: ExecuteWithConfig = async (request, config) => { response = await price.execute(config, request) break } - case difficulty.Name: { - response = await difficulty.execute(config, request) + case 'difficulty': + case 'height': { + response = await stats.execute(config, request) break } case balance.Name: { diff --git a/cryptoapis/src/endpoint/index.ts b/cryptoapis/src/endpoint/index.ts index 8b978122a6..3a8fbad0a4 100644 --- a/cryptoapis/src/endpoint/index.ts +++ b/cryptoapis/src/endpoint/index.ts @@ -1,6 +1,6 @@ export * as balance from './balance' export * as price from './price' -export * as difficulty from './difficulty' +export * as stats from './stats' export const COIN_KEYS = ['btc', 'eth', 'etc', 'bch', 'ltc', 'dash', 'doge', 'btcv', 'zil'] as const export type CoinType = typeof COIN_KEYS[number] diff --git a/cryptoapis/src/endpoint/difficulty.ts b/cryptoapis/src/endpoint/stats.ts similarity index 62% rename from cryptoapis/src/endpoint/difficulty.ts rename to cryptoapis/src/endpoint/stats.ts index 5b4ab7f2a7..f766a75b01 100644 --- a/cryptoapis/src/endpoint/difficulty.ts +++ b/cryptoapis/src/endpoint/stats.ts @@ -1,24 +1,32 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' +import { DEFAULT_ENDPOINT } from '../config' -export const Name = 'difficulty' +export const Name = 'stats' -const difficultyParams = { +const statsParams = { blockchain: ['blockchain', 'coin'], + endpoint: false, network: false, } +const convertEndpoint: { [key: string]: string } = { + height: 'headers', +} + export const execute = async (config: Config, request: AdapterRequest) => { - const validator = new Validator(request, difficultyParams) + const validator = new Validator(request, statsParams) if (validator.error) throw validator.error const blockchain = validator.validated.data.blockchain const network = validator.validated.data.network || 'mainnet' + let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] const url = `/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/info` const reqConfig = { ...config.api, url } const response = await Requester.request(reqConfig) - response.data.result = Requester.validateResultNumber(response.data, ['payload', 'difficulty']) + response.data.result = Requester.validateResultNumber(response.data, ['payload', endpoint]) return response } diff --git a/cryptoapis/test/difficulty.test.ts b/cryptoapis/test/stats.test.ts similarity index 80% rename from cryptoapis/test/difficulty.test.ts rename to cryptoapis/test/stats.test.ts index fb0359f1b9..fdf627f8b6 100644 --- a/cryptoapis/test/difficulty.test.ts +++ b/cryptoapis/test/stats.test.ts @@ -4,7 +4,7 @@ import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' import { AdapterRequest } from '@chainlink/types' import { makeExecute } from '../src/adapter' -describe('difficulty endpoint', () => { +describe('stats endpoint', () => { const jobID = '1' const execute = makeExecute() @@ -21,7 +21,21 @@ describe('difficulty endpoint', () => { name: 'BTC testnet difficulty', testData: { id: jobID, - data: { blockchain: 'BTC', network: 'Testnet', endpoint: 'difficulty' }, + data: { blockchain: 'BTC', network: 'testnet', endpoint: 'difficulty' }, + }, + }, + { + name: 'BTC height', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'height' }, + }, + }, + { + name: 'BTC testnet height', + testData: { + id: jobID, + data: { blockchain: 'BTC', network: 'testnet', endpoint: 'height' }, }, }, ] From eeb81c8897210d3f50269a001c60078d58d5f275 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Wed, 13 Jan 2021 17:07:40 +0200 Subject: [PATCH 15/23] fixed README --- cryptoapis/README.md | 73 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/cryptoapis/README.md b/cryptoapis/README.md index 11ec80ad52..187e9986e8 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -42,3 +42,76 @@ The adapter takes the following environment variables: "statusCode": 200 } ``` + +### Balance endpoint + +https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/btc/index#btc-address-info-endpoint + +- `dataPath`: Optional path where to find the addresses array, defaults to `result` +- `confirmations`: Optional confirmations param, defaults to `6` + +- `addresses`: Addresses to query + + { + + - `address`: Address to query + - `coin`: Optional currency to query, defaults to `btc`, one of `(btc|eth|etc|bch|ltc|dash|doge|btcv|zil)` + - `chain`: Optional chain to query, defaults to `mainnet`, one of `(mainnet|testnet)` + + } + +```json +{ + "id": "1", + "data": { + "addresses": [ + { + "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", + "chain": "testnet" + } + ], + "dataPath": "addresses" + } +} +``` + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "responses": [ + { + "payload": { + "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", + "totalSpent": "0.0498", + "totalReceived": "131.40923575", + "balance": "131.35943575", + "txi": 1, + "txo": 1590, + "txsCount": 1587, + "addresses": ["n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF"] + } + } + ], + "result": [ + { + "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", + "chain": "testnet", + "coin": "btc", + "balance": "131.35943575" + } + ] + }, + "result": [ + { + "address": "n4VQ5YdHf7hLQ2gWQYYrcxoE5B7nWuDFNF", + "chain": "testnet", + "coin": "btc", + "balance": "13135943575" + } + ], + "statusCode": 200 +} +``` From edcff2a298ea9605c3d245ebf06d7c7ab779a0f8 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 10:56:39 +0200 Subject: [PATCH 16/23] reverted to simpler version of bitcoin-json-rpc + review changes --- composite/bitcoin-json-rpc/README.md | 30 +-------- composite/bitcoin-json-rpc/src/adapter.ts | 46 ++++++------- composite/bitcoin-json-rpc/src/config.ts | 6 ++ .../bitcoin-json-rpc/test/adapter.test.ts | 66 ++++++++++--------- cryptoid/src/index.ts | 2 +- 5 files changed, 63 insertions(+), 87 deletions(-) create mode 100644 composite/bitcoin-json-rpc/src/config.ts diff --git a/composite/bitcoin-json-rpc/README.md b/composite/bitcoin-json-rpc/README.md index 545399a601..5554a37c4f 100644 --- a/composite/bitcoin-json-rpc/README.md +++ b/composite/bitcoin-json-rpc/README.md @@ -1,8 +1,6 @@ -# Bitcoin JSON-RPC External Adapter for Chainlink +# Bitcoin JSON-RPC Blockchain info adapter for Chainlink -- Should work for any Bitcoin JSON RPC supported endpoint - Supports AWS Lambda and GCP Functions -- Bitcoin clients can sign and send transactions if wallet is unlocked - Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) ## Input Params @@ -66,32 +64,6 @@ Returns blockchain info stats, by calling `"method": "getblockchainfo"` } ``` -Also allows for general JSON-RPC requests, formatted like this: - -# Input - -```JSON -{ - "jsonrpc": "2.0", - "method": "some_method", - "params": [ - "some_param", - "another_param" - ], - "id": 1 -} -``` - -# Output - -```JSON -{ - "id":1, - "jsonrpc": "2.0", - "result": "some_result" -} -``` - ## Install Install dependencies diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index d38df8e1cc..ce74f45aab 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -1,14 +1,9 @@ import JSONRPC from '@chainlink/json-rpc-adapter' -import { Execute, AdapterRequest } from '@chainlink/types' -import { Validator } from '@chainlink/external-adapter' -import { Requester } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory, AdapterRequest } from '@chainlink/types' +import { Validator, Requester } from '@chainlink/external-adapter' +import { makeConfig, DEFAULT_ENDPOINT } from './config' const inputParams = { - url: false, - method: false, - params: false, - blockchain: false, - coin: false, endpoint: false, } @@ -17,28 +12,25 @@ const convertEndpoint: { [key: string]: string } = { } // Export function to integrate with Chainlink node -export const execute: Execute = async (request: AdapterRequest) => { +export const execute: ExecuteWithConfig = async (request: AdapterRequest) => { const validator = new Validator(request, inputParams) - const blockchain = validator.validated.data.blockchain || validator.validated.data.coin - - let endpoint = validator.validated.data.endpoint - if (validator.error) throw validator.error - if (endpoint != undefined || blockchain != undefined) { - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] - if (!endpoint) endpoint = 'difficulty' - request.data.method = 'getblockchaininfo' - } - const response = await JSONRPC.execute(request) + const jobRunID = validator.validated.id + let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - if (endpoint) { - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] - response.result = Requester.validateResultNumber(response.data, ['result', endpoint]) - // data are returned in result, due to the called adapter, needs to be moved to data object - response.data = response.data.result - response.data.result = response.result - } + const jsonrpcRequest: AdapterRequest = { id: jobRunID, data: { method: 'getblockchaininfo' } } + const response = await JSONRPC.execute(jsonrpcRequest) + + if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + const result = Requester.validateResultNumber(response.data, ['result', endpoint]) + return Requester.success(jobRunID, { + data: { ...response.data.result, result }, + result, + status: 200, + }) +} - return response +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) } diff --git a/composite/bitcoin-json-rpc/src/config.ts b/composite/bitcoin-json-rpc/src/config.ts new file mode 100644 index 0000000000..981d982259 --- /dev/null +++ b/composite/bitcoin-json-rpc/src/config.ts @@ -0,0 +1,6 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'difficulty' + +export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) diff --git a/composite/bitcoin-json-rpc/test/adapter.test.ts b/composite/bitcoin-json-rpc/test/adapter.test.ts index 69bcdad5ea..9414a1ab57 100644 --- a/composite/bitcoin-json-rpc/test/adapter.test.ts +++ b/composite/bitcoin-json-rpc/test/adapter.test.ts @@ -1,53 +1,39 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' import { AdapterRequest } from '@chainlink/types' -import { Requester } from '@chainlink/external-adapter' - -/** - * Running these tests requires a connection to a Bitcoin client. - * Not all supported methods have a test case, just enough to display capability. - */ +import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' + const execute = makeExecute() context('successful calls @integration', () => { const requests = [ { - name: 'get blockchain info', - testData: { - id: jobID, - data: { - method: 'getblockchaininfo', - }, - }, + name: 'id not supplied', + testData: { data: { blockchain: 'BTC' } }, }, { - name: 'get height with endpoint convention', - testData: { - id: jobID, - data: { endpoint: 'height' }, - }, + name: 'blockchain', + testData: { id: jobID, data: { blockchain: 'BTC' } }, }, { - name: 'get difficulty with blockchain convention and default endpoint', - testData: { - id: jobID, - data: { blockchain: 'BTC' }, - }, + name: 'coin', + testData: { id: jobID, data: { coin: 'BTC' } }, }, { - name: 'get height with common interface', + name: 'BTC difficulty', testData: { id: jobID, - data: { blockchain: 'BTC', endpoint: 'height' }, + data: { blockchain: 'BTC' }, }, }, { - name: 'get difficulty with common interface', + name: 'BTC height', testData: { id: jobID, - data: { blockchain: 'BTC', endpoint: 'difficulty' }, + data: { blockchain: 'BTC', endpoint: 'height' }, }, }, ] @@ -56,6 +42,26 @@ describe('execute', () => { 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('validation error', () => { + const requests = [ + { name: 'empty body', testData: {} }, + { name: 'empty data', testData: { data: {} } }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + } }) }) }) @@ -63,8 +69,8 @@ describe('execute', () => { context('error calls @integration', () => { const requests = [ { - name: 'endpoint not existing', - testData: { id: jobID, data: { endpoint: 'no_op' } }, + name: 'unknown blockchain', + testData: { id: jobID, data: { blockchain: 'not_real' } }, }, ] diff --git a/cryptoid/src/index.ts b/cryptoid/src/index.ts index 41f8a8e898..51bc23e101 100644 --- a/cryptoid/src/index.ts +++ b/cryptoid/src/index.ts @@ -2,6 +2,6 @@ import { expose, util } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' -const NAME = 'EXAMPLE' +const NAME = 'CRYPTO-ID' export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } From 6e70ec8708594d38cd7f70fa31faa9a7dfef3c91 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 12:06:12 +0200 Subject: [PATCH 17/23] reviews changed + reverted tests --- composite/bitcoin-json-rpc/README.md | 6 +- composite/bitcoin-json-rpc/src/adapter.ts | 6 +- .../bitcoin-json-rpc/test/adapter.test.ts | 60 +++++++------------ cryptoid/src/adapter.ts | 6 +- 4 files changed, 33 insertions(+), 45 deletions(-) diff --git a/composite/bitcoin-json-rpc/README.md b/composite/bitcoin-json-rpc/README.md index 5554a37c4f..791d89e111 100644 --- a/composite/bitcoin-json-rpc/README.md +++ b/composite/bitcoin-json-rpc/README.md @@ -5,7 +5,7 @@ ## Input Params -Returns blockchain info stats, by calling `"method": "getblockchainfo"` +Returns blockchain info stats, by calling `"method": "getblockchainfo"`. It relies on `json-rpc` adapter. - `endpoint`: The parameter to query for. Default: "difficulty" @@ -69,7 +69,7 @@ Returns blockchain info stats, by calling `"method": "getblockchainfo"` Install dependencies ```bash -npm install +yarn ``` Set the `RPC_URL` environment variable to your client URL. @@ -83,5 +83,5 @@ RPC Address and Port Defaults: - BTC: (bitcoind) http://localhost:8332 (btcd) http://localhost:8334 ```bash -npm run test +yarn test ``` diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index ce74f45aab..a25917e273 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -19,8 +19,10 @@ export const execute: ExecuteWithConfig = async (request: AdapterRequest const jobRunID = validator.validated.id let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - const jsonrpcRequest: AdapterRequest = { id: jobRunID, data: { method: 'getblockchaininfo' } } - const response = await JSONRPC.execute(jsonrpcRequest) + const response = await JSONRPC.execute({ + ...request, + data: { ...request.data, method: 'getblockchaininfo' }, + }) if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] const result = Requester.validateResultNumber(response.data, ['result', endpoint]) diff --git a/composite/bitcoin-json-rpc/test/adapter.test.ts b/composite/bitcoin-json-rpc/test/adapter.test.ts index 9414a1ab57..c9b160f045 100644 --- a/composite/bitcoin-json-rpc/test/adapter.test.ts +++ b/composite/bitcoin-json-rpc/test/adapter.test.ts @@ -1,67 +1,53 @@ -import { assert } from 'chai' -import { Requester } from '@chainlink/external-adapter' import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { execute } from '../src/adapter' import { AdapterRequest } from '@chainlink/types' +import { Requester } from '@chainlink/external-adapter' import { makeExecute } from '../src/adapter' +/** + * Running these tests requires a connection to a Bitcoin client. + * Not all supported methods have a test case, just enough to display capability. + */ + describe('execute', () => { const jobID = '1' const execute = makeExecute() - context('successful calls @integration', () => { const requests = [ { - name: 'id not supplied', - testData: { data: { blockchain: 'BTC' } }, - }, - { - name: 'blockchain', - testData: { id: jobID, data: { blockchain: 'BTC' } }, - }, - { - name: 'coin', - testData: { id: jobID, data: { coin: 'BTC' } }, + name: 'get height with endpoint convention', + testData: { + id: jobID, + data: { endpoint: 'height' }, + }, }, { - name: 'BTC difficulty', + name: 'get difficulty with blockchain convention and default endpoint', testData: { id: jobID, data: { blockchain: 'BTC' }, }, }, { - name: 'BTC height', + name: 'get height with common interface', testData: { id: jobID, data: { blockchain: 'BTC', endpoint: 'height' }, }, }, + { + name: 'get difficulty with common interface', + testData: { + id: jobID, + data: { blockchain: 'BTC', endpoint: 'difficulty' }, + }, + }, ] 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('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, async () => { - try { - await execute(req.testData as AdapterRequest) - } catch (error) { - const errorResp = Requester.errored(jobID, error) - assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) - } }) }) }) @@ -69,8 +55,8 @@ describe('execute', () => { context('error calls @integration', () => { const requests = [ { - name: 'unknown blockchain', - testData: { id: jobID, data: { blockchain: 'not_real' } }, + name: 'endpoint not existing', + testData: { id: jobID, data: { endpoint: 'no_op' } }, }, ] diff --git a/cryptoid/src/adapter.ts b/cryptoid/src/adapter.ts index d1d498acc9..ba17874b81 100644 --- a/cryptoid/src/adapter.ts +++ b/cryptoid/src/adapter.ts @@ -8,7 +8,7 @@ const customParams = { endpoint: false, } -const endpointToQ: { [key: string]: string } = { +const endpointToApiParam: { [key: string]: string } = { difficulty: 'getdifficulty', height: 'getblockcount', } @@ -24,8 +24,8 @@ export const execute: ExecuteWithConfig = async (request, config) => { const blockchain = validator.validated.data.blockchain.toLowerCase() const key = util.getRandomRequiredEnv('API_KEY') - const q = endpointToQ[endpoint] - const params = { key, q } + const apiParam = endpointToApiParam[endpoint] + const params = { key, q: apiParam } const reqConfig = { ...config.api, From a62df92a21c5818a61843fc0ffaa76066415545d Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 13:00:50 +0200 Subject: [PATCH 18/23] changed var name --- cryptoid/src/adapter.ts | 6 +++--- yarn.lock | 28 ++++++++++------------------ 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/cryptoid/src/adapter.ts b/cryptoid/src/adapter.ts index ba17874b81..4fbd633342 100644 --- a/cryptoid/src/adapter.ts +++ b/cryptoid/src/adapter.ts @@ -8,7 +8,7 @@ const customParams = { endpoint: false, } -const endpointToApiParam: { [key: string]: string } = { +const endpointToApiFunctionName: { [key: string]: string } = { difficulty: 'getdifficulty', height: 'getblockcount', } @@ -24,8 +24,8 @@ export const execute: ExecuteWithConfig = async (request, config) => { const blockchain = validator.validated.data.blockchain.toLowerCase() const key = util.getRandomRequiredEnv('API_KEY') - const apiParam = endpointToApiParam[endpoint] - const params = { key, q: apiParam } + const apiFunctionName = endpointToApiFunctionName[endpoint] + const params = { key, q: apiFunctionName } const reqConfig = { ...config.api, diff --git a/yarn.lock b/yarn.lock index 01503faa59..bf83f08c0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -993,6 +993,11 @@ resolved "https://registry.yarnpkg.com/@types/bchaddrjs/-/bchaddrjs-0.4.0.tgz#f3d29ab56096c9d7105b5ccbc69969d11426e087" integrity sha512-Nvv3haWpXNWYfKasVoEp3VBgVsBTLTh45anhHUN8xVUPhn4ErU3FPS5AEcZtACWc5ICGUxbaiLTWOnwDkwYF1Q== +"@types/big.js@^6.0.0": + version "6.0.2" + resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.0.2.tgz#a86938c1bb4511e69d91f69823cacc3f48ff1fa3" + integrity sha512-7NdmOT3zjtghMofDwP1nAJCJWVjc/96V5msXRAZ4lPrvpGlajA95VQec7OXwA2wQaVmhjt+F5ko8pjvQU1tTFA== + "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -1624,6 +1629,11 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== +big.js@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.0.3.tgz#8b4d99ac7023668e0e465d3f78c23b8ac29ad381" + integrity sha512-n6yn1FyVL1EW2DBAr4jlU/kObhRzmr+NNRESl65VIOT8WBJj/Kezpx2zFdhJUqYI6qrtTW7moCStYL5VxeVdPA== + bigi@^1.1.0, bigi@^1.4.0: version "1.4.2" resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" @@ -5225,14 +5235,6 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== -node-cron@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-2.0.3.tgz#b9649784d0d6c00758410eef22fa54a10e3f602d" - integrity sha512-eJI+QitXlwcgiZwNNSRbqsjeZMp5shyajMR81RZCqeW0ZDEj4zU9tpd4nTh/1JsBiKbF8d08FCewiipDmVIYjg== - dependencies: - opencollective-postinstall "^2.0.0" - tz-offset "0.0.1" - node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -5414,11 +5416,6 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" -opencollective-postinstall@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" - integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== - "openzeppelin-solidity-2.3.0@npm:openzeppelin-solidity@2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.3.0.tgz#1ab7b4cc3782a5472ed61eb740c56a8bfdd74119" @@ -7026,11 +7023,6 @@ typescript@^3.9.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== -tz-offset@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/tz-offset/-/tz-offset-0.0.1.tgz#fef920257024d3583ed9072a767721a18bdb8a76" - integrity sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ== - ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" From 1ec7ed280b1a427c0a7d110fb39bfb57c3c49bbc Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 14:47:16 +0200 Subject: [PATCH 19/23] Review changes --- blockchair/src/endpoint/balance.ts | 2 +- composite/bitcoin-json-rpc/README.md | 1 - composite/bitcoin-json-rpc/src/adapter.ts | 4 ++-- composite/bitcoin-json-rpc/src/config.ts | 1 + composite/bitcoin-json-rpc/src/index.ts | 6 +++--- composite/bitcoin-json-rpc/test/adapter.test.ts | 1 - cryptoapis/src/adapter.ts | 4 ++-- cryptoapis/src/endpoint/{stats.ts => bc_info.ts} | 2 +- cryptoapis/src/endpoint/index.ts | 2 +- cryptoid/src/adapter.ts | 3 +-- 10 files changed, 12 insertions(+), 14 deletions(-) rename cryptoapis/src/endpoint/{stats.ts => bc_info.ts} (97%) diff --git a/blockchair/src/endpoint/balance.ts b/blockchair/src/endpoint/balance.ts index cf4abc6997..0e9091b054 100644 --- a/blockchair/src/endpoint/balance.ts +++ b/blockchair/src/endpoint/balance.ts @@ -11,7 +11,7 @@ const getBalanceURI = (addresses: string[], coin: string, chain: string) => { return `/${coin}/addresses/balances?addresses=${addresses.join(',')}` } -const getBalances: balance.GetBalances = async (accounts, config: Config) => { +const getBalances: balance.GetBalances = async (accounts, config) => { const { coin, chain } = accounts[0] const addresses = accounts.map((a) => a.address) diff --git a/composite/bitcoin-json-rpc/README.md b/composite/bitcoin-json-rpc/README.md index 791d89e111..73fa8cb4c5 100644 --- a/composite/bitcoin-json-rpc/README.md +++ b/composite/bitcoin-json-rpc/README.md @@ -1,6 +1,5 @@ # Bitcoin JSON-RPC Blockchain info adapter for Chainlink -- Supports AWS Lambda and GCP Functions - Takes optional connection to RPC endpoint (set via `RPC_URL` environment variable) ## Input Params diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index a25917e273..1be94f5768 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -1,7 +1,7 @@ import JSONRPC from '@chainlink/json-rpc-adapter' import { Config, ExecuteWithConfig, ExecuteFactory, AdapterRequest } from '@chainlink/types' import { Validator, Requester } from '@chainlink/external-adapter' -import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { DEFAULT_ENDPOINT, makeConfig } from './config' const inputParams = { endpoint: false, @@ -12,7 +12,7 @@ const convertEndpoint: { [key: string]: string } = { } // Export function to integrate with Chainlink node -export const execute: ExecuteWithConfig = async (request: AdapterRequest) => { +const execute: ExecuteWithConfig = async (request: AdapterRequest) => { const validator = new Validator(request, inputParams) if (validator.error) throw validator.error diff --git a/composite/bitcoin-json-rpc/src/config.ts b/composite/bitcoin-json-rpc/src/config.ts index 981d982259..48bea4203d 100644 --- a/composite/bitcoin-json-rpc/src/config.ts +++ b/composite/bitcoin-json-rpc/src/config.ts @@ -3,4 +3,5 @@ import { Config } from '@chainlink/types' export const DEFAULT_ENDPOINT = 'difficulty' +// TODO: needs to setup config for underlying JSON-RPC adapter export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) diff --git a/composite/bitcoin-json-rpc/src/index.ts b/composite/bitcoin-json-rpc/src/index.ts index 4d07246d09..65e183daef 100644 --- a/composite/bitcoin-json-rpc/src/index.ts +++ b/composite/bitcoin-json-rpc/src/index.ts @@ -1,6 +1,6 @@ +import { makeConfig } from './config' +import { makeExecute } from './adapter' import { expose, util } from '@chainlink/ea-bootstrap' -import { execute } from './adapter' const NAME = 'BITCOIN-JSON-RPC' - -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } diff --git a/composite/bitcoin-json-rpc/test/adapter.test.ts b/composite/bitcoin-json-rpc/test/adapter.test.ts index c9b160f045..cadc0f02cd 100644 --- a/composite/bitcoin-json-rpc/test/adapter.test.ts +++ b/composite/bitcoin-json-rpc/test/adapter.test.ts @@ -1,5 +1,4 @@ import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' -import { execute } from '../src/adapter' import { AdapterRequest } from '@chainlink/types' import { Requester } from '@chainlink/external-adapter' import { makeExecute } from '../src/adapter' diff --git a/cryptoapis/src/adapter.ts b/cryptoapis/src/adapter.ts index 9bfcf39a90..4136a726e5 100644 --- a/cryptoapis/src/adapter.ts +++ b/cryptoapis/src/adapter.ts @@ -1,7 +1,7 @@ import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { price, stats, balance } from './endpoint' +import { price, bc_info, balance } from './endpoint' const inputParams = { endpoint: false, @@ -25,7 +25,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { } case 'difficulty': case 'height': { - response = await stats.execute(config, request) + response = await bc_info.execute(config, request) break } case balance.Name: { diff --git a/cryptoapis/src/endpoint/stats.ts b/cryptoapis/src/endpoint/bc_info.ts similarity index 97% rename from cryptoapis/src/endpoint/stats.ts rename to cryptoapis/src/endpoint/bc_info.ts index f766a75b01..6d7df60834 100644 --- a/cryptoapis/src/endpoint/stats.ts +++ b/cryptoapis/src/endpoint/bc_info.ts @@ -2,7 +2,7 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' import { DEFAULT_ENDPOINT } from '../config' -export const Name = 'stats' +export const Name = 'bc_info' const statsParams = { blockchain: ['blockchain', 'coin'], diff --git a/cryptoapis/src/endpoint/index.ts b/cryptoapis/src/endpoint/index.ts index 3a8fbad0a4..ac0d59e88b 100644 --- a/cryptoapis/src/endpoint/index.ts +++ b/cryptoapis/src/endpoint/index.ts @@ -1,6 +1,6 @@ export * as balance from './balance' export * as price from './price' -export * as stats from './stats' +export * as bc_info from './bc_info' export const COIN_KEYS = ['btc', 'eth', 'etc', 'bch', 'ltc', 'dash', 'doge', 'btcv', 'zil'] as const export type CoinType = typeof COIN_KEYS[number] diff --git a/cryptoid/src/adapter.ts b/cryptoid/src/adapter.ts index 4fbd633342..41b01ddbba 100644 --- a/cryptoid/src/adapter.ts +++ b/cryptoid/src/adapter.ts @@ -1,7 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { util } from '@chainlink/ea-bootstrap' const customParams = { blockchain: ['blockchain', 'coin'], @@ -23,7 +22,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT const blockchain = validator.validated.data.blockchain.toLowerCase() - const key = util.getRandomRequiredEnv('API_KEY') + const key = config.apiKey const apiFunctionName = endpointToApiFunctionName[endpoint] const params = { key, q: apiFunctionName } From 4ee780a3a41084b952129fc99f40ee20de3b550e Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 16:27:35 +0200 Subject: [PATCH 20/23] reverted yarn.lock to 'develop' version --- yarn.lock | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/yarn.lock b/yarn.lock index bf83f08c0f..01503faa59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -993,11 +993,6 @@ resolved "https://registry.yarnpkg.com/@types/bchaddrjs/-/bchaddrjs-0.4.0.tgz#f3d29ab56096c9d7105b5ccbc69969d11426e087" integrity sha512-Nvv3haWpXNWYfKasVoEp3VBgVsBTLTh45anhHUN8xVUPhn4ErU3FPS5AEcZtACWc5ICGUxbaiLTWOnwDkwYF1Q== -"@types/big.js@^6.0.0": - version "6.0.2" - resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.0.2.tgz#a86938c1bb4511e69d91f69823cacc3f48ff1fa3" - integrity sha512-7NdmOT3zjtghMofDwP1nAJCJWVjc/96V5msXRAZ4lPrvpGlajA95VQec7OXwA2wQaVmhjt+F5ko8pjvQU1tTFA== - "@types/bn.js@^4.11.3", "@types/bn.js@^4.11.4", "@types/bn.js@^4.11.5": version "4.11.6" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" @@ -1629,11 +1624,6 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -big.js@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-6.0.3.tgz#8b4d99ac7023668e0e465d3f78c23b8ac29ad381" - integrity sha512-n6yn1FyVL1EW2DBAr4jlU/kObhRzmr+NNRESl65VIOT8WBJj/Kezpx2zFdhJUqYI6qrtTW7moCStYL5VxeVdPA== - bigi@^1.1.0, bigi@^1.4.0: version "1.4.2" resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" @@ -5235,6 +5225,14 @@ node-addon-api@^2.0.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== +node-cron@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/node-cron/-/node-cron-2.0.3.tgz#b9649784d0d6c00758410eef22fa54a10e3f602d" + integrity sha512-eJI+QitXlwcgiZwNNSRbqsjeZMp5shyajMR81RZCqeW0ZDEj4zU9tpd4nTh/1JsBiKbF8d08FCewiipDmVIYjg== + dependencies: + opencollective-postinstall "^2.0.0" + tz-offset "0.0.1" + node-environment-flags@1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" @@ -5416,6 +5414,11 @@ onetime@^5.1.0: dependencies: mimic-fn "^2.1.0" +opencollective-postinstall@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259" + integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q== + "openzeppelin-solidity-2.3.0@npm:openzeppelin-solidity@2.3.0": version "2.3.0" resolved "https://registry.yarnpkg.com/openzeppelin-solidity/-/openzeppelin-solidity-2.3.0.tgz#1ab7b4cc3782a5472ed61eb740c56a8bfdd74119" @@ -7023,6 +7026,11 @@ typescript@^3.9.7: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== +tz-offset@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/tz-offset/-/tz-offset-0.0.1.tgz#fef920257024d3583ed9072a767721a18bdb8a76" + integrity sha512-kMBmblijHJXyOpKzgDhKx9INYU4u4E1RPMB0HqmKSgWG8vEcf3exEfLh4FFfzd3xdQOw9EuIy/cP0akY6rHopQ== + ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" From fbe88b29af378fcdd7b3cca033c26d4fa25f0031 Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 17:28:21 +0200 Subject: [PATCH 21/23] review changes + updated README --- blockchair/src/endpoint/stats.ts | 2 +- composite/bitcoin-json-rpc/src/adapter.ts | 2 +- cryptoapis/README.md | 94 ++++++++++++++++++----- cryptoapis/src/endpoint/bc_info.ts | 2 +- cryptoapis/test/stats.test.ts | 2 +- cryptoid/src/adapter.ts | 2 +- 6 files changed, 81 insertions(+), 23 deletions(-) diff --git a/blockchair/src/endpoint/stats.ts b/blockchair/src/endpoint/stats.ts index 84f0b04a20..1441ea68ef 100644 --- a/blockchair/src/endpoint/stats.ts +++ b/blockchair/src/endpoint/stats.ts @@ -19,7 +19,7 @@ export const execute: ExecuteWithConfig = async (input, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + endpoint = convertEndpoint[endpoint] || endpoint const blockchain = Requester.toVendorName( validator.validated.data.blockchain.toLowerCase(), diff --git a/composite/bitcoin-json-rpc/src/adapter.ts b/composite/bitcoin-json-rpc/src/adapter.ts index 1be94f5768..e3caf0fd35 100644 --- a/composite/bitcoin-json-rpc/src/adapter.ts +++ b/composite/bitcoin-json-rpc/src/adapter.ts @@ -24,7 +24,7 @@ const execute: ExecuteWithConfig = async (request: AdapterRequest) => { data: { ...request.data, method: 'getblockchaininfo' }, }) - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + endpoint = convertEndpoint[endpoint] || endpoint const result = Requester.validateResultNumber(response.data, ['result', endpoint]) return Requester.success(jobRunID, { data: { ...response.data.result, result }, diff --git a/cryptoapis/README.md b/cryptoapis/README.md index 187e9986e8..0350191094 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -9,41 +9,95 @@ The adapter takes the following environment variables: ## Input Params -- `endpoint`: The endpoint to use. Default: "price" +- `endpoint`: The requested data point. One of (`price`|`difficulty`|`height`|`balance`). Defaults: `price`. -### Price endpoint +### Price -- `base`, `from`, or `coin`: The symbol or ID of the coin to query -- `quote`, `to`, or `market`: The symbol or ID of the market to convert to +- `base`, `from`, or `coin`: The symbol or ID of the coin to query (required). +- `quote`, `to`, or `market`: The symbol or ID of the market to convert to (required). -### Blockchain stats +### Output + +```json +{ + "jobRunID": "1", + "data": { + "payload": { + "weightedAveragePrice": 36670.69405468086, + "amount": 135.37338203000004, + "timestamp": 1610724372, + "datetime": "2021-01-15T15:26:12+0000", + "baseAsset": "BTC", + "quoteAsset": "USD" + }, + "result": 36670.69405468086 + }, + "result": 36670.69405468086, + "statusCode": 200 +} +``` -- `blockchain` or `coin`: The blockchain to get stats from -- `network`: The network of the blockchain to get stats from. Default: "mainnet" -- `endpoint`: The parameter to query for. Default: "difficulty" +### Difficulty -## Output +- `blockchain` or `coin`: The blockchain name (required). +- `network`: The blockchain network name. Default: `mainnet` + +### Output ```json { "jobRunID": "1", "data": { "payload": { - "weightedAveragePrice": 188.02563659478218, - "amount": 2848.4069787899994, - "timestamp": 1587650913, - "datetime": "2020-04-23T14:08:33+0000", - "baseAsset": "ETH", - "quoteAsset": "USD" + "difficulty": 20607418304385.63, + "headers": 666185, + "chain": "main", + "chainWork": "000000000000000000000000000000000000000018255ab714d1a15ffccd987e", + "mediantime": 1610721116, + "blocks": 666185, + "bestBlockHash": "0000000000000000000cc82b0a9a6e290cd13721a1abf88fdebb37fdc927308e", + "currency": "BTC", + "transactions": 606560353, + "verificationProgress": 0.9999930065052965 }, - "result": 188.02563659478218 + "result": 20607418304385.63 }, - "result": 188.02563659478218, + "result": 20607418304385.63, "statusCode": 200 } ``` -### Balance endpoint +### Height + +- `blockchain` or `coin`: The blockchain name (required). +- `network`: The blockchain network name. Default: `mainnet` + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "payload": { + "difficulty": 20607418304385.63, + "headers": 666185, + "chain": "main", + "chainWork": "000000000000000000000000000000000000000018255ab714d1a15ffccd987e", + "mediantime": 1610721116, + "blocks": 666185, + "bestBlockHash": "0000000000000000000cc82b0a9a6e290cd13721a1abf88fdebb37fdc927308e", + "currency": "BTC", + "transactions": 606560353, + "verificationProgress": 0.9999935897991173 + }, + "result": 666185 + }, + "result": 666185, + "statusCode": 200 +} +``` + +### Balance https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/btc/index#btc-address-info-endpoint @@ -115,3 +169,7 @@ https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/btc/index#btc- "statusCode": 200 } ``` + +``` + +``` diff --git a/cryptoapis/src/endpoint/bc_info.ts b/cryptoapis/src/endpoint/bc_info.ts index 6d7df60834..985de99d24 100644 --- a/cryptoapis/src/endpoint/bc_info.ts +++ b/cryptoapis/src/endpoint/bc_info.ts @@ -21,7 +21,7 @@ export const execute = async (config: Config, request: AdapterRequest) => { const blockchain = validator.validated.data.blockchain const network = validator.validated.data.network || 'mainnet' let endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - if (endpoint in convertEndpoint) endpoint = convertEndpoint[endpoint] + endpoint = convertEndpoint[endpoint] || endpoint const url = `/v1/bc/${blockchain.toLowerCase()}/${network.toLowerCase()}/info` const reqConfig = { ...config.api, url } diff --git a/cryptoapis/test/stats.test.ts b/cryptoapis/test/stats.test.ts index fdf627f8b6..f7eb10b9dc 100644 --- a/cryptoapis/test/stats.test.ts +++ b/cryptoapis/test/stats.test.ts @@ -4,7 +4,7 @@ import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' import { AdapterRequest } from '@chainlink/types' import { makeExecute } from '../src/adapter' -describe('stats endpoint', () => { +describe('bc_info endpoint', () => { const jobID = '1' const execute = makeExecute() diff --git a/cryptoid/src/adapter.ts b/cryptoid/src/adapter.ts index 41b01ddbba..0df15ba266 100644 --- a/cryptoid/src/adapter.ts +++ b/cryptoid/src/adapter.ts @@ -29,7 +29,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const reqConfig = { ...config.api, params, - baseURL: `https://${blockchain}.cryptoid.info/${blockchain}/api.dws`, + baseURL: config.api.baseURL || `https://${blockchain}.cryptoid.info/${blockchain}/api.dws`, } const response = await Requester.request(reqConfig) const result = response.data From eaf38310809b7b0b073532d961983528700cc54b Mon Sep 17 00:00:00 2001 From: Evangelos Barakos Date: Fri, 15 Jan 2021 17:35:10 +0200 Subject: [PATCH 22/23] updated cryptoid README --- cryptoid/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cryptoid/README.md b/cryptoid/README.md index 354033f236..018ea45639 100644 --- a/cryptoid/README.md +++ b/cryptoid/README.md @@ -2,8 +2,8 @@ ## Input Params -- `blockchain` or `coin`: The blockchain to get stats from -- `endpoint`: The parameter to query for. Default: "difficulty" +- `blockchain` or `coin`: The blockchain name (required). +- `endpoint`: The requested data point. One of (`difficulty`|`height`). Defaults: `difficulty`. ## Output From f2046a3841a9791591edcf80cec9d2e8ac23f405 Mon Sep 17 00:00:00 2001 From: Kristijan Rebernisak Date: Fri, 15 Jan 2021 16:53:59 +0100 Subject: [PATCH 23/23] Update cryptoapis/README.md --- cryptoapis/README.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cryptoapis/README.md b/cryptoapis/README.md index 0350191094..8b100f2262 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -169,7 +169,3 @@ https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/btc/index#btc- "statusCode": 200 } ``` - -``` - -```