From 418af1ba92fe886acecdaad1ce4fd8f68e77b998 Mon Sep 17 00:00:00 2001 From: Justin Kaseman Date: Tue, 13 Apr 2021 18:00:29 -0400 Subject: [PATCH] [Master] Release/v0.2.0 (#439) * coinmarketcap ts refactor * types & lint & package name * CHANGELOG * Requested changes: uses generic execute types, base url from config, splits tests * add conflux adapter * Remove adapter.js from merge * Fix: increase test timeout, move marketcap unknown market to error calls, correct typo in baseURL * getDefaultConfig takes new parameter for required API key * Brings back dummy API_KEYs for tests * Uses verbose env var to determine response data * update changelog: add conflux adapter * EA migration - batch #1 (#226) * 1forge to TS * Add Config type for recent change to generic types * Utilize ExecuteWithConfig in 1forge * Adds example README template & remove 'server' script * Alphavantage adapter to TS * Remove alphavantage-sdr folder * Amberdata-gasprice to TS * Anyblock-gasprice to TS * Binance-DEX to TS * Add note to binance-dex's differing use of API_ENDPOINT env var * Brave New Coin to TS * Bring Brave New Coin VWAP into Brave New Coin EA * Coinapi to TS * Coinbase to TS * Coingecko to TS * Move BNC helpers into BNC adapter * Increase alphavantage test timeout * Moves alphavantage APIkey param into endpoint - issue open for improvement * Clean up leftover bravenewcoin-vwap traces * Remove 1forge's conflicting and not useful endpoint param * Requested changes: 1forge balance -> price, alphavantage example output * Re-add server script & spread response data into endpoint responses * Uses verbose env var to determine response data * Gets API_KEY from makeConfig * Add test dummy API_KEY * Re-add server script to example * batch adapter TS refactor (#234) * batch adapter TS refactor * satisfy linter * Uses config in generics, removes non-TS server script * EA endpoints return their own response & use ExecuteWithConfig type * Capitalize name property * Use Config for baseURL and API_KEYS, re-add server script, use verbose env var for data response Co-authored-by: Justin Kaseman * README improvements, fix typo with PRICE_ADAPTER * networkId type fix * feed tx gas info * Read dxFeed config correctly with prefix * Include config change in secondary adapter * Use "Quote" for WTI * Fix case-sensitive endpoint params * batch requests on coingecko * DPI readme updated * removed console * Update Synthetix for new sDEFI pairs * docs: additiona to 1forge, amberdata-gasprice, anyblock-gasprice, & binance-dex * Add API_KEY docs & docs from QA for coinapi, coinbase, cmc, coinpaprika, cryptocompare, currencylayer, and poa-gasprice * Finish TS migrated adapter QA * Add dummy API keys for tests * Use Jonas' fix to non-lowercase endpoint param * Add dummy API key to openexchangerates test * Add dummy API key to polygon test * Requested changes: consistent endpoint title capitalization, tweak API_KEY description, add sample input to example EA * Fix: balance test helper path * Remove WrappedResponse * Merge develop * CHANGELOG * Requested Change: remove from CHANGELOG * Change: Only one Middleware type * Change: revert change to withCache to remove options param * Remove util.wrapExecute from all EAs * Revert result being optional * Makes Middleware a generic type to allow withCache options & fix test with result in mock data response * wrap response * returning result on callback * removed unused imports * Update KCS slug on CMC synth-index * Update KCS slug on CMC * synthx as composite using TA * removed old synth adapter and its build steps * base as default input param * TA default quote as env var * TA accepts method as param * validation on TA * avoid floating numbers on TA * removed TA reference from Readme * readme updated * negative balance checked easier * prefix preference on TA make config * updated adapter name * added cmc slugs * updated synthx version * fix synthx version * tiingo adapter with tests * misc tiingo fixes * updated changelog * updates * fix import name error * Remove synthetix dependency from global package * Update synthetix package * Prevent scientific notation of balances * removed google finance * filter out auth from logging * Publish Docker images to SDLC Public ECR instead of Prod Public ECR * remove old js code * add ts conflux adaptor code * remove lock file * comment example test * added preset ticker on coinpaprika and coingecko * update config helper * Include the "result" key on cached responses * Include jobRunID in cached response * added ethwrite JS project and its dependencies * converted to TS * working with the new structure * added hardhat and relevant test setup * default config + naming convention fix * changed README, added defaults and test * review changes; need to check tests * asserting true values written to contract in test * small change in config * made dataType optional, altered tests, added helper file * removed helper files * installed fresh dependencies * README changes and required env vars * moved private key to hardhat helpers * yarn install * json for hardhat exported var * cleaned comments * update new ea-bootstrap * fix lint error * add unit test * fix lint * update tests * Publish adapters to prod when merging to develop * basic setup * geoDB adapter * updated changelog * Removed other parts of config that include sensitive values. Removed api key warning * Add "agoric" adapter (#114) * feat: add Agoric adapter * fix: use agoric_oracle_query_id * fix: make more explicit * test: remove agoric/test * fix: another attempt to plumb through the 'Task Run Data' * fix: use the promise-based API * fix: make request_id numeric * fix: cast payment from number to string * fix: properly export the Agoric async adapter and string queryId * refactor: separate concerns and add tests * fix: more robust adapter; send errors to the oracleServer * feat!: surface errors from the oracle backend * fix: match with actual POST reply from the ag-solo * fix: use AdapterErrors to surface errors to the node operator * test: add integration test * fix: use Requester instead of axios * chore: rename package to @chainlink/agoric * ci: add "agoric" to adapters.json * refactor: clarify implementation according to review comments * refactor: use the adapter-test-helpers * ci: fix the Agoric build process * fix: address review comments * refactor: standardize code based on example adapter * fix: import makeConfig * fix: correct the default agoric adapter parameters * chore: remove dependency on bn.js * Kaiko special case for DIGG/BTC (#336) * Kaiko special case for DIGG/BTC * Increase timespan for Kaiko requests * Add changelog item * updated geodb url * Update node version in GCP readme * Add Amberdata case for DIGG/BTC (#345) * TheRundown adapter * updated changelog * updated therundown readme * TS EAs #4 batch 0 of 3 (#332) * Add couple more best practices into example adapter * TrueUSD adapter to TS * Reverts change to TrueUSD param name * Rename path param --> field * Move TE stream adapter to monorepo (#346) * Move TE stream adapter to monorepo * Add changelog entry * Make config optional * Add tests * Added LINA preset in coingecko * CMC should prefer IDs over slugs (#343) * CMC should prefer IDs over slugs * Add changelog entry * Add conversion from AMP to AMP2 for Nomics adapter * parsing c(close price) according to v3 * Add tradermade support to outlier-detection (#357) * Add tradermade support to outlier-detection * Fix outdated tests * Add changelog entry * Improve Redis connection cleanup * AlphaChain adapter to TS * Add postinstall script * More best practices in the example EA * Bitex adapter to TS * Bitso adapter to TS * Clean up bravenewcoin-vwap fragment * Coinlore adapter to TS * Remove postinstall script until entire build process can be refactored * TrueUSD adapter to TS * Reverts change to TrueUSD param name * COVID-tracker adapter to TS * Cryptomkt adapter to TS * Remove google-finance adapter fragment * Lition adapter to TS * Add public ecr registry to docker login * Skip coinlore integration tests * Add baseurl option (#367) * Explicitly specify aws region (#370) * Updated Asset Allocation Interface (#358) * asset allocation updated * updated version * decimals to number * Fix support for WING on Nomics (#371) * Add basic prom metrics (#365) * Messari adapter to TS * Orchid Bandwidth adapter to TS * SatoshiTange adapter to TS * TradingEconomics adapter to TS * Extra to TradingEconomics README * Fix to tradingeconomics .eslintrc.js * Fix: remove no longer relevant test * Cryptoapis tests and README (#248) * Update README about metadata (#379) * Add new ticker conversions * Overrides Input parameter (#384) * override format validator. coingecko impl * price adapter use overrides * overrides validator test * override inside validator * refactor * more adapters added * more adapters added * readmes updated * lint fix * preset symbols used re * tests updated * merge with lodash * fix merge * Bump elliptic from 3.0.3 to 6.5.4 (#391) Bumps [elliptic](https://github.com/indutny/elliptic) from 3.0.3 to 6.5.4. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v3.0.3...v6.5.4) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Remove GOOGL override for dxfeed-secondary (#392) * fix-format-readme-link (#393) * Set correct response type on metrics * Add FB support for dxfeed and dxfeed-secondary (#420) * bitcoin endpoints (#417) * bitcoin endpoints * Fix blockstream and blockchain.com adapters * Fix height result output * Fix blockstream README * Update blockstream/package.json * Update blockstream/README.md * Condense endpoints to blocks & use field param * Blockstream doesn't require an API KEY * Update Blockstream README * Add blockstream to github workflow Co-authored-by: Thomas Hodges Co-authored-by: Justin Kaseman * Update Genesis Volatility README (#422) * add support for RAI to kaiko, nomics (#425) * add rai, weth (#424) * add rai, weth Added handling of RAI/WETH pair using contract addresses * Fix formatting Co-authored-by: Jonas Hals * update API key url (#428) * additional fixes for RAI support on kaiko adapter (#431) * added includes:['weth'] support to kaiko adapter * Apply suggestions from code review Co-authored-by: Jonas Hals * v0.0.4 * Preparing v0.2.0 release: version patch bumps * Tag changelog * Bump dependency versions * Additional CHANGELOG items Co-authored-by: ryan Co-authored-by: PanaW Co-authored-by: Kristijan Rebernisak Co-authored-by: Jonas Hals Co-authored-by: Jonas Hals Co-authored-by: RodrigoAD <15104916+RodrigoAD@users.noreply.github.com> Co-authored-by: Thomas Hodges Co-authored-by: christianagnew Co-authored-by: Evangelos Barakos Co-authored-by: Edward Medvedev Co-authored-by: W Co-authored-by: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com> Co-authored-by: Michael FIG Co-authored-by: Peter van Mourik Co-authored-by: Evangelos Barakos Co-authored-by: Connor Stein Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Brendon Van Essen <42727620+nzleet@users.noreply.github.com> Co-authored-by: aalu1418 <50029043+aalu1418@users.noreply.github.com> --- .github/strategy/adapters.json | 33 +- .github/workflows/release-staging.yml | 20 +- .github/workflows/release.yml | 20 +- .gitignore | 1 + 1forge/.eslintrc.js | 4 +- 1forge/README.md | 54 +- 1forge/adapter.js | 43 - 1forge/index.js | 4 - 1forge/package.json | 31 +- 1forge/src/adapter.ts | 35 + 1forge/src/config.ts | 19 + 1forge/src/endpoint/index.ts | 1 + 1forge/src/endpoint/price.ts | 44 + 1forge/src/index.ts | 5 + .../test/adapter.test.ts | 46 +- 1forge/tsconfig.json | 10 + CHANGELOG.md | 31 + Dockerfile-SynthIndex | 17 - Makefile | 14 - README.md | 17 +- agoric/.eslintrc.js | 3 + agoric/README.md | 39 + agoric/package.json | 44 + agoric/src/adapter.ts | 117 + agoric/src/config.ts | 17 + agoric/src/index.ts | 7 + agoric/test/adapter.test.ts | 157 + agoric/tsconfig.json | 10 + alphachain/.eslintrc.js | 3 + alphachain/README.md | 43 +- alphachain/adapter.js | 50 - alphachain/index.js | 4 - alphachain/package.json | 42 +- alphachain/src/adapter.ts | 35 + alphachain/src/config.ts | 11 + alphachain/src/endpoint/data-query.ts | 52 + alphachain/src/endpoint/index.ts | 1 + alphachain/src/index.ts | 7 + .../{adapter_test.js => data-query.test.ts} | 46 +- alphachain/tsconfig.json | 10 + alphavantage-sdr/.eslintrc.js | 1 - alphavantage-sdr/test/adapter_test.js | 85 - alphavantage/.eslintrc.js | 4 +- alphavantage/README.md | 66 +- alphavantage/adapter.js | 53 - alphavantage/index.js | 4 - alphavantage/package.json | 44 +- alphavantage/src/adapter.ts | 35 + alphavantage/src/config.ts | 19 + alphavantage/src/endpoint/index.ts | 1 + alphavantage/src/endpoint/price.ts | 52 + alphavantage/src/index.ts | 5 + alphavantage/test/adapter.test.ts | 91 + alphavantage/tsconfig.json | 10 + amberdata-gasprice/.eslintrc.js | 4 +- amberdata-gasprice/README.md | 24 +- amberdata-gasprice/adapter.js | 42 - amberdata-gasprice/index.js | 4 - amberdata-gasprice/package.json | 41 +- amberdata-gasprice/src/adapter.ts | 13 + amberdata-gasprice/src/config.ts | 11 + amberdata-gasprice/src/endpoint/gasprice.ts | 41 + amberdata-gasprice/src/endpoint/index.ts | 1 + amberdata-gasprice/src/index.ts | 7 + .../{adapter_test.js => gasprice.test.ts} | 48 +- amberdata-gasprice/tsconfig.json | 10 + amberdata/README.md | 19 +- amberdata/package.json | 2 +- amberdata/src/config.ts | 5 +- amberdata/src/endpoint/price.ts | 27 +- amberdata/src/index.ts | 8 +- amberdata/test/balance.test.ts | 2 + amberdata/test/price.test.ts | 1 + anyblock-gasprice/.eslintrc.js | 4 +- anyblock-gasprice/README.md | 16 +- anyblock-gasprice/adapter.js | 31 - anyblock-gasprice/index.js | 4 - anyblock-gasprice/package.json | 41 +- anyblock-gasprice/src/adapter.ts | 13 + anyblock-gasprice/src/config.ts | 10 + anyblock-gasprice/src/endpoint/gasprice.ts | 39 + anyblock-gasprice/src/endpoint/index.ts | 1 + anyblock-gasprice/src/index.ts | 7 + .../{adapter_test.js => gasprice.test.ts} | 55 +- anyblock-gasprice/tsconfig.json | 10 + anyblock-uniswap-vwap/package.json | 2 +- anyblock-uniswap-vwap/src/index.ts | 4 +- binance-dex/.eslintrc.js | 4 +- binance-dex/README.md | 68 +- binance-dex/adapter.js | 57 - binance-dex/index.js | 4 - binance-dex/package.json | 41 +- binance-dex/src/adapter.ts | 13 + binance-dex/src/config.ts | 14 + binance-dex/src/endpoint/index.ts | 1 + binance-dex/src/endpoint/price.ts | 59 + binance-dex/src/index.ts | 7 + .../test/{adapter_test.js => price.test.ts} | 47 +- binance-dex/tsconfig.json | 10 + bitex/.eslintrc.js | 3 + bitex/README.md | 74 +- bitex/adapter.js | 35 - bitex/index.js | 4 - bitex/package.json | 42 +- bitex/src/adapter.ts | 35 + bitex/src/config.ts | 11 + bitex/src/endpoint/index.ts | 1 + bitex/src/endpoint/tickers.ts | 35 + bitex/src/index.ts | 7 + .../test/{adapter_test.js => tickers.test.ts} | 45 +- bitex/tsconfig.json | 10 + bitso/.eslintrc.js | 3 + bitso/README.md | 70 +- bitso/adapter.js | 33 - bitso/index.js | 4 - bitso/package.json | 42 +- bitso/src/adapter.ts | 35 + bitso/src/config.ts | 11 + bitso/src/endpoint/index.ts | 1 + bitso/src/endpoint/ticker.ts | 39 + bitso/src/index.ts | 7 + .../test/{adapter_test.js => ticker.test.ts} | 45 +- bitso/tsconfig.json | 10 + blockchain.com/README.md | 56 +- blockchain.com/package.json | 2 +- blockchain.com/src/adapter.ts | 8 +- blockchain.com/src/config.ts | 1 + blockchain.com/src/endpoint/difficulty.ts | 27 + blockchain.com/src/endpoint/height.ts | 27 + blockchain.com/src/endpoint/index.ts | 2 + blockchain.com/src/index.ts | 4 +- blockchain.com/test/adapter.test.ts | 84 + blockchair/package.json | 2 +- blockchair/src/index.ts | 4 +- blockcypher/package.json | 2 +- blockcypher/src/index.ts | 4 +- blockstream/.eslintrc.js | 3 + blockstream/README.md | 57 + blockstream/package.json | 46 + blockstream/src/adapter.ts | 40 + blockstream/src/config.ts | 14 + blockstream/src/endpoint/blocks.ts | 32 + blockstream/src/endpoint/index.ts | 1 + blockstream/src/index.ts | 7 + blockstream/test/adapter.test.ts | 88 + blockstream/tsconfig.json | 10 + bootstrap/README.md | 9 + bootstrap/package.json | 3 +- bootstrap/src/index.ts | 93 +- bootstrap/src/lib/cache/index.ts | 28 +- bootstrap/src/lib/cache/redis.ts | 16 +- bootstrap/src/lib/metrics/index.ts | 58 + bootstrap/src/lib/server.ts | 27 +- bootstrap/src/lib/util.ts | 37 +- bootstrap/test/cache.test.ts | 9 +- bravenewcoin-vwap/.eslintrc.js | 1 - bravenewcoin-vwap/README.md | 45 - bravenewcoin-vwap/adapter.js | 55 - bravenewcoin-vwap/index.js | 4 - bravenewcoin-vwap/package.json | 15 - bravenewcoin-vwap/test/adapter_test.js | 62 - bravenewcoin/.eslintrc.js | 4 +- bravenewcoin/README.md | 69 +- bravenewcoin/adapter.js | 29 - bravenewcoin/index.js | 4 - bravenewcoin/package.json | 41 +- bravenewcoin/src/adapter.ts | 38 + bravenewcoin/src/config.ts | 6 + bravenewcoin/src/endpoint/index.ts | 2 + bravenewcoin/src/endpoint/price.ts | 27 + bravenewcoin/src/endpoint/vwap.ts | 50 + .../helpers.js => bravenewcoin/src/helpers.ts | 48 +- bravenewcoin/src/index.ts | 7 + .../test/{adapter_test.js => price.test.ts} | 47 +- bravenewcoin/test/vwap.test.ts | 67 + bravenewcoin/tsconfig.json | 10 + btc.com/package.json | 2 +- btc.com/src/index.ts | 4 +- cfbenchmarks/package.json | 2 +- cfbenchmarks/src/index.ts | 4 +- coinapi/.eslintrc.js | 4 +- coinapi/README.md | 49 +- coinapi/adapter.js | 31 - coinapi/index.js | 4 - coinapi/package.json | 35 +- coinapi/src/adapter.ts | 35 + coinapi/src/config.ts | 19 + coinapi/src/endpoint/index.ts | 1 + coinapi/src/endpoint/price.ts | 36 + coinapi/src/index.ts | 5 + .../test/{adapter_test.js => price.test.ts} | 60 +- coinapi/tsconfig.json | 10 + coinbase/.editorconfig | 6 - coinbase/.eslintrc.js | 4 +- coinbase/README.md | 39 +- coinbase/adapter.js | 37 - coinbase/index.js | 4 - coinbase/package.json | 35 +- coinbase/src/adapter.ts | 35 + coinbase/src/config.ts | 11 + coinbase/src/endpoint/index.ts | 1 + coinbase/src/endpoint/price.ts | 40 + coinbase/src/index.ts | 7 + coinbase/test/adapter_test.js | 81 - .../test/price.test.ts | 46 +- coinbase/tsconfig.json | 10 + coincodex/package.json | 2 +- coincodex/src/index.ts | 4 +- coingecko/.editorconfig | 6 - coingecko/.eslintrc.js | 4 +- coingecko/README.md | 103 +- coingecko/adapter.js | 132 - coingecko/index.js | 4 - coingecko/package.json | 35 +- coingecko/src/adapter.ts | 42 + coingecko/src/config.ts | 13 + coingecko/src/endpoint/global.ts | 39 + coingecko/src/endpoint/index.ts | 2 + coingecko/src/endpoint/price.ts | 78 + coingecko/src/index.ts | 5 + coingecko/test/adapter_test.js | 134 - .../test/dominance.test.ts | 60 +- coingecko/test/global.test.ts | 80 + coingecko/test/price.test.ts | 98 + coingecko/tsconfig.json | 10 + coinlore/.eslintrc.js | 3 + coinlore/README.md | 37 +- coinlore/adapter.js | 53 - coinlore/index.js | 4 - coinlore/package.json | 42 +- coinlore/src/adapter.ts | 40 + coinlore/src/config.ts | 11 + coinlore/src/endpoint/global.ts | 34 + coinlore/src/endpoint/index.ts | 1 + coinlore/src/index.ts | 7 + coinlore/test/adapter_test.js | 79 - coinlore/test/global.test.ts | 56 + coinlore/tsconfig.json | 10 + coinmarketcap/.eslintrc.js | 4 +- coinmarketcap/README.md | 101 +- coinmarketcap/adapter.js | 184 - coinmarketcap/index.js | 4 - coinmarketcap/package.json | 43 +- coinmarketcap/src/adapter.ts | 41 + coinmarketcap/src/config.ts | 16 + coinmarketcap/src/endpoint/dominance.ts | 33 + coinmarketcap/src/endpoint/index.ts | 3 + coinmarketcap/src/endpoint/marketcap.ts | 38 + coinmarketcap/src/endpoint/price.ts | 104 + coinmarketcap/src/index.ts | 5 + coinmarketcap/test/dominance.test.ts | 73 + coinmarketcap/test/marketcap.test.ts | 69 + .../test/{adapter_test.js => price.test.ts} | 62 +- coinmarketcap/tsconfig.json | 10 + coinpaprika/.eslintrc.js | 4 +- coinpaprika/README.md | 65 +- coinpaprika/adapter.js | 133 - coinpaprika/index.js | 4 - coinpaprika/package.json | 44 +- coinpaprika/src/adapter.ts | 41 + coinpaprika/src/config.ts | 16 + coinpaprika/src/endpoint/dominance.ts | 36 + coinpaprika/src/endpoint/index.ts | 3 + coinpaprika/src/endpoint/marketcap.ts | 30 + coinpaprika/src/endpoint/price.ts | 72 + coinpaprika/src/index.ts | 5 + .../test/{adapter_test.js => adapter.test.ts} | 53 +- coinpaprika/tsconfig.json | 10 + coinranking/package.json | 2 +- coinranking/src/index.ts | 4 +- composite/apy-finance/package.json | 4 +- .../apy-finance/src/abi/IAssetAllocation.json | 37 +- composite/apy-finance/src/index.ts | 4 +- composite/apy-finance/src/registry/index.ts | 23 +- composite/bitcoin-json-rpc/package.json | 4 +- composite/bitcoin-json-rpc/src/index.ts | 4 +- .../crypto-volatility-index/package.json | 8 +- .../crypto-volatility-index/src/index.ts | 4 +- composite/defi-pulse/README.md | 13 +- composite/defi-pulse/package.json | 4 +- composite/defi-pulse/src/index.ts | 4 +- composite/dns-record-check/package.json | 4 +- composite/dns-record-check/src/index.ts | 4 +- composite/market-closure/README.md | 90 +- composite/market-closure/package.json | 8 +- composite/market-closure/src/index.ts | 4 +- composite/market-closure/src/price.ts | 2 +- composite/outlier-detection/README.md | 8 +- composite/outlier-detection/package.json | 15 +- composite/outlier-detection/src/check.ts | 4 + composite/outlier-detection/src/index.ts | 4 +- composite/outlier-detection/src/source.ts | 4 + composite/proof-of-reserves/package.json | 22 +- composite/proof-of-reserves/src/index.ts | 4 +- composite/synth-index/.eslintrc.js | 3 + composite/synth-index/README.md | 139 + composite/synth-index/package.json | 48 + composite/synth-index/src/adapter.ts | 67 + composite/synth-index/src/config.ts | 14 + composite/synth-index/src/index.ts | 7 + composite/synth-index/test/adapter.test.ts | 108 + composite/synth-index/tsconfig.json | 22 + composite/token-allocation/package.json | 2 +- composite/token-allocation/src/adapter.ts | 61 +- composite/token-allocation/src/config.ts | 16 +- .../src/data-providers/coingecko.ts | 67 +- .../src/data-providers/coinmarketcap.ts | 51 +- composite/token-allocation/src/index.ts | 4 +- composite/token-allocation/src/types.ts | 11 + .../token-allocation/test/adapter.test.ts | 3 +- conflux/.eslintrc.js | 3 + conflux/README.md | 20 + conflux/package.json | 48 + conflux/src/adapter.ts | 38 + conflux/src/config.ts | 19 + conflux/src/endpoint/conflux.ts | 84 + conflux/src/endpoint/index.ts | 1 + conflux/src/index.ts | 7 + conflux/test/adapter.test.ts | 71 + conflux/tsconfig.json | 10 + covid-tracker/.eslintrc.js | 3 + covid-tracker/README.md | 29 +- covid-tracker/adapter.js | 78 - covid-tracker/index.js | 4 - covid-tracker/package.json | 42 +- covid-tracker/src/adapter.ts | 35 + covid-tracker/src/config.ts | 11 + covid-tracker/src/endpoint/index.ts | 1 + covid-tracker/src/endpoint/us.ts | 70 + covid-tracker/src/index.ts | 7 + covid-tracker/test/adapter.test.ts | 90 + covid-tracker/test/adapter_test.js | 89 - covid-tracker/tsconfig.json | 10 + cryptoapis/README.md | 76 +- cryptoapis/package.json | 4 +- cryptoapis/src/endpoint/price.ts | 1 - cryptoapis/src/index.ts | 4 +- cryptoapis/test/bc_info.test.ts | 67 + cryptocompare/.eslintrc.js | 4 +- cryptocompare/README.md | 41 +- cryptocompare/adapter.js | 49 - cryptocompare/index.js | 4 - cryptocompare/package.json | 43 +- cryptocompare/src/adapter.ts | 35 + cryptocompare/src/config.ts | 18 + cryptocompare/src/endpoint/index.ts | 1 + cryptocompare/src/endpoint/price.ts | 43 + cryptocompare/src/index.ts | 5 + .../test/{adapter_test.js => adapter.test.ts} | 46 +- cryptocompare/tsconfig.json | 10 + cryptoid/package.json | 2 +- cryptoid/src/index.ts | 4 +- cryptomkt/.eslintrc.js | 3 + cryptomkt/README.md | 69 +- cryptomkt/adapter.js | 35 - cryptomkt/index.js | 4 - cryptomkt/package.json | 42 +- cryptomkt/src/adapter.ts | 35 + cryptomkt/src/config.ts | 11 + cryptomkt/src/endpoint/index.ts | 1 + cryptomkt/src/endpoint/ticker.ts | 39 + cryptomkt/src/index.ts | 7 + .../test/{adapter_test.js => ticker.test.ts} | 45 +- cryptomkt/tsconfig.json | 10 + currencylayer/.eslintrc.js | 4 +- currencylayer/README.md | 66 +- currencylayer/adapter.js | 43 - currencylayer/index.js | 4 - currencylayer/package.json | 43 +- currencylayer/src/adapter.ts | 36 + currencylayer/src/config.ts | 19 + currencylayer/src/endpoint/convert.ts | 46 + currencylayer/src/endpoint/index.ts | 1 + currencylayer/src/index.ts | 5 + .../test/adapter.test.ts | 46 +- currencylayer/tsconfig.json | 10 + deribit/package.json | 2 +- deribit/src/index.ts | 4 +- dns-query/package.json | 2 +- dns-query/src/index.ts | 4 +- dxfeed-secondary/README.md | 38 +- dxfeed-secondary/package.json | 4 +- dxfeed-secondary/src/config.ts | 6 +- dxfeed-secondary/src/endpoint/price.ts | 14 +- dxfeed-secondary/src/index.ts | 8 +- dxfeed/README.md | 34 +- dxfeed/package.json | 2 +- dxfeed/src/config.ts | 19 +- dxfeed/src/endpoint/price.ts | 42 +- dxfeed/src/index.ts | 8 +- dydx-stark/package.json | 2 +- dydx-stark/src/index.ts | 4 +- ea-factories/package.json | 2 +- eodhistoricaldata/.eslintrc.js | 4 +- eodhistoricaldata/README.md | 61 +- eodhistoricaldata/adapter.js | 46 - eodhistoricaldata/index.js | 4 - eodhistoricaldata/package.json | 43 +- eodhistoricaldata/src/adapter.ts | 35 + eodhistoricaldata/src/config.ts | 15 + eodhistoricaldata/src/endpoint/index.ts | 1 + eodhistoricaldata/src/endpoint/price.ts | 48 + eodhistoricaldata/src/index.ts | 7 + eodhistoricaldata/test/adapter.test.ts | 67 + eodhistoricaldata/test/adapter_test.js | 61 - eodhistoricaldata/tsconfig.json | 10 + etherchain/.eslintrc.js | 4 +- etherchain/README.md | 23 +- etherchain/adapter.js | 30 - etherchain/index.js | 4 - etherchain/package.json | 43 +- etherchain/src/adapter.ts | 35 + etherchain/src/config.ts | 11 + etherchain/src/endpoint/gasprice.ts | 33 + etherchain/src/endpoint/index.ts | 1 + etherchain/src/index.ts | 7 + .../test/{adapter_test.js => adapter.test.ts} | 52 +- etherchain/tsconfig.json | 10 + ethgasstation/.eslintrc.js | 4 +- ethgasstation/README.md | 32 +- ethgasstation/adapter.js | 38 - ethgasstation/index.js | 4 - ethgasstation/package.json | 43 +- ethgasstation/src/adapter.ts | 35 + ethgasstation/src/config.ts | 17 + ethgasstation/src/endpoint/gasprice.ts | 34 + ethgasstation/src/endpoint/index.ts | 1 + ethgasstation/src/index.ts | 7 + ethgasstation/test/adapter.test.ts | 88 + ethgasstation/test/adapter_test.js | 89 - ethgasstation/tsconfig.json | 10 + ethwrite/.eslintrc.js | 3 + ethwrite/README.md | 56 + ethwrite/package.json | 43 + ethwrite/src/adapter.ts | 35 + ethwrite/src/config.ts | 19 + ethwrite/src/endpoint/index.ts | 1 + ethwrite/src/endpoint/txsend.ts | 72 + ethwrite/src/index.ts | 7 + ethwrite/test/adapter.test.ts | 203 + ethwrite/test/helpers.ts | 37 + ethwrite/tsconfig.json | 10 + example/README.md | 57 +- example/package.json | 2 +- example/src/adapter.ts | 2 +- example/src/config.ts | 5 +- example/src/endpoint/example.ts | 16 +- example/src/index.ts | 8 +- .../test/{adapter.test.ts => example.test.ts} | 0 external-adapter/package.json | 3 +- external-adapter/src/config.ts | 10 +- .../src/overrides/presetSymbols.json | 56 + external-adapter/src/validator.ts | 82 +- external-adapter/test/validator.test.ts | 40 + external-adapter/tsconfig.json | 17 +- fcsapi/package.json | 6 +- fcsapi/src/adapter.ts | 4 +- fcsapi/src/index.ts | 4 +- finage/README.md | 1 + finage/package.json | 2 +- finage/src/adapter.ts | 13 +- finage/src/index.ts | 8 +- finnhub/package.json | 2 +- finnhub/src/index.ts | 4 +- fixer/.eslintrc.js | 4 +- fixer/README.md | 64 +- fixer/adapter.js | 43 - fixer/index.js | 4 - fixer/package.json | 43 +- fixer/src/adapter.ts | 36 + fixer/src/config.ts | 19 + fixer/src/endpoint/convert.ts | 46 + fixer/src/endpoint/index.ts | 1 + fixer/src/index.ts | 5 + .../test/{adapter_test.js => adapter.test.ts} | 46 +- fixer/tsconfig.json | 10 + fmpcloud/.eslintrc.js | 4 +- fmpcloud/README.md | 83 +- fmpcloud/adapter.js | 54 - fmpcloud/index.js | 4 - fmpcloud/package.json | 43 +- fmpcloud/src/adapter.ts | 36 + fmpcloud/src/config.ts | 17 + fmpcloud/src/endpoint/index.ts | 1 + fmpcloud/src/endpoint/quote.ts | 46 + fmpcloud/src/index.ts | 7 + fmpcloud/test/adapter.test.ts | 67 + fmpcloud/test/adapter_test.js | 61 - fmpcloud/tsconfig.json | 10 + genesis-volatility/README.md | 74 +- genesis-volatility/package.json | 2 +- genesis-volatility/src/index.ts | 4 +- geodb/.eslintrc.js | 3 + geodb/README.md | 51 + geodb/package.json | 46 + geodb/src/adapter.ts | 35 + geodb/src/config.ts | 14 + geodb/src/endpoint/index.ts | 1 + geodb/src/endpoint/matches.ts | 39 + geodb/src/index.ts | 7 + geodb/test/adapter.test.ts | 221 ++ geodb/tsconfig.json | 10 + google-finance/.eslintrc.js | 1 - google-finance/README.md | 46 - google-finance/adapter.js | 37 - google-finance/index.js | 4 - google-finance/package.json | 17 - google-finance/test/adapter_test.js | 98 - hardhat.config.js | 29 + harmony/package.json | 2 +- harmony/src/index.ts | 4 +- helpers/reference-data-reader/package.json | 2 +- iex-cloud/README.md | 1 + iex-cloud/package.json | 2 +- iex-cloud/src/config.ts | 2 + iex-cloud/src/endpoint/crypto.ts | 3 +- iex-cloud/src/endpoint/stock.ts | 3 +- iex-cloud/src/index.ts | 8 +- json-rpc/package.json | 2 +- json-rpc/src/index.ts | 4 +- kaiko/README.md | 19 +- kaiko/package.json | 2 +- kaiko/src/adapter.ts | 35 +- kaiko/src/config.ts | 6 +- kaiko/src/index.ts | 8 +- kaiko/test/adapter.test.ts | 1 + lcx/package.json | 2 +- lcx/src/index.ts | 4 +- linkpool/package.json | 2 +- linkpool/src/index.ts | 4 +- lition/.eslintrc.js | 3 + lition/README.md | 29 +- lition/adapter.js | 35 - lition/index.js | 4 - lition/package.json | 42 +- lition/src/adapter.ts | 35 + lition/src/config.ts | 11 + lition/src/endpoint/energy.ts | 37 + lition/src/endpoint/index.ts | 1 + lition/src/index.ts | 7 + .../test/{adapter_test.js => adapter.test.ts} | 45 +- lition/tsconfig.json | 10 + marketstack/README.md | 31 +- marketstack/package.json | 2 +- marketstack/src/adapter.ts | 2 +- marketstack/src/config.ts | 2 +- marketstack/src/endpoint/eod.ts | 3 +- marketstack/src/index.ts | 4 +- marketstack/test/adapter.test.ts | 1 + messari/.eslintrc.js | 3 + messari/README.md | 27 +- messari/adapter.js | 58 - messari/index.js | 4 - messari/package.json | 42 +- messari/src/adapter.ts | 36 + messari/src/config.ts | 11 + messari/src/endpoint/assets.ts | 38 + messari/src/endpoint/index.ts | 1 + messari/src/index.ts | 7 + messari/test/adapter_test.js | 73 - messari/test/assets.test.ts | 78 + messari/tsconfig.json | 10 + metalsapi/README.md | 29 +- metalsapi/package.json | 2 +- metalsapi/src/adapter.ts | 1 + metalsapi/src/config.ts | 5 +- metalsapi/src/endpoint/convert.ts | 10 +- metalsapi/src/index.ts | 8 +- metalsapi/test/adapter.test.ts | 19 +- nikkei/README.md | 19 +- nikkei/package.json | 2 +- nikkei/src/adapter.ts | 6 +- nikkei/src/config.ts | 2 +- nikkei/src/endpoint/index.ts | 2 +- nikkei/src/endpoint/{realData.ts => price.ts} | 3 +- nikkei/src/index.ts | 4 +- nomics/README.md | 270 +- nomics/package.json | 2 +- nomics/src/adapter.ts | 6 +- nomics/src/config.ts | 4 +- nomics/src/endpoint/globalmarketcap.ts | 2 +- nomics/src/endpoint/price.ts | 13 +- nomics/src/index.ts | 8 +- nomics/test/adapter.test.ts | 1 + oilpriceapi/package.json | 2 +- oilpriceapi/src/index.ts | 4 +- onchain/package.json | 2 +- onchain/src/index.ts | 4 +- openexchangerates/README.md | 29 +- openexchangerates/package.json | 2 +- openexchangerates/src/adapter.ts | 34 +- openexchangerates/src/config.ts | 6 +- openexchangerates/src/endpoint/index.ts | 1 + openexchangerates/src/endpoint/price.ts | 40 + openexchangerates/src/index.ts | 8 +- openexchangerates/test/adapter.test.ts | 5 +- orchid-bandwidth/.eslintrc.js | 3 + orchid-bandwidth/README.md | 14 +- orchid-bandwidth/adapter.js | 18 - orchid-bandwidth/index.js | 4 - orchid-bandwidth/package.json | 42 +- orchid-bandwidth/src/adapter.ts | 35 + orchid-bandwidth/src/config.ts | 11 + orchid-bandwidth/src/endpoint/bandwidth.ts | 28 + orchid-bandwidth/src/endpoint/index.ts | 1 + orchid-bandwidth/src/index.ts | 7 + orchid-bandwidth/test/adapter_test.js | 25 - orchid-bandwidth/test/bandwidth.test.ts | 25 + orchid-bandwidth/tsconfig.json | 10 + package.json | 31 +- paxos/package.json | 2 +- paxos/src/index.ts | 4 +- poa-gasprice/README.md | 10 +- poa-gasprice/package.json | 2 +- poa-gasprice/src/index.ts | 4 +- polygon/README.md | 34 +- polygon/package.json | 2 +- polygon/src/adapter.ts | 6 +- polygon/src/config.ts | 7 +- polygon/src/endpoint/index.ts | 2 +- .../src/endpoint/{conversion.ts => price.ts} | 15 +- polygon/src/index.ts | 8 +- polygon/test/adapter.test.ts | 1 + reduce/package.json | 2 +- reduce/src/index.ts | 4 +- renvm-address-set/package.json | 2 +- renvm-address-set/src/index.ts | 4 +- satoshitango/.eslintrc.js | 3 + satoshitango/README.md | 175 +- satoshitango/adapter.js | 36 - satoshitango/index.js | 4 - satoshitango/package.json | 42 +- satoshitango/src/adapter.ts | 35 + satoshitango/src/config.ts | 11 + satoshitango/src/endpoint/index.ts | 1 + satoshitango/src/endpoint/ticker.ts | 35 + satoshitango/src/index.ts | 7 + .../test/{adapter_test.js => ticker.test.ts} | 45 +- satoshitango/tsconfig.json | 10 + sochain/package.json | 2 +- sochain/src/index.ts | 4 +- stasis/package.json | 2 +- stasis/src/index.ts | 4 +- synth-index/README.md | 31 - synth-index/adapter.js | 40 - synth-index/amberdata/.eslintrc.js | 1 - synth-index/amberdata/README.md | 5 - synth-index/amberdata/index.js | 4 - synth-index/amberdata/package.json | 15 - synth-index/amberdata/priceAdapter.js | 44 - synth-index/bravenewcoin/.eslintrc.js | 1 - synth-index/bravenewcoin/README.md | 5 - synth-index/bravenewcoin/index.js | 4 - synth-index/bravenewcoin/package.json | 15 - synth-index/bravenewcoin/priceAdapter.js | 53 - synth-index/coinapi/.eslintrc.js | 1 - synth-index/coinapi/README.md | 5 - synth-index/coinapi/index.js | 4 - synth-index/coinapi/package.json | 15 - synth-index/coinapi/priceAdapter.js | 43 - synth-index/coingecko/.eslintrc.js | 1 - synth-index/coingecko/README.md | 1 - synth-index/coingecko/index.js | 4 - synth-index/coingecko/package.json | 15 - synth-index/coingecko/priceAdapter.js | 69 - synth-index/coinmarketcap/.eslintrc.js | 1 - synth-index/coinmarketcap/README.md | 5 - synth-index/coinmarketcap/index.js | 4 - synth-index/coinmarketcap/package.json | 15 - synth-index/coinmarketcap/priceAdapter.js | 90 - synth-index/coinpaprika/.eslintrc.js | 1 - synth-index/coinpaprika/README.md | 1 - synth-index/coinpaprika/index.js | 4 - synth-index/coinpaprika/package.json | 15 - synth-index/coinpaprika/priceAdapter.js | 46 - synth-index/cryptocompare/.eslintrc.js | 1 - synth-index/cryptocompare/README.md | 1 - synth-index/cryptocompare/index.js | 4 - synth-index/cryptocompare/package.json | 15 - synth-index/cryptocompare/priceAdapter.js | 59 - synth-index/kaiko/README.md | 1 - synth-index/kaiko/index.js | 4 - synth-index/kaiko/package.json | 11 - synth-index/kaiko/priceAdapter.js | 51 - synth-index/nomics/README.md | 1 - synth-index/nomics/index.js | 4 - synth-index/nomics/package.json | 11 - synth-index/nomics/priceAdapter.js | 64 - taapi/package.json | 2 +- taapi/src/index.ts | 4 +- test-helpers/package.json | 3 +- test-helpers/src/behaviors/balance.ts | 42 +- test-helpers/src/hardhat.ts | 28 + test-helpers/src/hardhat_config.json | 4 + test-helpers/src/index.ts | 4 +- test-helpers/tsconfig.json | 3 +- therundown/.eslintrc.js | 3 + therundown/README.md | 220 ++ therundown/package.json | 46 + therundown/src/adapter.ts | 35 + therundown/src/config.ts | 18 + therundown/src/endpoint/index.ts | 1 + therundown/src/endpoint/totalScore.ts | 43 + therundown/src/index.ts | 7 + therundown/test/adapter.test.ts | 71 + therundown/tsconfig.json | 10 + tiingo/.eslintrc.js | 3 + tiingo/README.md | 53 + tiingo/package.json | 46 + tiingo/src/adapter.ts | 35 + tiingo/src/config.ts | 14 + tiingo/src/endpoint/eod.ts | 36 + tiingo/src/endpoint/index.ts | 1 + tiingo/src/index.ts | 7 + tiingo/test/adapter.test.ts | 83 + tiingo/tsconfig.json | 10 + tradermade/README.md | 1 + tradermade/package.json | 2 +- tradermade/src/adapter.ts | 33 +- tradermade/src/config.ts | 11 + tradermade/src/index.ts | 9 +- tradermade/test/adapter.test.ts | 8 +- tradingeconomics-stream/.eslintrc.js | 3 + tradingeconomics-stream/README.md | 43 + tradingeconomics-stream/package.json | 50 + tradingeconomics-stream/src/adapter.ts | 83 + tradingeconomics-stream/src/config.ts | 19 + tradingeconomics-stream/src/index.ts | 19 + tradingeconomics-stream/test/adapter.test.ts | 31 + tradingeconomics-stream/tsconfig.json | 10 + tradingeconomics/.eslintrc.js | 4 +- tradingeconomics/README.md | 130 +- tradingeconomics/adapter.js | 99 - tradingeconomics/index.js | 4 - tradingeconomics/package.json | 42 +- tradingeconomics/src/adapter.ts | 36 + tradingeconomics/src/config.ts | 31 + tradingeconomics/src/endpoint/index.ts | 1 + tradingeconomics/src/endpoint/markets.ts | 101 + tradingeconomics/src/index.ts | 7 + tradingeconomics/test/adapter.test.ts | 68 + tradingeconomics/test/adapter_test.js | 61 - tradingeconomics/tsconfig.json | 10 + .../typings/tradingeconomics/index.d.ts | 1 + trueusd/.eslintrc.js | 3 + trueusd/README.md | 29 +- trueusd/adapter.js | 28 - trueusd/index.js | 4 - trueusd/package.json | 42 +- trueusd/src/adapter.ts | 35 + trueusd/src/config.ts | 11 + trueusd/src/endpoint/index.ts | 1 + trueusd/src/endpoint/trueusd.ts | 30 + trueusd/src/index.ts | 7 + trueusd/test/adapter_test.js | 53 - trueusd/test/trueusd.test.ts | 56 + trueusd/tsconfig.json | 10 + typings/@chainlink/index.d.ts | 13 +- wbtc-address-set/package.json | 2 +- wbtc-address-set/src/index.ts | 4 +- xbto/package.json | 2 +- xbto/src/index.ts | 4 +- yarn.lock | 3368 ++++++++++------- 763 files changed, 15209 insertions(+), 7218 deletions(-) delete mode 100644 1forge/adapter.js delete mode 100644 1forge/index.js create mode 100644 1forge/src/adapter.ts create mode 100644 1forge/src/config.ts create mode 100644 1forge/src/endpoint/index.ts create mode 100644 1forge/src/endpoint/price.ts create mode 100644 1forge/src/index.ts rename currencylayer/test/adapter_test.js => 1forge/test/adapter.test.ts (52%) create mode 100644 1forge/tsconfig.json delete mode 100644 Dockerfile-SynthIndex create mode 100644 agoric/.eslintrc.js create mode 100644 agoric/README.md create mode 100644 agoric/package.json create mode 100644 agoric/src/adapter.ts create mode 100644 agoric/src/config.ts create mode 100644 agoric/src/index.ts create mode 100644 agoric/test/adapter.test.ts create mode 100644 agoric/tsconfig.json create mode 100644 alphachain/.eslintrc.js delete mode 100644 alphachain/adapter.js delete mode 100644 alphachain/index.js create mode 100644 alphachain/src/adapter.ts create mode 100644 alphachain/src/config.ts create mode 100644 alphachain/src/endpoint/data-query.ts create mode 100644 alphachain/src/endpoint/index.ts create mode 100644 alphachain/src/index.ts rename alphachain/test/{adapter_test.js => data-query.test.ts} (54%) create mode 100644 alphachain/tsconfig.json delete mode 100644 alphavantage-sdr/.eslintrc.js delete mode 100644 alphavantage-sdr/test/adapter_test.js delete mode 100644 alphavantage/adapter.js delete mode 100644 alphavantage/index.js create mode 100644 alphavantage/src/adapter.ts create mode 100644 alphavantage/src/config.ts create mode 100644 alphavantage/src/endpoint/index.ts create mode 100644 alphavantage/src/endpoint/price.ts create mode 100644 alphavantage/src/index.ts create mode 100644 alphavantage/test/adapter.test.ts create mode 100644 alphavantage/tsconfig.json delete mode 100644 amberdata-gasprice/adapter.js delete mode 100644 amberdata-gasprice/index.js create mode 100644 amberdata-gasprice/src/adapter.ts create mode 100644 amberdata-gasprice/src/config.ts create mode 100644 amberdata-gasprice/src/endpoint/gasprice.ts create mode 100644 amberdata-gasprice/src/endpoint/index.ts create mode 100644 amberdata-gasprice/src/index.ts rename amberdata-gasprice/test/{adapter_test.js => gasprice.test.ts} (51%) create mode 100644 amberdata-gasprice/tsconfig.json delete mode 100644 anyblock-gasprice/adapter.js delete mode 100644 anyblock-gasprice/index.js create mode 100644 anyblock-gasprice/src/adapter.ts create mode 100644 anyblock-gasprice/src/config.ts create mode 100644 anyblock-gasprice/src/endpoint/gasprice.ts create mode 100644 anyblock-gasprice/src/endpoint/index.ts create mode 100644 anyblock-gasprice/src/index.ts rename anyblock-gasprice/test/{adapter_test.js => gasprice.test.ts} (52%) create mode 100644 anyblock-gasprice/tsconfig.json delete mode 100644 binance-dex/adapter.js delete mode 100644 binance-dex/index.js create mode 100644 binance-dex/src/adapter.ts create mode 100644 binance-dex/src/config.ts create mode 100644 binance-dex/src/endpoint/index.ts create mode 100644 binance-dex/src/endpoint/price.ts create mode 100644 binance-dex/src/index.ts rename binance-dex/test/{adapter_test.js => price.test.ts} (64%) create mode 100644 binance-dex/tsconfig.json create mode 100644 bitex/.eslintrc.js delete mode 100644 bitex/adapter.js delete mode 100644 bitex/index.js create mode 100644 bitex/src/adapter.ts create mode 100644 bitex/src/config.ts create mode 100644 bitex/src/endpoint/index.ts create mode 100644 bitex/src/endpoint/tickers.ts create mode 100644 bitex/src/index.ts rename bitex/test/{adapter_test.js => tickers.test.ts} (52%) create mode 100644 bitex/tsconfig.json create mode 100644 bitso/.eslintrc.js delete mode 100644 bitso/adapter.js delete mode 100644 bitso/index.js create mode 100644 bitso/src/adapter.ts create mode 100644 bitso/src/config.ts create mode 100644 bitso/src/endpoint/index.ts create mode 100644 bitso/src/endpoint/ticker.ts create mode 100644 bitso/src/index.ts rename bitso/test/{adapter_test.js => ticker.test.ts} (52%) create mode 100644 bitso/tsconfig.json create mode 100644 blockchain.com/src/endpoint/difficulty.ts create mode 100644 blockchain.com/src/endpoint/height.ts create mode 100644 blockstream/.eslintrc.js create mode 100644 blockstream/README.md create mode 100644 blockstream/package.json create mode 100644 blockstream/src/adapter.ts create mode 100644 blockstream/src/config.ts create mode 100644 blockstream/src/endpoint/blocks.ts create mode 100644 blockstream/src/endpoint/index.ts create mode 100644 blockstream/src/index.ts create mode 100644 blockstream/test/adapter.test.ts create mode 100644 blockstream/tsconfig.json create mode 100644 bootstrap/src/lib/metrics/index.ts delete mode 100644 bravenewcoin-vwap/.eslintrc.js delete mode 100644 bravenewcoin-vwap/README.md delete mode 100644 bravenewcoin-vwap/adapter.js delete mode 100644 bravenewcoin-vwap/index.js delete mode 100644 bravenewcoin-vwap/package.json delete mode 100644 bravenewcoin-vwap/test/adapter_test.js delete mode 100644 bravenewcoin/adapter.js delete mode 100644 bravenewcoin/index.js create mode 100644 bravenewcoin/src/adapter.ts create mode 100644 bravenewcoin/src/config.ts create mode 100644 bravenewcoin/src/endpoint/index.ts create mode 100644 bravenewcoin/src/endpoint/price.ts create mode 100644 bravenewcoin/src/endpoint/vwap.ts rename helpers/bravenewcoin/helpers.js => bravenewcoin/src/helpers.ts (67%) create mode 100644 bravenewcoin/src/index.ts rename bravenewcoin/test/{adapter_test.js => price.test.ts} (54%) create mode 100644 bravenewcoin/test/vwap.test.ts create mode 100644 bravenewcoin/tsconfig.json delete mode 100644 coinapi/adapter.js delete mode 100644 coinapi/index.js create mode 100644 coinapi/src/adapter.ts create mode 100644 coinapi/src/config.ts create mode 100644 coinapi/src/endpoint/index.ts create mode 100644 coinapi/src/endpoint/price.ts create mode 100644 coinapi/src/index.ts rename coinapi/test/{adapter_test.js => price.test.ts} (56%) create mode 100644 coinapi/tsconfig.json delete mode 100644 coinbase/.editorconfig delete mode 100644 coinbase/adapter.js delete mode 100644 coinbase/index.js create mode 100644 coinbase/src/adapter.ts create mode 100644 coinbase/src/config.ts create mode 100644 coinbase/src/endpoint/index.ts create mode 100644 coinbase/src/endpoint/price.ts create mode 100644 coinbase/src/index.ts delete mode 100644 coinbase/test/adapter_test.js rename alphavantage/test/adapter_test.js => coinbase/test/price.test.ts (54%) create mode 100644 coinbase/tsconfig.json delete mode 100644 coingecko/.editorconfig delete mode 100644 coingecko/adapter.js delete mode 100644 coingecko/index.js create mode 100644 coingecko/src/adapter.ts create mode 100644 coingecko/src/config.ts create mode 100644 coingecko/src/endpoint/global.ts create mode 100644 coingecko/src/endpoint/index.ts create mode 100644 coingecko/src/endpoint/price.ts create mode 100644 coingecko/src/index.ts delete mode 100644 coingecko/test/adapter_test.js rename cryptoapis/test/stats.test.ts => coingecko/test/dominance.test.ts (64%) create mode 100644 coingecko/test/global.test.ts create mode 100644 coingecko/test/price.test.ts create mode 100644 coingecko/tsconfig.json create mode 100644 coinlore/.eslintrc.js delete mode 100644 coinlore/adapter.js delete mode 100644 coinlore/index.js create mode 100644 coinlore/src/adapter.ts create mode 100644 coinlore/src/config.ts create mode 100644 coinlore/src/endpoint/global.ts create mode 100644 coinlore/src/endpoint/index.ts create mode 100644 coinlore/src/index.ts delete mode 100644 coinlore/test/adapter_test.js create mode 100644 coinlore/test/global.test.ts create mode 100644 coinlore/tsconfig.json delete mode 100644 coinmarketcap/adapter.js delete mode 100644 coinmarketcap/index.js create mode 100644 coinmarketcap/src/adapter.ts create mode 100644 coinmarketcap/src/config.ts create mode 100644 coinmarketcap/src/endpoint/dominance.ts create mode 100644 coinmarketcap/src/endpoint/index.ts create mode 100644 coinmarketcap/src/endpoint/marketcap.ts create mode 100644 coinmarketcap/src/endpoint/price.ts create mode 100644 coinmarketcap/src/index.ts create mode 100644 coinmarketcap/test/dominance.test.ts create mode 100644 coinmarketcap/test/marketcap.test.ts rename coinmarketcap/test/{adapter_test.js => price.test.ts} (53%) create mode 100644 coinmarketcap/tsconfig.json delete mode 100644 coinpaprika/adapter.js delete mode 100644 coinpaprika/index.js create mode 100644 coinpaprika/src/adapter.ts create mode 100644 coinpaprika/src/config.ts create mode 100644 coinpaprika/src/endpoint/dominance.ts create mode 100644 coinpaprika/src/endpoint/index.ts create mode 100644 coinpaprika/src/endpoint/marketcap.ts create mode 100644 coinpaprika/src/endpoint/price.ts create mode 100644 coinpaprika/src/index.ts rename coinpaprika/test/{adapter_test.js => adapter.test.ts} (61%) create mode 100644 coinpaprika/tsconfig.json create mode 100644 composite/synth-index/.eslintrc.js create mode 100644 composite/synth-index/README.md create mode 100644 composite/synth-index/package.json create mode 100644 composite/synth-index/src/adapter.ts create mode 100644 composite/synth-index/src/config.ts create mode 100644 composite/synth-index/src/index.ts create mode 100644 composite/synth-index/test/adapter.test.ts create mode 100644 composite/synth-index/tsconfig.json create mode 100644 conflux/.eslintrc.js create mode 100644 conflux/README.md create mode 100644 conflux/package.json create mode 100644 conflux/src/adapter.ts create mode 100644 conflux/src/config.ts create mode 100644 conflux/src/endpoint/conflux.ts create mode 100644 conflux/src/endpoint/index.ts create mode 100644 conflux/src/index.ts create mode 100644 conflux/test/adapter.test.ts create mode 100644 conflux/tsconfig.json create mode 100644 covid-tracker/.eslintrc.js delete mode 100644 covid-tracker/adapter.js delete mode 100644 covid-tracker/index.js create mode 100644 covid-tracker/src/adapter.ts create mode 100644 covid-tracker/src/config.ts create mode 100644 covid-tracker/src/endpoint/index.ts create mode 100644 covid-tracker/src/endpoint/us.ts create mode 100644 covid-tracker/src/index.ts create mode 100644 covid-tracker/test/adapter.test.ts delete mode 100644 covid-tracker/test/adapter_test.js create mode 100644 covid-tracker/tsconfig.json create mode 100644 cryptoapis/test/bc_info.test.ts delete mode 100644 cryptocompare/adapter.js delete mode 100644 cryptocompare/index.js create mode 100644 cryptocompare/src/adapter.ts create mode 100644 cryptocompare/src/config.ts create mode 100644 cryptocompare/src/endpoint/index.ts create mode 100644 cryptocompare/src/endpoint/price.ts create mode 100644 cryptocompare/src/index.ts rename cryptocompare/test/{adapter_test.js => adapter.test.ts} (56%) create mode 100644 cryptocompare/tsconfig.json create mode 100644 cryptomkt/.eslintrc.js delete mode 100644 cryptomkt/adapter.js delete mode 100644 cryptomkt/index.js create mode 100644 cryptomkt/src/adapter.ts create mode 100644 cryptomkt/src/config.ts create mode 100644 cryptomkt/src/endpoint/index.ts create mode 100644 cryptomkt/src/endpoint/ticker.ts create mode 100644 cryptomkt/src/index.ts rename cryptomkt/test/{adapter_test.js => ticker.test.ts} (52%) create mode 100644 cryptomkt/tsconfig.json delete mode 100644 currencylayer/adapter.js delete mode 100644 currencylayer/index.js create mode 100644 currencylayer/src/adapter.ts create mode 100644 currencylayer/src/config.ts create mode 100644 currencylayer/src/endpoint/convert.ts create mode 100644 currencylayer/src/endpoint/index.ts create mode 100644 currencylayer/src/index.ts rename 1forge/test/adapter_test.js => currencylayer/test/adapter.test.ts (52%) create mode 100644 currencylayer/tsconfig.json delete mode 100644 eodhistoricaldata/adapter.js delete mode 100644 eodhistoricaldata/index.js create mode 100644 eodhistoricaldata/src/adapter.ts create mode 100644 eodhistoricaldata/src/config.ts create mode 100644 eodhistoricaldata/src/endpoint/index.ts create mode 100644 eodhistoricaldata/src/endpoint/price.ts create mode 100644 eodhistoricaldata/src/index.ts create mode 100644 eodhistoricaldata/test/adapter.test.ts delete mode 100644 eodhistoricaldata/test/adapter_test.js create mode 100644 eodhistoricaldata/tsconfig.json delete mode 100644 etherchain/adapter.js delete mode 100644 etherchain/index.js create mode 100644 etherchain/src/adapter.ts create mode 100644 etherchain/src/config.ts create mode 100644 etherchain/src/endpoint/gasprice.ts create mode 100644 etherchain/src/endpoint/index.ts create mode 100644 etherchain/src/index.ts rename etherchain/test/{adapter_test.js => adapter.test.ts} (50%) create mode 100644 etherchain/tsconfig.json delete mode 100644 ethgasstation/adapter.js delete mode 100644 ethgasstation/index.js create mode 100644 ethgasstation/src/adapter.ts create mode 100644 ethgasstation/src/config.ts create mode 100644 ethgasstation/src/endpoint/gasprice.ts create mode 100644 ethgasstation/src/endpoint/index.ts create mode 100644 ethgasstation/src/index.ts create mode 100644 ethgasstation/test/adapter.test.ts delete mode 100644 ethgasstation/test/adapter_test.js create mode 100644 ethgasstation/tsconfig.json create mode 100644 ethwrite/.eslintrc.js create mode 100644 ethwrite/README.md create mode 100644 ethwrite/package.json create mode 100644 ethwrite/src/adapter.ts create mode 100644 ethwrite/src/config.ts create mode 100644 ethwrite/src/endpoint/index.ts create mode 100644 ethwrite/src/endpoint/txsend.ts create mode 100644 ethwrite/src/index.ts create mode 100644 ethwrite/test/adapter.test.ts create mode 100644 ethwrite/test/helpers.ts create mode 100644 ethwrite/tsconfig.json rename example/test/{adapter.test.ts => example.test.ts} (100%) create mode 100644 external-adapter/src/overrides/presetSymbols.json delete mode 100644 fixer/adapter.js delete mode 100644 fixer/index.js create mode 100644 fixer/src/adapter.ts create mode 100644 fixer/src/config.ts create mode 100644 fixer/src/endpoint/convert.ts create mode 100644 fixer/src/endpoint/index.ts create mode 100644 fixer/src/index.ts rename fixer/test/{adapter_test.js => adapter.test.ts} (59%) create mode 100644 fixer/tsconfig.json delete mode 100644 fmpcloud/adapter.js delete mode 100644 fmpcloud/index.js create mode 100644 fmpcloud/src/adapter.ts create mode 100644 fmpcloud/src/config.ts create mode 100644 fmpcloud/src/endpoint/index.ts create mode 100644 fmpcloud/src/endpoint/quote.ts create mode 100644 fmpcloud/src/index.ts create mode 100644 fmpcloud/test/adapter.test.ts delete mode 100644 fmpcloud/test/adapter_test.js create mode 100644 fmpcloud/tsconfig.json create mode 100644 geodb/.eslintrc.js create mode 100644 geodb/README.md create mode 100644 geodb/package.json create mode 100644 geodb/src/adapter.ts create mode 100644 geodb/src/config.ts create mode 100644 geodb/src/endpoint/index.ts create mode 100644 geodb/src/endpoint/matches.ts create mode 100644 geodb/src/index.ts create mode 100644 geodb/test/adapter.test.ts create mode 100644 geodb/tsconfig.json delete mode 100644 google-finance/.eslintrc.js delete mode 100644 google-finance/README.md delete mode 100644 google-finance/adapter.js delete mode 100644 google-finance/index.js delete mode 100644 google-finance/package.json delete mode 100644 google-finance/test/adapter_test.js create mode 100644 hardhat.config.js create mode 100644 lition/.eslintrc.js delete mode 100644 lition/adapter.js delete mode 100644 lition/index.js create mode 100644 lition/src/adapter.ts create mode 100644 lition/src/config.ts create mode 100644 lition/src/endpoint/energy.ts create mode 100644 lition/src/endpoint/index.ts create mode 100644 lition/src/index.ts rename lition/test/{adapter_test.js => adapter.test.ts} (54%) create mode 100644 lition/tsconfig.json create mode 100644 messari/.eslintrc.js delete mode 100644 messari/adapter.js delete mode 100644 messari/index.js create mode 100644 messari/src/adapter.ts create mode 100644 messari/src/config.ts create mode 100644 messari/src/endpoint/assets.ts create mode 100644 messari/src/endpoint/index.ts create mode 100644 messari/src/index.ts delete mode 100644 messari/test/adapter_test.js create mode 100644 messari/test/assets.test.ts create mode 100644 messari/tsconfig.json rename nikkei/src/endpoint/{realData.ts => price.ts} (92%) create mode 100644 openexchangerates/src/endpoint/index.ts create mode 100644 openexchangerates/src/endpoint/price.ts create mode 100644 orchid-bandwidth/.eslintrc.js delete mode 100644 orchid-bandwidth/adapter.js delete mode 100644 orchid-bandwidth/index.js create mode 100644 orchid-bandwidth/src/adapter.ts create mode 100644 orchid-bandwidth/src/config.ts create mode 100644 orchid-bandwidth/src/endpoint/bandwidth.ts create mode 100644 orchid-bandwidth/src/endpoint/index.ts create mode 100644 orchid-bandwidth/src/index.ts delete mode 100644 orchid-bandwidth/test/adapter_test.js create mode 100644 orchid-bandwidth/test/bandwidth.test.ts create mode 100644 orchid-bandwidth/tsconfig.json rename polygon/src/endpoint/{conversion.ts => price.ts} (74%) create mode 100644 satoshitango/.eslintrc.js delete mode 100644 satoshitango/adapter.js delete mode 100644 satoshitango/index.js create mode 100644 satoshitango/src/adapter.ts create mode 100644 satoshitango/src/config.ts create mode 100644 satoshitango/src/endpoint/index.ts create mode 100644 satoshitango/src/endpoint/ticker.ts create mode 100644 satoshitango/src/index.ts rename satoshitango/test/{adapter_test.js => ticker.test.ts} (52%) create mode 100644 satoshitango/tsconfig.json delete mode 100644 synth-index/README.md delete mode 100644 synth-index/adapter.js delete mode 100644 synth-index/amberdata/.eslintrc.js delete mode 100644 synth-index/amberdata/README.md delete mode 100644 synth-index/amberdata/index.js delete mode 100644 synth-index/amberdata/package.json delete mode 100644 synth-index/amberdata/priceAdapter.js delete mode 100644 synth-index/bravenewcoin/.eslintrc.js delete mode 100644 synth-index/bravenewcoin/README.md delete mode 100644 synth-index/bravenewcoin/index.js delete mode 100644 synth-index/bravenewcoin/package.json delete mode 100644 synth-index/bravenewcoin/priceAdapter.js delete mode 100644 synth-index/coinapi/.eslintrc.js delete mode 100644 synth-index/coinapi/README.md delete mode 100644 synth-index/coinapi/index.js delete mode 100644 synth-index/coinapi/package.json delete mode 100644 synth-index/coinapi/priceAdapter.js delete mode 100644 synth-index/coingecko/.eslintrc.js delete mode 100644 synth-index/coingecko/README.md delete mode 100644 synth-index/coingecko/index.js delete mode 100644 synth-index/coingecko/package.json delete mode 100644 synth-index/coingecko/priceAdapter.js delete mode 100644 synth-index/coinmarketcap/.eslintrc.js delete mode 100644 synth-index/coinmarketcap/README.md delete mode 100644 synth-index/coinmarketcap/index.js delete mode 100644 synth-index/coinmarketcap/package.json delete mode 100644 synth-index/coinmarketcap/priceAdapter.js delete mode 100644 synth-index/coinpaprika/.eslintrc.js delete mode 100644 synth-index/coinpaprika/README.md delete mode 100644 synth-index/coinpaprika/index.js delete mode 100644 synth-index/coinpaprika/package.json delete mode 100644 synth-index/coinpaprika/priceAdapter.js delete mode 100644 synth-index/cryptocompare/.eslintrc.js delete mode 100644 synth-index/cryptocompare/README.md delete mode 100644 synth-index/cryptocompare/index.js delete mode 100644 synth-index/cryptocompare/package.json delete mode 100644 synth-index/cryptocompare/priceAdapter.js delete mode 100644 synth-index/kaiko/README.md delete mode 100644 synth-index/kaiko/index.js delete mode 100644 synth-index/kaiko/package.json delete mode 100644 synth-index/kaiko/priceAdapter.js delete mode 100644 synth-index/nomics/README.md delete mode 100644 synth-index/nomics/index.js delete mode 100644 synth-index/nomics/package.json delete mode 100644 synth-index/nomics/priceAdapter.js create mode 100644 test-helpers/src/hardhat.ts create mode 100644 test-helpers/src/hardhat_config.json create mode 100644 therundown/.eslintrc.js create mode 100644 therundown/README.md create mode 100644 therundown/package.json create mode 100644 therundown/src/adapter.ts create mode 100644 therundown/src/config.ts create mode 100644 therundown/src/endpoint/index.ts create mode 100644 therundown/src/endpoint/totalScore.ts create mode 100644 therundown/src/index.ts create mode 100644 therundown/test/adapter.test.ts create mode 100644 therundown/tsconfig.json create mode 100644 tiingo/.eslintrc.js create mode 100644 tiingo/README.md create mode 100644 tiingo/package.json create mode 100644 tiingo/src/adapter.ts create mode 100644 tiingo/src/config.ts create mode 100644 tiingo/src/endpoint/eod.ts create mode 100644 tiingo/src/endpoint/index.ts create mode 100644 tiingo/src/index.ts create mode 100644 tiingo/test/adapter.test.ts create mode 100644 tiingo/tsconfig.json create mode 100644 tradermade/src/config.ts create mode 100644 tradingeconomics-stream/.eslintrc.js create mode 100644 tradingeconomics-stream/README.md create mode 100644 tradingeconomics-stream/package.json create mode 100644 tradingeconomics-stream/src/adapter.ts create mode 100644 tradingeconomics-stream/src/config.ts create mode 100644 tradingeconomics-stream/src/index.ts create mode 100644 tradingeconomics-stream/test/adapter.test.ts create mode 100644 tradingeconomics-stream/tsconfig.json delete mode 100644 tradingeconomics/adapter.js delete mode 100644 tradingeconomics/index.js create mode 100644 tradingeconomics/src/adapter.ts create mode 100644 tradingeconomics/src/config.ts create mode 100644 tradingeconomics/src/endpoint/index.ts create mode 100644 tradingeconomics/src/endpoint/markets.ts create mode 100644 tradingeconomics/src/index.ts create mode 100644 tradingeconomics/test/adapter.test.ts delete mode 100644 tradingeconomics/test/adapter_test.js create mode 100644 tradingeconomics/tsconfig.json create mode 100644 tradingeconomics/typings/tradingeconomics/index.d.ts create mode 100644 trueusd/.eslintrc.js delete mode 100644 trueusd/adapter.js delete mode 100644 trueusd/index.js create mode 100644 trueusd/src/adapter.ts create mode 100644 trueusd/src/config.ts create mode 100644 trueusd/src/endpoint/index.ts create mode 100644 trueusd/src/endpoint/trueusd.ts create mode 100644 trueusd/src/index.ts delete mode 100644 trueusd/test/adapter_test.js create mode 100644 trueusd/test/trueusd.test.ts create mode 100644 trueusd/tsconfig.json diff --git a/.github/strategy/adapters.json b/.github/strategy/adapters.json index 664ed26227..9abb2e1968 100644 --- a/.github/strategy/adapters.json +++ b/.github/strategy/adapters.json @@ -7,6 +7,7 @@ "image_name": "__ADAPTER__-adapter", "adapter": [ "1forge", + "agoric", "alphachain", "alphavantage", "amberdata", @@ -19,9 +20,9 @@ "blockchain.com", "blockchair", "blockcypher", + "blockstream", "bootstrap", "bravenewcoin", - "bravenewcoin-vwap", "coinapi", "coinbase", "coincodex", @@ -51,7 +52,6 @@ "fixer", "fmpcloud", "genesis-volatility", - "google-finance", "json-rpc", "kaiko", "lcx", @@ -82,7 +82,13 @@ "dydx-stark", "iex-cloud", "cfbenchmarks", - "harmony" + "conflux", + "harmony", + "tiingo", + "ethwrite", + "geodb", + "therundown", + "tradingeconomics-stream" ] }, "2-step": { @@ -116,25 +122,8 @@ "apy-finance", "crypto-volatility-index", "bitcoin-json-rpc", - "token-allocation" - ] - }, - "synth-index": { - "docker": "make docker-synth-index adapter=__ADAPTER__", - "cmd": "make zip-synth-index adapter=__ADAPTER__", - "asset_path": "./synth-index/__ADAPTER__/dist/synth-index-__ADAPTER__-adapter.zip", - "asset_name": "synth-index-__ADAPTER__-adapter.zip", - "image_name": "synth-index-__ADAPTER__-adapter", - "adapter": [ - "amberdata", - "bravenewcoin", - "coinapi", - "coingecko", - "coinmarketcap", - "coinpaprika", - "cryptocompare", - "kaiko", - "nomics" + "token-allocation", + "synth-index" ] } } diff --git a/.github/workflows/release-staging.yml b/.github/workflows/release-staging.yml index 6df8cedce3..2aa721044c 100644 --- a/.github/workflows/release-staging.yml +++ b/.github/workflows/release-staging.yml @@ -52,21 +52,25 @@ jobs: run: ${{ matrix.adapter.cmd }} - name: Build Docker containers run: ${{ matrix.adapter.docker }} repo="public.ecr.aws/${{ env.publicecr-name }}/adapters/" - - name: Configure AWS Credentials + - name: Configure AWS prod Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + - name: Upload to prod S3 + run: aws s3 cp ${{ matrix.adapter.asset_path }} s3://adaptor-source-cl/${{ matrix.adapter.asset_name }} + - name: Configure staging AWS Credentials uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_STAGING }} aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY_STAGING }} aws-region: ${{ secrets.AWS_REGION }} - - name: Upload to S3 + - name: Upload to staging S3 run: aws s3 cp ${{ matrix.adapter.asset_path }} s3://adaptor-source-cl-staging/${{ matrix.adapter.asset_name }} - name: Authenticate to public ECR - run: aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws - env: - AWS_DEFAULT_REGION: us-east-1 # https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html#public-registry-auth + run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/${{ env.publicecr-name }} - name: Create a public ECR repository if does not exist - run: aws ecr-public create-repository --repository-name adapters/${{ matrix.adapter.image_name }} || true - env: - AWS_DEFAULT_REGION: us-east-1 + run: aws ecr-public create-repository --region us-east-1 --repository-name adapters/${{ matrix.adapter.image_name }} || true - name: Push to public ECR run: docker push public.ecr.aws/${{ env.publicecr-name }}/adapters/${{ matrix.adapter.image_name }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 79affecb00..006c81ccb8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ name: Release env: cache-name: release-ci - publicecr-name: chainlink + publicecr-name: z0b1w9r9 jobs: release: @@ -90,7 +90,7 @@ jobs: asset_path: ${{ matrix.adapter.asset_path }} asset_name: ${{ needs.release.outputs.asset_prefix }}-${{ matrix.adapter.asset_name }} asset_content_type: application/zip - - name: Configure AWS Credentials + - name: Configure AWS Credentials for Prod S3 uses: aws-actions/configure-aws-credentials@v1 with: aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} @@ -98,13 +98,17 @@ jobs: aws-region: ${{ secrets.AWS_REGION }} - name: Upload to S3 run: aws s3 cp ${{ matrix.adapter.asset_path }} s3://adaptor-source-cl/${{ matrix.adapter.asset_name }} + - name: Configure AWS Credentials for SDLC Public ECR + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_PUBLICECR_ACCESSKEY }} + aws-secret-access-key: ${{ secrets.AWS_PUBLICECR_SECRETKEY }} + aws-region: ${{ secrets.AWS_REGION }} + role-to-assume: ${{ secrets.AWS_PUBLICECR_ROLE_ARN }} + role-duration-seconds: 1200 - name: Authenticate to public ECR - run: aws ecr-public get-login-password | docker login --username AWS --password-stdin public.ecr.aws - env: - AWS_DEFAULT_REGION: us-east-1 # https://docs.aws.amazon.com/AmazonECR/latest/public/public-registries.html#public-registry-auth + run: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/${{ env.publicecr-name }} - name: Create a public ECR repository if does not exist - run: aws ecr-public create-repository --repository-name adapters/${{ matrix.adapter.image_name }} || true - env: - AWS_DEFAULT_REGION: us-east-1 + run: aws ecr-public create-repository --region us-east-1 --repository-name adapters/${{ matrix.adapter.image_name }} || true - name: Push to public ECR run: docker push public.ecr.aws/${{ env.publicecr-name }}/adapters/${{ matrix.adapter.image_name }} diff --git a/.gitignore b/.gitignore index 3ec85d39e8..427a19a0df 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ *.zip dist .DS_Store +cache diff --git a/1forge/.eslintrc.js b/1forge/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/1forge/.eslintrc.js +++ b/1forge/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/1forge/README.md b/1forge/README.md index c352dd06d2..4b0f9e6c45 100644 --- a/1forge/README.md +++ b/1forge/README.md @@ -1,23 +1,47 @@ -# Chainlink 1Forge External Adapter +# Chainlink External Adapter for 1forge -## Input Params +### Environment Variables -- `base` or `to`: The target currency to query (required) -- `quote` or `from`: The currency to convert to (required) -- `endpoint`: The endpoint to call (optional) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from the 1forge dashboard | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +[`/convert`](https://1forge.com/api#convert) - Convert from one currency to another + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------: | :-------------------------------------------: | :--------------------------------------: | :---------: | +| ✅ | `base`, `from` | The symbol of the currency to query | [List](https://1forge.com/currency-list) | | +| ✅ | `quote`, `to` | The symbol of the currency to convert to | [List](https://1forge.com/currency-list) | | +| 🟡 | `quantity` | An additional amount of the original currency | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + + +### Output ```json { - "jobRunID": "1", - "data": { - "value": 1.22687, - "text": "1.0 GBP is worth 1.22687 USD", - "timestamp": 1587489920, - "result": 1.22687 - }, - "result": 1.22687, - "statusCode": 200 + "jobRunID": "1", + "data": { + "value": 1.22687, + "text": "1.0 GBP is worth 1.22687 USD", + "timestamp": 1587489920, + "result": 1.22687 + }, + "result": 1.22687, + "statusCode": 200 } ``` diff --git a/1forge/adapter.js b/1forge/adapter.js deleted file mode 100644 index 6a4ca2727d..0000000000 --- a/1forge/adapter.js +++ /dev/null @@ -1,43 +0,0 @@ -const { util } = require('@chainlink/ea-bootstrap') -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - base: ['base', 'from'], - quote: ['quote', 'to'], - endpoint: false, - quantity: false, -} - -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 endpoint = validator.validated.data.endpoint || 'convert' - const url = `https://api.1forge.com/${endpoint}` - const from = validator.validated.data.base.toUpperCase() - const to = validator.validated.data.quote.toUpperCase() - const quantity = validator.validated.data.quantity || 1 - const api_key = util.getRandomRequiredEnv('API_KEY') // eslint-disable-line camelcase - - const params = { - from, - to, - quantity, - api_key, - } - - const config = { - url, - params, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['value']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/1forge/index.js b/1forge/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/1forge/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/1forge/package.json b/1forge/package.json index e5d4e152e3..c6a5d4458e 100644 --- a/1forge/package.json +++ b/1forge/package.json @@ -1,15 +1,34 @@ { "name": "@chainlink/1forge-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], "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 15000 -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 15000 --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/1forge/src/adapter.ts b/1forge/src/adapter.ts new file mode 100644 index 0000000000..e7c19bba45 --- /dev/null +++ b/1forge/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/1forge/src/config.ts b/1forge/src/config.ts new file mode 100644 index 0000000000..b07edb911e --- /dev/null +++ b/1forge/src/config.ts @@ -0,0 +1,19 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = '1FORGE' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://api.1forge.com/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + api_key: config.apiKey, + }, + } + return config +} diff --git a/1forge/src/endpoint/index.ts b/1forge/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/1forge/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/1forge/src/endpoint/price.ts b/1forge/src/endpoint/price.ts new file mode 100644 index 0000000000..f9d06f2abd --- /dev/null +++ b/1forge/src/endpoint/price.ts @@ -0,0 +1,44 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const customParams = { + base: ['base', 'from'], + quote: ['quote', 'to'], + quantity: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = `/convert` + const from = validator.overrideSymbol(AdapterName).toUpperCase() + const to = validator.validated.data.quote.toUpperCase() + const quantity = validator.validated.data.quantity || 1 + + const params = { + ...config.api.params, + from, + to, + quantity, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['value']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/1forge/src/index.ts b/1forge/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/1forge/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/currencylayer/test/adapter_test.js b/1forge/test/adapter.test.ts similarity index 52% rename from currencylayer/test/adapter_test.js rename to 1forge/test/adapter.test.ts index 4f163acbd8..19473de743 100644 --- a/currencylayer/test/adapter_test.js +++ b/1forge/test/adapter.test.ts @@ -1,9 +1,13 @@ -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 { makeExecute } from '../src/adapter' +import { AdapterRequest } from '@chainlink/types' describe('execute', () => { const jobID = '1' + const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -22,13 +26,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -48,11 +50,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) + } }) }) }) @@ -70,11 +74,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/1forge/tsconfig.json b/1forge/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/1forge/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/CHANGELOG.md b/CHANGELOG.md index a714aef6e4..451e28afcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,36 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ## [Unreleased] +### Added + +### Changed + +## [0.2.0] - 2021-4-13 + +### Added + +- New adapters: + - `agoric` to push results to the Agoric blockchain + - `therundown` to get sports score data from TheRundown + - `tradingeconomics-stream` to get stream data from TradingEconomics + - `blockstream` to get Bitcoin height and difficulty +- Basic prometheus metrics endpoint + +### Changed + +- Remaining non-2-step adapters migrated to TS +- Updated support for `DIGG/BTC` in Kaiko and Amberdata +- Updated base URL for GeoDB +- CMC now uses preset IDs instead of preset slugs +- Added support for `tradermade` in `outlier-detection` composite adapter +- Added support for `overrides` param in price adapters + ## [0.2.0-rc.1] - 2021-2-4 ### Added - New adapters: + - `conflux` to send conflux chain transaction - `xbto` to get BRN quotes - `iv-outlier-detection` to get IV values with outlier detection - `taapi` to get Trading Analysis data @@ -34,6 +59,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `cfbenchmarks` to get crypto benchmarks and indices - `dxfeed-secondary` to handle secondary mappings for the TSLA symbol - `harmony` to write transactions to the Harmony blockchain + - `tiingo` to get end-of-day stock price data from Tiingo + - `geodb` to get location data from GeoDB - 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. @@ -66,6 +93,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - `polygon` - `nomics` - `openexchangerates` + - `coinmarketcap` +- `synth-index` adapter is now a composite adapter. Going forward there is only one instance of `synth-index` adapter built, one that you configure with the underlying data provider you wish to use. +- Removed + - `google-finance` ### Removed diff --git a/Dockerfile-SynthIndex b/Dockerfile-SynthIndex deleted file mode 100644 index 2bf61e4cbb..0000000000 --- a/Dockerfile-SynthIndex +++ /dev/null @@ -1,17 +0,0 @@ -FROM node:12 as builder -ARG adapter -WORKDIR /home/node/app - -COPY . . -RUN make deps -RUN make build-synth-index - -FROM node:12-alpine -ARG adapter -EXPOSE 8080 -WORKDIR /home/node/app - -COPY --from=builder /home/node/app/synth-index/$adapter/dist ./ -COPY --from=builder /home/node/app/synth-index/$adapter/package.json ./ - -CMD ["yarn", "server"] diff --git a/Makefile b/Makefile index 91d530b4ba..ad585e5e03 100644 --- a/Makefile +++ b/Makefile @@ -39,20 +39,6 @@ deps: clean build: npx @vercel/ncc@0.25.1 build $(adapter) -o $(adapter)/dist -clean-synth-index: - rm -rf synth-index/$(adapter)/dist - -build-synth-index: - cp synth-index/adapter.js synth-index/$(adapter) - npx @vercel/ncc@0.25.1 build synth-index/$(adapter) -o synth-index/$(adapter)/dist - rm synth-index/$(adapter)/adapter.js - -docker-synth-index: - docker build --no-cache --build-arg adapter=$(adapter) -f Dockerfile-SynthIndex . -t $(repo)synth-index-$(adapter)-adapter - -zip-synth-index: deps clean-synth-index build-synth-index - (cd synth-index/$(adapter)/dist && zip -r synth-index-$(adapter)-adapter.zip index.js) - clean-2-step: rm -rf 2-step/$(adapter) diff --git a/README.md b/README.md index ee5e8fd840..ecb7190edc 100644 --- a/README.md +++ b/README.md @@ -43,28 +43,23 @@ _If on a Mac, this requires `gnu-sed` to be installed and set as the default for ### Input -This is an example, a JSON body the adapter will receive when plugged into the Chainlink node pipeline: +When flux monitor or OCR jobs post to external adapters, the request body looks as follows: ```json { "id": "2cae6a10e5184aa685c3428964b02418", "data": { "from": "ETH", "to": "USD" }, "meta": { - "availableFunds": 99900000000000000000, - "eligibleToSubmit": true, "latestAnswer": 39307000000, - "oracleCount": 1, - "paymentAmount": 100000000000000000, - "reportableRoundID": 2, - "startedAt": 0, - "timeout": 0 + "updatedAt": 1616448197, } } ``` -When the FluxMonitor posts to External Adapters, it will include the `RoundState` as the "meta" field in the request, serialized to a JSON object with lower camelCase keys. +The `updatedAt` field is a unix timestamp representing when the `latestAnswer` was computed. Optionally `data` parameters can also be passed via a query string like: `{ENDPOINT}?from=ETH&to=USD` + ## Docker To build a Docker container for a specific `$adapter`, use the following example: @@ -156,7 +151,7 @@ $input.json('$') ### Install to GCP - In Functions, create a new function, choose to ZIP upload -- Select Node.js 10 for the Runtime +- Select Node.js 12 for the Runtime - Click Browse and select the `$adapter-adapter.zip` file - Select a Storage Bucket to keep the zip in - Function to execute: gcpHandler @@ -170,4 +165,4 @@ In order to use multiple API keys for an adapter, simply comma delimit the keys ``` API_KEY=myapikey1,myapikey2,myapikey3 ``` -The external adapter will then randomly rotate the keys. Over time this should balance out the number of requests between each of the API keys. \ No newline at end of file +The external adapter will then randomly rotate the keys. Over time this should balance out the number of requests between each of the API keys. diff --git a/agoric/.eslintrc.js b/agoric/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/agoric/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/agoric/README.md b/agoric/README.md new file mode 100644 index 0000000000..1246d1470e --- /dev/null +++ b/agoric/README.md @@ -0,0 +1,39 @@ +# Chainlink External Adapter for Agoric + +This adapter posts a result to the [Agoric blockchain](https://agoric.com). See +the [Agoric Chainlink Oracle +integration](https://github.com/Agoric/dapp-oracle/tree/master/chainlink-agoric) +for details on how to use it with your Chainlink node. + + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [agoric](#Agoric-endpoint) | agoric | + +--- + +## Agoric endpoint + +This is the endpoint exposed by your local `ag-solo` after installing the +[Agoric Chainlink Oracle +integration](https://github.com/Agoric/dapp-oracle/tree/master/chainlink-agoric). + +The default is http://localhost:8000/api/oracle + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `request_id` | The Agoric oracle queryId | string | request_id from Agoric External Initiator | +| | `payment` | How much $LINK the Chainlink node would like to collect as a fee | number as a string | the whole fee the user offered | +| ✅ | `result` | The result to return to the Agoric oracle contract | string | | + +## Output + +```json +{ + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { "result": "..." }, + "statusCode": 200 +} +``` diff --git a/agoric/package.json b/agoric/package.json new file mode 100644 index 0000000000..12521dd3d7 --- /dev/null +++ b/agoric/package.json @@ -0,0 +1,44 @@ +{ + "name": "@chainlink/agoric-adapter", + "version": "0.0.2", + "description": "Chainlink adapter to post to the Agoric blockchain", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + } +} diff --git a/agoric/src/adapter.ts b/agoric/src/adapter.ts new file mode 100644 index 0000000000..258f73a29e --- /dev/null +++ b/agoric/src/adapter.ts @@ -0,0 +1,117 @@ +import { BigNumber } from 'ethers' + +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' + +import { makeConfig } from './config' + +// We're on localhost, so retries just confuse the oracle state. +const NUM_RETRIES = 1 + +export interface Action { + type: string + data: unknown +} + +const inputParams = { + request_id: ['request_id'], + result: ['result'], + payment: ['payment'], +} + +// FIXME: Ideally, these would be the same. +const LINK_UNIT = BigNumber.from(10).pow(BigNumber.from(18)) +const LINK_AGORIC_UNIT = BigNumber.from(10).pow(BigNumber.from(6)) + +// Convert the payment in $LINK into Agoric's pegged $LINK token. +export const getRequiredFee = (value: string | number): number => { + const paymentCL = BigNumber.from(value) + const paymentAgoricLink = paymentCL.mul(LINK_AGORIC_UNIT).div(LINK_UNIT) + return paymentAgoricLink.toNumber() +} + +export interface PostReply { + ok: boolean + res?: unknown + rej?: unknown +} + +const executeImpl: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) { + throw validator.error + } + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const { request_id: queryId, result, payment } = validator.validated.data + const requiredFee = getRequiredFee(payment) + + const obj = { + type: 'oracleServer/reply', + data: { queryId, reply: result, requiredFee }, + } + + const response = await Requester.request( + { + ...config.api, + method: 'POST', + data: obj, + }, + undefined, + NUM_RETRIES, + ) + + const pr = response.data as PostReply + if (!pr.ok) { + throw Error(`${obj.type} response failed: ${pr.rej}`) + } + + return Requester.success(jobRunID, { + data: { result }, + result, + status: 200, + }) +} + +const tryExecuteLogError = ( + execute: ExecuteWithConfig, +): ExecuteWithConfig => async (request, config) => { + try { + return await execute(request, config) + } catch (e) { + const queryId = request.data?.request_id + const rest = { queryId } + + await Requester.request( + { + ...config.api, + method: 'POST', + data: { + type: 'oracleServer/error', + data: { error: `${(e && e.message) || e}`, ...(queryId && rest) }, + }, + }, + undefined, + NUM_RETRIES, + ).catch((e2: Error) => console.error(`Cannot reflect error to caller:`, e2)) + + // See https://github.com/smartcontractkit/external-adapters-js/issues/204 + // for discussion of why this code is necessary. + if (e instanceof AdapterError) { + throw e + } + throw new AdapterError({ + jobRunID: request.id, + statusCode: 500, + message: `${(e && e.message) || e}`, + cause: e, + }) + } +} + +export const execute = tryExecuteLogError(executeImpl) +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/agoric/src/config.ts b/agoric/src/config.ts new file mode 100644 index 0000000000..2aeeaa5985 --- /dev/null +++ b/agoric/src/config.ts @@ -0,0 +1,17 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' +import { util } from '@chainlink/ea-bootstrap' + +export const DEFAULT_API_ENDPOINT = 'http://localhost:8000/api/oracle' + +// This environment variable is needed for the Hack the Orb oracle +// instructions to remain correct. +const LEGACY_API_ENDPOINT_ENV = 'AG_SOLO_ORACLE_URL' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = + config.api.baseURL || util.getEnv(LEGACY_API_ENDPOINT_ENV) || DEFAULT_API_ENDPOINT + config.apiKey = config.apiKey || 'not required' + return config +} diff --git a/agoric/src/index.ts b/agoric/src/index.ts new file mode 100644 index 0000000000..454a553c4a --- /dev/null +++ b/agoric/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'Agoric' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/agoric/test/adapter.test.ts b/agoric/test/adapter.test.ts new file mode 100644 index 0000000000..c32582ec75 --- /dev/null +++ b/agoric/test/adapter.test.ts @@ -0,0 +1,157 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { Action, makeExecute } from '../src/adapter' +import { makeConfig } from '../src/config' + +import express from 'express' +import { Server } from 'http' + +describe('execute', () => { + const jobID = '1' + + const requests = [ + { + name: 'request_id not supplied', + status: 400, + testData: { data: { payment: '0', result: 'abc' } }, + sends: [ + { + type: 'oracleServer/error', + data: { + error: `Required parameter not supplied: request_id`, + }, + }, + ], + receive: { ok: true, res: true }, + }, + { + name: 'payment not supplied', + status: 400, + testData: { data: { request_id: '3939', result: 'abc' } }, + sends: [ + { + type: 'oracleServer/error', + data: { + queryId: '3939', + error: `Required parameter not supplied: payment`, + }, + }, + ], + receive: { ok: true, res: true }, + }, + { + name: 'push request', + status: 200, + testData: { id: jobID, data: { request_id: 'push-3', payment: '12', result: 'abc' } }, + sends: [ + { + type: 'oracleServer/reply', + data: { + queryId: 'push-3', + reply: 'abc', + requiredFee: 0, + }, + }, + ], + receive: { ok: true, res: true }, + }, + { + name: 'normal request', + status: 200, + testData: { + id: jobID, + data: { request_id: 'push-3', payment: '120000000000000', result: 'abc' }, + }, + sends: [ + { + type: 'oracleServer/reply', + data: { + queryId: 'push-3', + reply: 'abc', + requiredFee: 120, + }, + }, + ], + receive: { ok: true, res: true }, + }, + { + name: 'bad request_id', + status: 500, + testData: { + id: jobID, + data: { request_id: 'bad', payment: '120000000000000', result: 'abc' }, + }, + sends: [ + { + type: 'oracleServer/reply', + data: { + queryId: 'bad', + reply: 'abc', + requiredFee: 120, + }, + }, + { + type: 'oracleServer/error', + data: { + queryId: 'bad', + error: 'Request failed with status code 500', + }, + }, + ], + receive: { ok: false, err: 'unrecognized queryId bad' }, + }, + ] + + context('POST to localhost @integration', async () => { + let reqIndex: number + let sends: Action[] = [] + let server: Server + + const port = 18082 + process.env.AG_SOLO_ORACLE_URL = `http://localhost:${port}/api/oracle` + const execute = makeExecute(makeConfig('AGORICTEST')) + delete process.env.AG_SOLO_ORACLE_URL + + before( + () => + new Promise((resolve) => { + const app = express() + app.use(express.json()) + app.post('/api/oracle', (req, res) => { + const a = (req.body as unknown) as Action + sends.push(a) + const { queryId } = (a.data as { queryId?: string }) || {} + if (a.type === 'oracleServer/reply' && queryId === 'bad') { + res.status(500).json({ ok: false, rej: `invalid queryId ${queryId}` }) + } else { + res.status(200).json(requests[reqIndex].receive) + } + }) + + server = app.listen(port, resolve) + }), + ) + + after(() => { + server.close() + }) + + requests.forEach((req, i) => { + it(`${req.name}`, async () => { + reqIndex = i + sends = [] + + try { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: req.status, actual: data.statusCode }, data, jobID) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: req.status, actual: errorResp.statusCode }, errorResp, jobID) + } + assert.deepEqual(sends, req.sends) + }) + }) + }) +}) diff --git a/agoric/tsconfig.json b/agoric/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/agoric/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/alphachain/.eslintrc.js b/alphachain/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/alphachain/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/alphachain/README.md b/alphachain/README.md index acbdbbca00..3fa0a76f8d 100644 --- a/alphachain/README.md +++ b/alphachain/README.md @@ -1,11 +1,46 @@ # Chainlink External Adapter for AlphaChain (SDR) -## Input Params +### Environment Variables -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-----------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from the AlphaChain dashboard | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :-------------------------------: | :---------: | +| | endpoint | The endpoint to use | [dataquery](#Data-Query-Endpoint) | dataquery | + +--- + +## Data Query Endpoint + +Retrieves price data for a given currency pair + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| | `field` | The object path to access the value that will be returned as the result | | `result` | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "ETH", + "quote": "SDR" + } +} +``` + +### Sample Output ```json { diff --git a/alphachain/adapter.js b/alphachain/adapter.js deleted file mode 100644 index 02fccb0e2b..0000000000 --- a/alphachain/adapter.js +++ /dev/null @@ -1,50 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customError = (data) => { - if (data.status !== '200') return true - return false -} - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], -} - -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 url = 'https://alpha-chain2.p.rapidapi.com/data-query' - const host = 'alpha-chain2.p.rapidapi.com' - const headers = { - 'content-type': 'application/octet-stream', - 'x-rapidapi-host': host, - 'x-rapidapi-key': util.getRandomRequiredEnv('API_KEY'), - useQueryString: true, - } - const base = validator.validated.data.base.toUpperCase() - const quote = validator.validated.data.quote.toUpperCase() - - const params = { - from_symbol: base, - to_symbol: quote, - chainlink_node: true, - } - - const config = { - url, - params, - headers, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['result']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/alphachain/index.js b/alphachain/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/alphachain/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/alphachain/package.json b/alphachain/package.json index 0a6a53a776..3c7bbd5885 100644 --- a/alphachain/package.json +++ b/alphachain/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/alphachain-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink alphachain adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/alphachain/src/adapter.ts b/alphachain/src/adapter.ts new file mode 100644 index 0000000000..fd8e7801e9 --- /dev/null +++ b/alphachain/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { dataQuery } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case dataQuery.NAME: { + return await dataQuery.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/alphachain/src/config.ts b/alphachain/src/config.ts new file mode 100644 index 0000000000..1cf40221b2 --- /dev/null +++ b/alphachain/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'dataquery' +export const DEFAULT_BASE_URL = 'https://alpha-chain2.p.rapidapi.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/alphachain/src/endpoint/data-query.ts b/alphachain/src/endpoint/data-query.ts new file mode 100644 index 0000000000..f52cd91914 --- /dev/null +++ b/alphachain/src/endpoint/data-query.ts @@ -0,0 +1,52 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'dataquery' + +const customError = (data: any) => data.status !== '200' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const base = validator.validated.data.base.toUpperCase() + const quote = validator.validated.data.quote.toUpperCase() + const field = validator.validated.data.field || 'result' + const url = '/data-query' + const host = 'alpha-chain2.p.rapidapi.com' + const headers = { + 'content-type': 'application/octet-stream', + 'x-rapidapi-host': host, + 'x-rapidapi-key': config.apiKey, + useQueryString: true, + } + + const params = { + from_symbol: base, + to_symbol: quote, + chainlink_node: true, + } + + const options = { + ...config.api, + url, + params, + headers, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/alphachain/src/endpoint/index.ts b/alphachain/src/endpoint/index.ts new file mode 100644 index 0000000000..15ae4d77a9 --- /dev/null +++ b/alphachain/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as dataQuery from './data-query' diff --git a/alphachain/src/index.ts b/alphachain/src/index.ts new file mode 100644 index 0000000000..5768887efb --- /dev/null +++ b/alphachain/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ALPHACHAIN' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/alphachain/test/adapter_test.js b/alphachain/test/data-query.test.ts similarity index 54% rename from alphachain/test/adapter_test.js rename to alphachain/test/data-query.test.ts index 8737d5f5a6..4a5cee2a76 100644 --- a/alphachain/test/adapter_test.js +++ b/alphachain/test/data-query.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -26,13 +30,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) }) }) }) @@ -52,11 +54,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) + } }) }) }) @@ -74,11 +78,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/alphachain/tsconfig.json b/alphachain/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/alphachain/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/alphavantage-sdr/.eslintrc.js b/alphavantage-sdr/.eslintrc.js deleted file mode 100644 index 7d045a59de..0000000000 --- a/alphavantage-sdr/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../.eslintrc.js') diff --git a/alphavantage-sdr/test/adapter_test.js b/alphavantage-sdr/test/adapter_test.js deleted file mode 100644 index 8737d5f5a6..0000000000 --- a/alphavantage-sdr/test/adapter_test.js +++ /dev/null @@ -1,85 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { base: 'ETH', quote: 'XDR' } }, - }, - { - name: 'base/quote', - testData: { id: jobID, data: { base: 'ETH', quote: 'XDR' } }, - }, - { - name: 'from/to', - testData: { id: jobID, data: { from: 'ETH', to: 'XDR' } }, - }, - { - name: 'coin/market', - testData: { id: jobID, data: { coin: 'ETH', market: 'XDR' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - { - name: 'base not supplied', - testData: { id: jobID, data: { quote: 'XDR' } }, - }, - { - name: 'quote not supplied', - testData: { id: jobID, data: { base: 'ETH' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real', quote: 'XDR' } }, - }, - { - name: 'unknown quote', - testData: { id: jobID, data: { base: 'ETH', quote: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/alphavantage/.eslintrc.js b/alphavantage/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/alphavantage/.eslintrc.js +++ b/alphavantage/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/alphavantage/README.md b/alphavantage/README.md index 2f2a7aa47c..1ba93fe544 100644 --- a/alphavantage/README.md +++ b/alphavantage/README.md @@ -1,33 +1,55 @@ # Chainlink External Adapter for AlphaVantage -Use this adapter for connecting to AlphaVantage's API from a Chainlink node. +Use this adapter for connecting to [AlphaVantage's API](https://www.alphavantage.co/documentation/) from a Chainlink node. -## Input params +### Environment Variables -- `function`: (Optional) The function to call (defaults to CURRENCY_EXCHANGE_RATE) -- `base`, `from`, or `coin`: The asset to query -- `quote`, `to`, or `market`: The currency to convert to +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-----------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://www.alphavantage.co/support/#api-key) | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +Returns the exchange rate from a currency's current price to a given currency + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | [Physical Currency list](https://www.alphavantage.co/physical_currency_list/) or [Cryptocurrency list](https://www.alphavantage.co/digital_currency_list/) | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | [Physical Currency list](https://www.alphavantage.co/physical_currency_list/) or [Cryptocurrency list](https://www.alphavantage.co/digital_currency_list/) | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { - "jobRunID": "1", - "data": { - "Realtime Currency Exchange Rate": { - "1. From_Currency Code": "ETH", - "2. From_Currency Name": "Ethereum", - "3. To_Currency Code": "USD", - "4. To_Currency Name": "United States Dollar", - "5. Exchange Rate": "170.88000000", - "6. Last Refreshed": "2020-04-16 19:15:01", - "7. Time Zone": "UTC", - "8. Bid Price": "170.84000000", - "9. Ask Price": "170.88000000" + "jobRunID": "1", + "data": { + "Realtime Currency Exchange Rate": { + "1. From_Currency Code": "ETH", + "2. From_Currency Name": "Ethereum", + "3. To_Currency Code": "USD", + "4. To_Currency Name": "United States Dollar", + "5. Exchange Rate": "170.88000000", + "6. Last Refreshed": "2020-04-16 19:15:01", + "7. Time Zone": "UTC", + "8. Bid Price": "170.84000000", + "9. Ask Price": "170.88000000" + }, + "result": 170.88 }, - "result": 170.88 - }, - "result": 170.88, - "statusCode": 200 + "result": 170.88, + "statusCode": 200 } ``` diff --git a/alphavantage/adapter.js b/alphavantage/adapter.js deleted file mode 100644 index b5baaf6372..0000000000 --- a/alphavantage/adapter.js +++ /dev/null @@ -1,53 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customError = (data) => { - if (data['Error Message']) return true - return false -} - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - function: false, -} - -const execute = (input, callback) => { - const validator = new Validator(input, customParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const url = 'https://www.alphavantage.co/query' - const jobRunID = validator.validated.id - const func = validator.validated.data.function || 'CURRENCY_EXCHANGE_RATE' - const from = validator.validated.data.base - const to = validator.validated.data.quote - - const params = { - function: func, - from_currency: from, - to_currency: to, - from_symbol: from, - to_symbol: to, - symbol: from, - market: to, - apikey: util.getRandomRequiredEnv('API_KEY'), - } - - const config = { - url, - params, - } - Requester.request(config, customError) - .then((response) => { - response.data.result = JSON.parse( - Requester.validateResultNumber(response.data, [ - 'Realtime Currency Exchange Rate', - '5. Exchange Rate', - ]), - ) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/alphavantage/index.js b/alphavantage/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/alphavantage/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/alphavantage/package.json b/alphavantage/package.json index 4937603aec..bde3b01e27 100644 --- a/alphavantage/package.json +++ b/alphavantage/package.json @@ -1,15 +1,47 @@ { "name": "@chainlink/alphavantage-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink example adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "alphavantage", + "adapter" + ], "license": "MIT", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 7000 -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 7000 --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/alphavantage/src/adapter.ts b/alphavantage/src/adapter.ts new file mode 100644 index 0000000000..e7c19bba45 --- /dev/null +++ b/alphavantage/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/alphavantage/src/config.ts b/alphavantage/src/config.ts new file mode 100644 index 0000000000..73100052b6 --- /dev/null +++ b/alphavantage/src/config.ts @@ -0,0 +1,19 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'ALPHAVANTAGE' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://www.alphavantage.co/query' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + apikey: config.apiKey, + }, + } + return config +} diff --git a/alphavantage/src/endpoint/index.ts b/alphavantage/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/alphavantage/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/alphavantage/src/endpoint/price.ts b/alphavantage/src/endpoint/price.ts new file mode 100644 index 0000000000..e653829427 --- /dev/null +++ b/alphavantage/src/endpoint/price.ts @@ -0,0 +1,52 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const customError = (data: any) => { + if (data['Error Message']) return true + return false +} + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const from = validator.overrideSymbol(AdapterName) + const to = validator.validated.data.quote + + const params = { + ...config.api.params, + function: 'CURRENCY_EXCHANGE_RATE', + from_currency: from, + to_currency: to, + from_symbol: from, + to_symbol: to, + symbol: from, + market: to, + } + + const options = { + ...config.api, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [ + 'Realtime Currency Exchange Rate', + '5. Exchange Rate', + ]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/alphavantage/src/index.ts b/alphavantage/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/alphavantage/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/alphavantage/test/adapter.test.ts b/alphavantage/test/adapter.test.ts new file mode 100644 index 0000000000..780ec333f8 --- /dev/null +++ b/alphavantage/test/adapter.test.ts @@ -0,0 +1,91 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { base: 'ETH', quote: 'USD' } }, + }, + { + name: 'base/quote', + testData: { id: jobID, data: { base: 'ETH', quote: 'USD' } }, + }, + { + name: 'from/to', + testData: { id: jobID, data: { from: 'ETH', to: 'USD' } }, + }, + { + name: 'coin/market', + testData: { id: jobID, data: { coin: 'ETH', market: 'XDR' } }, + }, + ] + + 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: {} } }, + { + name: 'base not supplied', + testData: { id: jobID, data: { quote: 'USD' } }, + }, + { + name: 'quote not supplied', + testData: { id: jobID, data: { base: 'ETH' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { base: 'not_real', quote: 'USD' } }, + }, + { + name: 'unknown quote', + testData: { id: jobID, data: { base: 'ETH', quote: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/alphavantage/tsconfig.json b/alphavantage/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/alphavantage/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/amberdata-gasprice/.eslintrc.js b/amberdata-gasprice/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/amberdata-gasprice/.eslintrc.js +++ b/amberdata-gasprice/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/amberdata-gasprice/README.md b/amberdata-gasprice/README.md index cc788ad865..775d26d311 100644 --- a/amberdata-gasprice/README.md +++ b/amberdata-gasprice/README.md @@ -1,15 +1,23 @@ # Chainlink External Adapter for Amberdata gas price -## Input Params +### Environment Variables -- `speed`: The speed for gas price to get. Available choices: - - `safeLow` - - `average` (default) - - `fast` - - `fastest` -- `endpoint`: The blockchain id to get gas prices from (optional, default: ethereum-mainnet) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-----------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://amberdata.io/user/api-keys) | | | -## Output Format +--- + +## Gas Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------: | :--------------------------------------: | :----------------------------------: | :----------------: | +| 🟡 | `speed` | The desired speed | `safeLow`,`average`,`fast`,`fastest` | `average` | +| 🟡 | `endpoint` | The blockchain id to get gas prices from | | `ethereum-mainnet` | + +## Output ```json { diff --git a/amberdata-gasprice/adapter.js b/amberdata-gasprice/adapter.js deleted file mode 100644 index bd94362620..0000000000 --- a/amberdata-gasprice/adapter.js +++ /dev/null @@ -1,42 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customError = (data) => { - return Object.keys(data.payload).length < 1 -} - -const customParams = { - speed: false, - endpoint: false, -} - -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 speed = validator.validated.data.speed || 'average' - const endpoint = validator.validated.data.endpoint || 'ethereum-mainnet' - const url = 'https://web3api.io/api/v2/transactions/gas/predictions' - - const config = { - url, - headers: { - 'x-api-key': util.getRandomRequiredEnv('API_KEY'), - 'x-amberdata-blockchain-id': endpoint, - }, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'payload', - speed, - 'gasPrice', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/amberdata-gasprice/index.js b/amberdata-gasprice/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/amberdata-gasprice/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/amberdata-gasprice/package.json b/amberdata-gasprice/package.json index b79b89714d..b04ef5a189 100644 --- a/amberdata-gasprice/package.json +++ b/amberdata-gasprice/package.json @@ -1,15 +1,44 @@ { "name": "@chainlink/amberdata-gasprice-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 4000 -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 4000 --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/amberdata-gasprice/src/adapter.ts b/amberdata-gasprice/src/adapter.ts new file mode 100644 index 0000000000..15326c2e17 --- /dev/null +++ b/amberdata-gasprice/src/adapter.ts @@ -0,0 +1,13 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig } from './config' +import { gasprice } from './endpoint' + +export const execute: ExecuteWithConfig = async (request, config) => { + Requester.logConfig(config) + return await gasprice.execute(request, config) +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/amberdata-gasprice/src/config.ts b/amberdata-gasprice/src/config.ts new file mode 100644 index 0000000000..df5133d1b4 --- /dev/null +++ b/amberdata-gasprice/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_API_ENDPOINT = 'https://web3api.io' + +export const makeConfig = (prefix = ''): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api.headers['x-api-key'] = config.apiKey + config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT + return config +} diff --git a/amberdata-gasprice/src/endpoint/gasprice.ts b/amberdata-gasprice/src/endpoint/gasprice.ts new file mode 100644 index 0000000000..61da056c48 --- /dev/null +++ b/amberdata-gasprice/src/endpoint/gasprice.ts @@ -0,0 +1,41 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'gasprice' + +const customError = (data: any) => { + return Object.keys(data.payload).length < 1 +} + +const customParams = { + speed: false, + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const speed = validator.validated.data.speed || 'average' + const endpoint = validator.validated.data.endpoint || 'ethereum-mainnet' + const url = '/api/v2/transactions/gas/predictions' + + const options = { + ...config.api, + url, + headers: { + ...config.api.headers, + 'x-amberdata-blockchain-id': endpoint, + }, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['payload', speed, 'gasPrice']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/amberdata-gasprice/src/endpoint/index.ts b/amberdata-gasprice/src/endpoint/index.ts new file mode 100644 index 0000000000..42f92d14ac --- /dev/null +++ b/amberdata-gasprice/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as gasprice from './gasprice' diff --git a/amberdata-gasprice/src/index.ts b/amberdata-gasprice/src/index.ts new file mode 100644 index 0000000000..69835af6c1 --- /dev/null +++ b/amberdata-gasprice/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'AMBERDATA_GASPRICE' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/amberdata-gasprice/test/adapter_test.js b/amberdata-gasprice/test/gasprice.test.ts similarity index 51% rename from amberdata-gasprice/test/adapter_test.js rename to amberdata-gasprice/test/gasprice.test.ts index c3c2474c84..a8390dfda8 100644 --- a/amberdata-gasprice/test/adapter_test.js +++ b/amberdata-gasprice/test/gasprice.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -15,7 +19,7 @@ describe('execute', () => { name: 'no speed param', testData: { id: jobID, - data: { endpoint: 'not_real' }, + data: { endpoint: 'ethereum-mainnet' }, }, }, { @@ -37,13 +41,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -57,11 +59,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) + } }) }) }) @@ -85,11 +89,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/amberdata-gasprice/tsconfig.json b/amberdata-gasprice/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/amberdata-gasprice/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/amberdata/README.md b/amberdata/README.md index 93b46e4832..2ec94bbe7f 100644 --- a/amberdata/README.md +++ b/amberdata/README.md @@ -7,20 +7,25 @@ The adapter takes the following environment variables: - `API_KEY`: Optional API key to use - `API_TIMEOUT`: Optional timeout param, defaults to `30000` -## Input Params +### Input Parameters -- `endpoint`: The endpoint to use, one of (price|balance). Default: "price" +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint), [balance](#Balance-Endpoint) | price | -### Price endpoint +--- + +## Price Endpoint Gets the [latest spot VWAP price](https://docs.amberdata.io/reference#spot-price-pair-latest) from Amberdata. -## Input Params +### Input Params - `base`, `from`, or `coin`: The asset to query - `quote`, `to`, or `market`: The currency to convert to +- `overrides`: (not required) If base provided is found in overrides, that will be used. [Format](../external-adapter/src/overrides/presetSymbols.json) -## Output +### Output ```json { @@ -42,7 +47,9 @@ Gets the [latest spot VWAP price](https://docs.amberdata.io/reference#spot-price } ``` -### Balance endpoint +## Balance Endpoint + +### Input Params - `dataPath`: Optional path where to find the addresses array, defaults to `result` - `confirmations`: Optional confirmations param, defaults to `6` diff --git a/amberdata/package.json b/amberdata/package.json index cb91b8d6bf..fb705f6190 100644 --- a/amberdata/package.json +++ b/amberdata/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/amberdata-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "main": "dist/index.js", "scripts": { diff --git a/amberdata/src/config.ts b/amberdata/src/config.ts index 9be3cf6fa5..e0a20f3980 100644 --- a/amberdata/src/config.ts +++ b/amberdata/src/config.ts @@ -1,12 +1,13 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' -export const DEFAULT_API_ENDPOINT = 'https://web3api.io' +export const NAME = 'AMBERDATA' +export const DEFAULT_API_ENDPOINT = 'https://web3api.io' export const DEFAULT_ENDPOINT = 'price' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.headers['x-api-key'] = config.apiKey config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT return config diff --git a/amberdata/src/endpoint/price.ts b/amberdata/src/endpoint/price.ts index 799918a646..2b30ff3cbb 100644 --- a/amberdata/src/endpoint/price.ts +++ b/amberdata/src/endpoint/price.ts @@ -1,5 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' export const Name = 'price' @@ -7,9 +8,17 @@ const customError = (data: any) => { return Object.keys(data.payload).length === 0 } +const addressMapping: { [symbol: string]: string } = { + DIGG: '0x798d1be841a82a273720ce31c822c61a67a601c3', + WBTC: '0x2260fac5e5542a773aa44fbcfedf7c193bc2c599', + RAI: '0x03ab458634910aad20ef5f1c8ee96f1d6ac54919', + WETH: '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', +} + const customParams = { base: ['base', 'from', 'coin'], quote: ['quote', 'to', 'market'], + includes: false, } export const execute: ExecuteWithConfig = async (input, config) => { @@ -17,14 +26,26 @@ export const execute: ExecuteWithConfig = async (input, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id - const coin = validator.validated.data.base + const coin = validator.overrideSymbol(AdapterName) const market = validator.validated.data.quote - const url = `/api/v2/market/spot/prices/pairs/${coin.toLowerCase()}_${market.toLowerCase()}/latest` + const includes = validator.validated.data.includes || [] - const params = { + let url = `/api/v2/market/spot/prices/pairs/${coin.toLowerCase()}_${market.toLowerCase()}/latest` + let params: { [key: string]: any } = { includeCrossRates: true, } + if ( + includes.length > 0 && + ((includes[0].toLowerCase() === 'wbtc' && coin.toLowerCase() === 'digg') || + (includes[0].toLowerCase() === 'weth' && coin.toLowerCase() === 'rai')) + ) { + const fromAddress = addressMapping[coin.toUpperCase()] + const toAddress = addressMapping[includes[0].toUpperCase()] + url = `/api/v2/market/defi/prices/pairs/bases/${fromAddress}/quotes/${toAddress}/latest` + params = {} + } + const reqConfig = { ...config.api, params, url } const response = await Requester.request(reqConfig, customError) diff --git a/amberdata/src/index.ts b/amberdata/src/index.ts index 2f68af8a67..21ea85958f 100644 --- a/amberdata/src/index.ts +++ b/amberdata/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'AMBERDATA' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/amberdata/test/balance.test.ts b/amberdata/test/balance.test.ts index 307a348ee0..b8ad88e441 100644 --- a/amberdata/test/balance.test.ts +++ b/amberdata/test/balance.test.ts @@ -1,6 +1,8 @@ import { makeExecute } from '../src/adapter' import { shouldBehaveLikeBalanceAdapter } from '@chainlink/adapter-test-helpers' +process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + shouldBehaveLikeBalanceAdapter(makeExecute(), [ 'bitcoin_mainnet', 'ethereum_mainnet', diff --git a/amberdata/test/price.test.ts b/amberdata/test/price.test.ts index 4557f26728..15f43eb974 100644 --- a/amberdata/test/price.test.ts +++ b/amberdata/test/price.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('price endpoint', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ diff --git a/anyblock-gasprice/.eslintrc.js b/anyblock-gasprice/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/anyblock-gasprice/.eslintrc.js +++ b/anyblock-gasprice/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/anyblock-gasprice/README.md b/anyblock-gasprice/README.md index 58c5519bcb..8800d8cf33 100644 --- a/anyblock-gasprice/README.md +++ b/anyblock-gasprice/README.md @@ -1,15 +1,15 @@ # Chainlink External Adapter for Anyblock Analytics Gas Price -## Input Params +## Gas Price Endpoint -- `speed`: The speed for gas price to get. Available choices: - - `slow` - - `standard` (default) - - `fast` - - `instant` -- `endpoint`: The endpoint to use (optional, default: latest-minimum-gasprice) +### Input Params -## Output Format +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------: | :---------------: | :--------------------------------: | :-----------------------: | +| 🟡 | `speed` | The desired speed | `slow`,`standard`,`fast`,`instant` | `standard` | +| 🟡 | `endpoint` | | | `latest-minimum-gasprice` | + +## Output ```json { diff --git a/anyblock-gasprice/adapter.js b/anyblock-gasprice/adapter.js deleted file mode 100644 index 3706e786a3..0000000000 --- a/anyblock-gasprice/adapter.js +++ /dev/null @@ -1,31 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customError = (data) => { - if (Object.keys(data).length < 1) return true - if (!('health' in data) || !data.health) return true - return false -} - -const customParams = { - speed: false, - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'latest-minimum-gasprice' - const speed = validator.validated.data.speed || 'standard' - const url = `https://api.anyblock.tools/${endpoint}` - - Requester.request(url, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [speed]) * 1e9 - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/anyblock-gasprice/index.js b/anyblock-gasprice/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/anyblock-gasprice/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/anyblock-gasprice/package.json b/anyblock-gasprice/package.json index 5acc42486e..fc8198f9c9 100644 --- a/anyblock-gasprice/package.json +++ b/anyblock-gasprice/package.json @@ -1,15 +1,44 @@ { "name": "@chainlink/anyblock-gasprice-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 4000 -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 4000 --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/anyblock-gasprice/src/adapter.ts b/anyblock-gasprice/src/adapter.ts new file mode 100644 index 0000000000..15326c2e17 --- /dev/null +++ b/anyblock-gasprice/src/adapter.ts @@ -0,0 +1,13 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig } from './config' +import { gasprice } from './endpoint' + +export const execute: ExecuteWithConfig = async (request, config) => { + Requester.logConfig(config) + return await gasprice.execute(request, config) +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/anyblock-gasprice/src/config.ts b/anyblock-gasprice/src/config.ts new file mode 100644 index 0000000000..1a79873b95 --- /dev/null +++ b/anyblock-gasprice/src/config.ts @@ -0,0 +1,10 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_API_ENDPOINT = 'https://api.anyblock.tools' + +export const makeConfig = (prefix = ''): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT + return config +} diff --git a/anyblock-gasprice/src/endpoint/gasprice.ts b/anyblock-gasprice/src/endpoint/gasprice.ts new file mode 100644 index 0000000000..1143f405eb --- /dev/null +++ b/anyblock-gasprice/src/endpoint/gasprice.ts @@ -0,0 +1,39 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'gasprice' + +const customError = (data: any) => { + if (Object.keys(data).length < 1) return true + if (!('health' in data) || !data.health) return true + return false +} + +const customParams = { + speed: false, + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || 'latest-minimum-gasprice' + const speed = validator.validated.data.speed || 'standard' + const url = `/${endpoint}` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [speed]) * 1e9 + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/anyblock-gasprice/src/endpoint/index.ts b/anyblock-gasprice/src/endpoint/index.ts new file mode 100644 index 0000000000..42f92d14ac --- /dev/null +++ b/anyblock-gasprice/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as gasprice from './gasprice' diff --git a/anyblock-gasprice/src/index.ts b/anyblock-gasprice/src/index.ts new file mode 100644 index 0000000000..788228f1e0 --- /dev/null +++ b/anyblock-gasprice/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ANYBLOCK_GASPRICE' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/anyblock-gasprice/test/adapter_test.js b/anyblock-gasprice/test/gasprice.test.ts similarity index 52% rename from anyblock-gasprice/test/adapter_test.js rename to anyblock-gasprice/test/gasprice.test.ts index 334ce399b9..817f31f7ea 100644 --- a/anyblock-gasprice/test/adapter_test.js +++ b/anyblock-gasprice/test/gasprice.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 = [ @@ -15,7 +18,7 @@ describe('execute', () => { name: 'no speed param', testData: { id: jobID, - data: { endpoint: 'not_real' }, + data: { endpoint: 'latest-minimum-gasprice' }, }, }, { @@ -37,13 +40,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -57,11 +58,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) + } }) }) }) @@ -69,27 +72,29 @@ describe('execute', () => { context('error calls @integration', () => { const requests = [ { - name: 'unknown endpoint', + name: 'unknown speed', testData: { id: jobID, - data: { speed: 'standard', endpoint: 'not_real' }, + data: { speed: 'not_real' }, }, }, { - name: 'unknown speed', + name: 'unknown endpoint', testData: { id: jobID, - data: { speed: 'not_real' }, + data: { speed: 'standard', endpoint: 'not_real' }, }, }, ] 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/anyblock-gasprice/tsconfig.json b/anyblock-gasprice/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/anyblock-gasprice/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/anyblock-uniswap-vwap/package.json b/anyblock-uniswap-vwap/package.json index 21aed60b43..17963a0c72 100644 --- a/anyblock-uniswap-vwap/package.json +++ b/anyblock-uniswap-vwap/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/anyblock-uniswap-vwap-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter using Anyblock Analytics service to calculate the volume weighted average price (VWAP) for any Uniswap asset.", "keywords": [ "Chainlink", diff --git a/anyblock-uniswap-vwap/src/index.ts b/anyblock-uniswap-vwap/src/index.ts index b725a7448d..8d54a6c4c0 100644 --- a/anyblock-uniswap-vwap/src/index.ts +++ b/anyblock-uniswap-vwap/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'ANYBLOCK_UNISWAP_VWAP' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/binance-dex/.eslintrc.js b/binance-dex/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/binance-dex/.eslintrc.js +++ b/binance-dex/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/binance-dex/README.md b/binance-dex/README.md index 8cfcf0fc57..19f8f492c9 100644 --- a/binance-dex/README.md +++ b/binance-dex/README.md @@ -1,9 +1,13 @@ # Chainlink External Adapter for Binance DEX -## Input Params +### Input Params + +The following `base` and `quote` pair must be taken from [this list](https://dex.binance.org/api/v1/markets) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | `BTC`, `ETH`, `USD` | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | `BTC`, `ETH`, `USD` | | -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to - `endpoint`: The path to get data from (optional) ### Endpoints @@ -23,34 +27,34 @@ This will default to `dex-asiapacific`. ```json { - "jobRunID":"1", - "data":{ - "symbol":"BNB_BUSD-BD1", - "baseAssetName":"BNB", - "quoteAssetName":"BUSD-BD1", - "priceChange":"-0.67720000", - "priceChangePercent":"-4.1300", - "prevClosePrice":"15.79610000", - "lastPrice":"15.71260000", - "lastQuantity":"5.59400000", - "openPrice":"16.38980000", - "highPrice":"16.39630000", - "lowPrice":"15.71260000", - "openTime":1592472051001, - "closeTime":1592558451001, - "firstId":"95085363-0", - "lastId":"95288334-2", - "bidPrice":"16.00820000", - "bidQuantity":"7.91500000", - "askPrice":"16.13910000", - "askQuantity":"189.09900000", - "weightedAvgPrice":"16.25508542", - "volume":"1941.24500000", - "quoteVolume":"31555.10329880", - "count":163, - "result":15.7126 - }, - "result":15.7126, - "statusCode":200 + "jobRunID": "1", + "data": { + "symbol": "BNB_BUSD-BD1", + "baseAssetName": "BNB", + "quoteAssetName": "BUSD-BD1", + "priceChange": "-0.67720000", + "priceChangePercent": "-4.1300", + "prevClosePrice": "15.79610000", + "lastPrice": "15.71260000", + "lastQuantity": "5.59400000", + "openPrice": "16.38980000", + "highPrice": "16.39630000", + "lowPrice": "15.71260000", + "openTime": 1592472051001, + "closeTime": 1592558451001, + "firstId": "95085363-0", + "lastId": "95288334-2", + "bidPrice": "16.00820000", + "bidQuantity": "7.91500000", + "askPrice": "16.13910000", + "askQuantity": "189.09900000", + "weightedAvgPrice": "16.25508542", + "volume": "1941.24500000", + "quoteVolume": "31555.10329880", + "count": 163, + "result": 15.7126 + }, + "result": 15.7126, + "statusCode": 200 } ``` diff --git a/binance-dex/adapter.js b/binance-dex/adapter.js deleted file mode 100644 index 0b130ca6a3..0000000000 --- a/binance-dex/adapter.js +++ /dev/null @@ -1,57 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const DEFAULT_API_ENDPOINT = 'dex-asiapacific' -const DEFAULT_DATA_ENDPOINT = 'v1/ticker/24hr' -const apiEndpoint = process.env.API_ENDPOINT || DEFAULT_API_ENDPOINT - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_DATA_ENDPOINT - const url = `https://${apiEndpoint}.binance.org/api/${endpoint}` - const base = validator.validated.data.base.toUpperCase() - const quote = validator.validated.data.quote.toUpperCase() - const symbol = `${base}_${quote}` - - const _handleResponse = (response) => { - if (response.data.length < 1) { - return callback(500, Requester.errored(jobRunID, 'no result for query')) - } - - // Replace array by the first object in array - // to avoid unexpected behavior when returning arrays. - response.data = response.data[0] - - const lastUpdate = response.data.closeTime - const curTime = new Date() - // If data is older than 10 minutes, discard it - if (lastUpdate < curTime.setMinutes(curTime.getMinutes() - 10)) { - return callback(500, Requester.errored(jobRunID, 'data is too old')) - } - - response.data.result = Requester.validateResultNumber(response.data, ['lastPrice']) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - const params = { - symbol, - } - - const config = { - url, - params, - } - - Requester.request(config).then(_handleResponse).catch(_handleError) -} - -module.exports.execute = execute diff --git a/binance-dex/index.js b/binance-dex/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/binance-dex/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/binance-dex/package.json b/binance-dex/package.json index 9f595e1ca9..ee13dfff51 100644 --- a/binance-dex/package.json +++ b/binance-dex/package.json @@ -1,15 +1,44 @@ { "name": "@chainlink/binance-dex-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 5000 -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 5000 --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/binance-dex/src/adapter.ts b/binance-dex/src/adapter.ts new file mode 100644 index 0000000000..dac083b012 --- /dev/null +++ b/binance-dex/src/adapter.ts @@ -0,0 +1,13 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig } from './config' +import { price } from './endpoint' + +export const execute: ExecuteWithConfig = async (request, config) => { + Requester.logConfig(config) + return await price.execute(request, config) +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/binance-dex/src/config.ts b/binance-dex/src/config.ts new file mode 100644 index 0000000000..125e6e4b8a --- /dev/null +++ b/binance-dex/src/config.ts @@ -0,0 +1,14 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_API_ENDPOINT = 'dex-asiapacific' +export const DEFAULT_DATA_ENDPOINT = 'v1/ticker/24hr' + +// TODO: this usage of the process.env.API_ENDPOINT differs from most other adapters and should be changed +const getBaseURL = (region: string) => `https://${region}.binance.org` + +export const makeConfig = (prefix = ''): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = getBaseURL(config.api.baseURL || DEFAULT_API_ENDPOINT) + return config +} diff --git a/binance-dex/src/endpoint/index.ts b/binance-dex/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/binance-dex/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/binance-dex/src/endpoint/price.ts b/binance-dex/src/endpoint/price.ts new file mode 100644 index 0000000000..14a3b67b20 --- /dev/null +++ b/binance-dex/src/endpoint/price.ts @@ -0,0 +1,59 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { DEFAULT_DATA_ENDPOINT } from '../config' + +export const NAME = 'price' + +const customError = (data: any) => data.length < 1 + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_DATA_ENDPOINT + const url = `/api/${endpoint}` + const base = validator.validated.data.base.toUpperCase() + const quote = validator.validated.data.quote.toUpperCase() + const symbol = `${base}_${quote}` + + const params = { + symbol, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + + // Replace array by the first object in array + // to avoid unexpected behavior when returning arrays. + response.data = response.data[0] + + const lastUpdate = response.data.closeTime + const curTime = new Date() + // If data is older than 10 minutes, discard it + if (lastUpdate < curTime.setMinutes(curTime.getMinutes() - 10)) + throw new AdapterError({ + jobRunID, + message: `Data is too old`, + statusCode: 500, + }) + + const result = Requester.validateResultNumber(response.data, ['lastPrice']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/binance-dex/src/index.ts b/binance-dex/src/index.ts new file mode 100644 index 0000000000..75befd89dc --- /dev/null +++ b/binance-dex/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'BINANCE_DEX' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/binance-dex/test/adapter_test.js b/binance-dex/test/price.test.ts similarity index 64% rename from binance-dex/test/adapter_test.js rename to binance-dex/test/price.test.ts index 2b4938b5a4..0a80c5c3ac 100644 --- a/binance-dex/test/adapter_test.js +++ b/binance-dex/test/price.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', () => { +describe('price endpoint', () => { const jobID = '1' + const execute = makeExecute() context('successful calls @integration', () => { const requests = [ @@ -49,13 +52,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) }) }) }) @@ -87,11 +88,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) + } }) }) }) @@ -132,11 +135,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/binance-dex/tsconfig.json b/binance-dex/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/binance-dex/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/bitex/.eslintrc.js b/bitex/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/bitex/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/bitex/README.md b/bitex/README.md index fb3e41cd44..b53753dc2b 100644 --- a/bitex/README.md +++ b/bitex/README.md @@ -1,35 +1,59 @@ # Chainlink External Adapter for Bitex -## Input Params +### Input Parameters -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param (default: "tickers") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [tickers](#Tickers-Endpoint) | tickers | -## Output +--- + +## Tickers Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :---------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | | | +| | `field` | The object path to access the value that will be returned as the result | | `vwap` | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "ETH", + "quote": "ARS" + } +} +``` + +### Sample Output ```json { - "jobRunID":"1", - "data":{ - "data":{ - "id":"btc_ars", - "type":"tickers", - "attributes":{ - "last":1625000, - "open":1592499, - "high":1637900, - "low":1572000, - "vwap":1613873.6183712287, - "volume":2.51892688, - "bid":1585000, - "ask":1631800, - "price_before_last":1605000 - } - }, - "result":1625000 + "jobRunID": "1", + "data": { + "data": { + "id": "btc_ars", + "type": "tickers", + "attributes": { + "last": 1625000, + "open": 1592499, + "high": 1637900, + "low": 1572000, + "vwap": 1613873.6183712287, + "volume": 2.51892688, + "bid": 1585000, + "ask": 1631800, + "price_before_last": 1605000 + } }, - "result":1625000, - "statusCode":200 + "result": 1625000 + }, + "result": 1625000, + "statusCode": 200 } ``` diff --git a/bitex/adapter.js b/bitex/adapter.js deleted file mode 100644 index fc97bdadf7..0000000000 --- a/bitex/adapter.js +++ /dev/null @@ -1,35 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'tickers' - const base = validator.validated.data.base.toLowerCase() - const quote = validator.validated.data.quote.toLowerCase() - const url = `https://bitex.la/api/${endpoint}/${base}_${quote}` - - const config = { - url, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - 'attributes', - 'vwap', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/bitex/index.js b/bitex/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/bitex/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/bitex/package.json b/bitex/package.json index 624cad7ab0..36470ad716 100644 --- a/bitex/package.json +++ b/bitex/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/bitex-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Bitex adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 5000 -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 5000 --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/bitex/src/adapter.ts b/bitex/src/adapter.ts new file mode 100644 index 0000000000..2224972df1 --- /dev/null +++ b/bitex/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { tickers } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case tickers.NAME: { + return await tickers.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/bitex/src/config.ts b/bitex/src/config.ts new file mode 100644 index 0000000000..ce80264f62 --- /dev/null +++ b/bitex/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'tickers' +export const DEFAULT_BASE_URL = 'https://bitex.la/api' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/bitex/src/endpoint/index.ts b/bitex/src/endpoint/index.ts new file mode 100644 index 0000000000..d25188d9fb --- /dev/null +++ b/bitex/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as tickers from './tickers' diff --git a/bitex/src/endpoint/tickers.ts b/bitex/src/endpoint/tickers.ts new file mode 100644 index 0000000000..170a7f9d0e --- /dev/null +++ b/bitex/src/endpoint/tickers.ts @@ -0,0 +1,35 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'tickers' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const base = validator.validated.data.base + const quote = validator.validated.data.quote + const field = validator.validated.data.field || 'vwap' + const url = `${NAME}/${base}_${quote}` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['data', 'attributes', field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/bitex/src/index.ts b/bitex/src/index.ts new file mode 100644 index 0000000000..e76d21a13d --- /dev/null +++ b/bitex/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'BITEX' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/bitex/test/adapter_test.js b/bitex/test/tickers.test.ts similarity index 52% rename from bitex/test/adapter_test.js rename to bitex/test/tickers.test.ts index 68602b3e75..f709ac5d24 100644 --- a/bitex/test/adapter_test.js +++ b/bitex/test/tickers.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 = [ @@ -14,13 +17,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) }) }) }) @@ -34,11 +35,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) + } }) }) }) @@ -50,11 +53,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/bitex/tsconfig.json b/bitex/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/bitex/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/bitso/.eslintrc.js b/bitso/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/bitso/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/bitso/README.md b/bitso/README.md index dbd5e17716..9f6c830b2a 100644 --- a/bitso/README.md +++ b/bitso/README.md @@ -1,33 +1,57 @@ # Chainlink External Adapter for Bitso -## Input Params +### Input Parameters -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param (default: "ticker") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [ticker](#Ticker-Endpoint) | `ticker` | -## Output +--- + +## Ticker Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :---------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | | | +| | `field` | The object path to access the value that will be returned as the result | | `vwap` | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "BTC", + "quote": "ARS" + } +} +``` + +### Sample Output ```json { - "jobRunID":"1", - "data":{ - "success":true, - "payload":{ - "high":"1581920.78", - "last":"1567306.98", - "created_at":"2020-10-06T10:57:38+00:00", - "book":"btc_ars", - "volume":"16.96252687", - "vwap":"1568906.7103474855", - "low":"1553404.00", - "ask":"1574120.27", - "bid":"1567306.98", - "change_24":"2345.15" - }, - "result":1567306.98 + "jobRunID": "1", + "data": { + "success": true, + "payload": { + "high": "1581920.78", + "last": "1567306.98", + "created_at": "2020-10-06T10:57:38+00:00", + "book": "btc_ars", + "volume": "16.96252687", + "vwap": "1568906.7103474855", + "low": "1553404.00", + "ask": "1574120.27", + "bid": "1567306.98", + "change_24": "2345.15" }, - "result":1567306.98, - "statusCode":200 + "result": 1567306.98 + }, + "result": 1567306.98, + "statusCode": 200 } ``` diff --git a/bitso/adapter.js b/bitso/adapter.js deleted file mode 100644 index cb308db596..0000000000 --- a/bitso/adapter.js +++ /dev/null @@ -1,33 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'ticker' - const url = `https://api.bitso.com/v3/${endpoint}/` - const base = validator.validated.data.base.toLowerCase() - const quote = validator.validated.data.quote.toLowerCase() - const book = `${base}_${quote}` - - const params = { book } - const config = { url, params } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['payload', 'vwap']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => { - callback(500, Requester.errored(jobRunID, error)) - }) -} - -module.exports.execute = execute diff --git a/bitso/index.js b/bitso/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/bitso/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/bitso/package.json b/bitso/package.json index 4ea43e51f6..a47a278791 100644 --- a/bitso/package.json +++ b/bitso/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/bitso-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Bitso adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/bitso/src/adapter.ts b/bitso/src/adapter.ts new file mode 100644 index 0000000000..6671088a13 --- /dev/null +++ b/bitso/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { ticker } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case ticker.NAME: { + return await ticker.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/bitso/src/config.ts b/bitso/src/config.ts new file mode 100644 index 0000000000..3108235673 --- /dev/null +++ b/bitso/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'ticker' +export const DEFAULT_BASE_URL = 'https://api.bitso.com/v3' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/bitso/src/endpoint/index.ts b/bitso/src/endpoint/index.ts new file mode 100644 index 0000000000..1f6d4dc303 --- /dev/null +++ b/bitso/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as ticker from './ticker' diff --git a/bitso/src/endpoint/ticker.ts b/bitso/src/endpoint/ticker.ts new file mode 100644 index 0000000000..d1c60b2645 --- /dev/null +++ b/bitso/src/endpoint/ticker.ts @@ -0,0 +1,39 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'ticker' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const field = validator.validated.data.field || 'vwap' + const url = `${NAME}` + const base = validator.validated.data.base.toLowerCase() + const quote = validator.validated.data.quote.toLowerCase() + const book = `${base}_${quote}` + + const params = { book } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['payload', field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/bitso/src/index.ts b/bitso/src/index.ts new file mode 100644 index 0000000000..9c5eddcaa9 --- /dev/null +++ b/bitso/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'BITSO' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/bitso/test/adapter_test.js b/bitso/test/ticker.test.ts similarity index 52% rename from bitso/test/adapter_test.js rename to bitso/test/ticker.test.ts index 68602b3e75..f709ac5d24 100644 --- a/bitso/test/adapter_test.js +++ b/bitso/test/ticker.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 = [ @@ -14,13 +17,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) }) }) }) @@ -34,11 +35,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) + } }) }) }) @@ -50,11 +53,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/bitso/tsconfig.json b/bitso/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/bitso/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/blockchain.com/README.md b/blockchain.com/README.md index 379a20156b..84561b4856 100644 --- a/blockchain.com/README.md +++ b/blockchain.com/README.md @@ -1,4 +1,4 @@ -# Chainlink External Adapters to query BTC address balance from blockchain.com +# Chainlink External Adapter for blockchain.com ## Configuration @@ -58,7 +58,7 @@ The adapter takes the following environment variables: } ``` -### Output +#### Output ```json { @@ -156,3 +156,55 @@ The adapter takes the following environment variables: "statusCode": 200 } ``` + +### Difficulty endpoint + +- `chain`: Optional chain to query, defaults to `mainnet` + +```json +{ + "id": "1", + "data": { + "endpoint": "difficulty" + } +} +``` + +#### Output + +```json +{ + "jobRunID": "1", + "data": { + "result": 23137439666472 + }, + "result": 23137439666472, + "statusCode": 200 +} +``` + +### Height endpoint + +- `chain`: Optional chain to query, defaults to `mainnet` + +```json +{ + "id": "1", + "data": { + "endpoint": "height" + } +} +``` + +#### Output + +```json +{ + "jobRunID": "1", + "data": { + "result": 678212 + }, + "result": 678212, + "statusCode": 200 +} +``` diff --git a/blockchain.com/package.json b/blockchain.com/package.json index 5bbd07203f..2b5d0459e5 100644 --- a/blockchain.com/package.json +++ b/blockchain.com/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/blockchain.com-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter to query BTC address balance from blockchain.com.", "keywords": [ "Chainlink", diff --git a/blockchain.com/src/adapter.ts b/blockchain.com/src/adapter.ts index 3d799d7f63..45a035cbea 100644 --- a/blockchain.com/src/adapter.ts +++ b/blockchain.com/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 { balance } from './endpoint' +import { balance, difficulty, height } from './endpoint' const inputParams = { endpoint: false, @@ -21,6 +21,12 @@ export const execute: ExecuteWithConfig = async (request, config) => { case balance.Name: { return balance.makeExecute(config)(request) } + case difficulty.Name: { + return difficulty.execute(request, config) + } + case height.Name: { + return height.execute(request, config) + } default: { throw new AdapterError({ jobRunID, diff --git a/blockchain.com/src/config.ts b/blockchain.com/src/config.ts index ef5ad3c98d..61b398cd44 100644 --- a/blockchain.com/src/config.ts +++ b/blockchain.com/src/config.ts @@ -14,6 +14,7 @@ export const DEFAULT_ENDPOINT = 'balance' export const getBaseURL = (chain: ChainType): string => { switch (chain) { case 'mainnet': + default: return API_ENDPOINT_MAIN case 'testnet': return API_ENDPOINT_TEST diff --git a/blockchain.com/src/endpoint/difficulty.ts b/blockchain.com/src/endpoint/difficulty.ts new file mode 100644 index 0000000000..2884ab278b --- /dev/null +++ b/blockchain.com/src/endpoint/difficulty.ts @@ -0,0 +1,27 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig } from '@chainlink/types' +import { API_ENDPOINT_MAIN } from '../config' + +export const Name = 'difficulty' + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const reqConfig = { + ...config.api, + baseURL: config.api.baseURL || API_ENDPOINT_MAIN, + url: 'q/getdifficulty', + } + + const response = await Requester.request(reqConfig) + const result = response.data + + return Requester.success(jobRunID, { + data: { result }, + result, + status: 200, + }) +} diff --git a/blockchain.com/src/endpoint/height.ts b/blockchain.com/src/endpoint/height.ts new file mode 100644 index 0000000000..717387119f --- /dev/null +++ b/blockchain.com/src/endpoint/height.ts @@ -0,0 +1,27 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig } from '@chainlink/types' +import { API_ENDPOINT_MAIN } from '../config' + +export const Name = 'height' + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const reqConfig = { + ...config.api, + baseURL: config.api.baseURL || API_ENDPOINT_MAIN, + url: 'q/getblockcount', + } + + const response = await Requester.request(reqConfig) + const result = response.data + + return Requester.success(jobRunID, { + data: { result }, + result, + status: 200, + }) +} diff --git a/blockchain.com/src/endpoint/index.ts b/blockchain.com/src/endpoint/index.ts index 0a2ca59a84..613fc337e6 100644 --- a/blockchain.com/src/endpoint/index.ts +++ b/blockchain.com/src/endpoint/index.ts @@ -1,4 +1,6 @@ export * as balance from './balance' +export * as difficulty from './difficulty' +export * as height from './height' export const COIN_KEYS = ['btc'] as const export type CoinType = typeof COIN_KEYS[number] diff --git a/blockchain.com/src/index.ts b/blockchain.com/src/index.ts index 925424bddd..28394a695f 100644 --- a/blockchain.com/src/index.ts +++ b/blockchain.com/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'BLOCKCHAIN_COM' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/blockchain.com/test/adapter.test.ts b/blockchain.com/test/adapter.test.ts index 70fff1a4ad..fab33a4172 100644 --- a/blockchain.com/test/adapter.test.ts +++ b/blockchain.com/test/adapter.test.ts @@ -1,4 +1,88 @@ +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' import { shouldBehaveLikeBalanceAdapter } from '@chainlink/adapter-test-helpers' shouldBehaveLikeBalanceAdapter(makeExecute(), ['bitcoin_mainnet', 'bitcoin_testnet']) + +describe('endpoints: difficulty & height', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'endpoint difficulty', + testData: { + id: jobID, + data: { + endpoint: 'difficulty', + }, + }, + }, + { + name: 'endpoint height', + testData: { + id: jobID, + data: { + endpoint: 'height', + }, + }, + }, + ] + + 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: {}, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown endpoint', + testData: { + id: jobID, + data: { endpoint: 'not_real' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/blockchair/package.json b/blockchair/package.json index d93e4db10a..3c48c3ca4c 100644 --- a/blockchair/package.json +++ b/blockchair/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/blockchair-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "main": "dist/index.js", "scripts": { diff --git a/blockchair/src/index.ts b/blockchair/src/index.ts index bda446bf96..2d173a280f 100644 --- a/blockchair/src/index.ts +++ b/blockchair/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'BLOCKCHAIR' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/blockcypher/package.json b/blockcypher/package.json index aee496c856..cfbff9b4df 100644 --- a/blockcypher/package.json +++ b/blockcypher/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/blockcypher-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter to query BTC address balance from blockcypher.com.", "keywords": [ "Chainlink", diff --git a/blockcypher/src/index.ts b/blockcypher/src/index.ts index fb1a2eb227..621efcdc85 100644 --- a/blockcypher/src/index.ts +++ b/blockcypher/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'BLOCKCYPHER' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/blockstream/.eslintrc.js b/blockstream/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/blockstream/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/blockstream/README.md b/blockstream/README.md new file mode 100644 index 0000000000..e578273a2a --- /dev/null +++ b/blockstream/README.md @@ -0,0 +1,57 @@ +# Chainlink External Adapter for blockstream + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------------------------------------: | :----------: | +| | endpoint | The endpoint to use | [difficulty](#Blocks-Endpoint), [height](#Blocks-Endpoint) | `difficulty` | + +--- + +## Blocks Endpoint + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---: | :---------------------------------: | :-----: | :---------: | +| | field | The object path to the result value | | difficulty | + +When using `difficulty` or `height` the field will be filled as the endpoint. + +### Sample Input + +```json +{ + "id": "1", + "data": {} +} +``` + +### Sample Output + +```json +{ + "jobRunID": "1", + "data": { + "0": { + "id": "00000000000000000004f750822529412b53652759dfbc4adbe1fa247cf03fe0", + "height": 678212, + "version": 1073676288, + "timestamp": 1617825114, + "tx_count": 2569, + "size": 1333284, + "weight": 3993177, + "merkle_root": "bb11ec6d7be9cdfd9cef4b6e9b2e6c0b6999b3d36e9603d9d37c0995390fd0f5", + "previousblockhash": "0000000000000000000548216a24e05e8e4d9ba2a1d835f19bfd677d0205501d", + "mediantime": 1617822205, + "nonce": 4102352243, + "bits": 386673224, + "difficulty": 23137439666472 + }, + ... + "result": 23137439666472 + }, + "result": 23137439666472, + "statusCode": 200 +} +``` diff --git a/blockstream/package.json b/blockstream/package.json new file mode 100644 index 0000000000..305a3bb27b --- /dev/null +++ b/blockstream/package.json @@ -0,0 +1,46 @@ +{ + "name": "@chainlink/blockstream-adapter", + "version": "0.0.2", + "description": "Chainlink blockstream adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "blockstream" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": {} +} diff --git a/blockstream/src/adapter.ts b/blockstream/src/adapter.ts new file mode 100644 index 0000000000..0ea29e753b --- /dev/null +++ b/blockstream/src/adapter.ts @@ -0,0 +1,40 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { blocks } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case 'difficulty': { + request.data.field = 'difficulty' + return await blocks.execute(request, config) + } + case 'height': { + request.data.field = 'height' + return await blocks.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/blockstream/src/config.ts b/blockstream/src/config.ts new file mode 100644 index 0000000000..0bc9d0c9e9 --- /dev/null +++ b/blockstream/src/config.ts @@ -0,0 +1,14 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'difficulty' +export const DEFAULT_API_ENDPOINT = 'https://blockstream.info/api' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + } + return config +} diff --git a/blockstream/src/endpoint/blocks.ts b/blockstream/src/endpoint/blocks.ts new file mode 100644 index 0000000000..c962fece34 --- /dev/null +++ b/blockstream/src/endpoint/blocks.ts @@ -0,0 +1,32 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const field = validator.validated.data.field || 'difficulty' + const url = `/blocks` + + const options = { + ...config.api, + url, + timeout: 10000, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [0, field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/blockstream/src/endpoint/index.ts b/blockstream/src/endpoint/index.ts new file mode 100644 index 0000000000..46632700eb --- /dev/null +++ b/blockstream/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as blocks from './blocks' diff --git a/blockstream/src/index.ts b/blockstream/src/index.ts new file mode 100644 index 0000000000..7f1b0eeb8e --- /dev/null +++ b/blockstream/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'BLOCKSTREAM' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/blockstream/test/adapter.test.ts b/blockstream/test/adapter.test.ts new file mode 100644 index 0000000000..19a2ccd3c4 --- /dev/null +++ b/blockstream/test/adapter.test.ts @@ -0,0 +1,88 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'empty data', + testData: { data: {} }, + }, + { + name: 'id not supplied (endpoint height)', + testData: { + data: { + endpoint: 'height', + }, + }, + }, + { + name: 'endpoint difficulty', + testData: { + id: jobID, + data: { + 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: {}, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown endpoint', + testData: { + id: jobID, + data: { endpoint: 'not_real' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/blockstream/tsconfig.json b/blockstream/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/blockstream/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/bootstrap/README.md b/bootstrap/README.md index f0bdb3fcf7..410d4e66ea 100644 --- a/bootstrap/README.md +++ b/bootstrap/README.md @@ -2,6 +2,15 @@ Bootstrap an external adapter with this package +## Server config +- `BASE_URL`: Optional string, Set a base url that is used for setting up routes on the external adapter. Ex. Typically a external adapter is served on the root, so you would make requests to `/`, setting `BASE_URL` to `/coingecko` would instead have requests made to `/coingecko`. Useful when multiple external adapters are being hosted under the same domain, and path mapping is being used to route between them. + +## Metrics +A metrics server can be exposed which returns prometheus compatible data on the `/metrics` endpoint on the specified port. Note that this feature is ONLY available when running this application as an http server. Please note that this feature is EXPERIMENTAL. +- `EXPERIMENTAL_METRICS_ENABLED`: Optional bool, defaults to `false`. Set to `true` to enable metrics collection. +- `METRICS_PORT`: Optional number, defaults to `9080`, set to change the port the `/metrics` endpoint is served on +- `METRICS_NAME`: Optional string, defaults to 'N/A', set to apply a label of `NAME` to each metric + ## Caching To cache data, every adapter using the `bootstrap` package, has access to a simple LRU cache that will cache successful 200 responses using SHA1 hash of input as a key. diff --git a/bootstrap/package.json b/bootstrap/package.json index 0bdd797637..54aa7bf623 100644 --- a/bootstrap/package.json +++ b/bootstrap/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/ea-bootstrap", - "version": "0.1.6", + "version": "0.1.8", "description": "Bootstrap an external adapter with this package", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -22,6 +22,7 @@ "express": "^4.17.1", "lru-cache": "^6.0.0", "object-hash": "^2.0.3", + "prom-client": "^13.1.0", "promise-timeout": "^1.3.0", "redis": "^3.0.2", "uuid": "^8.3.0" diff --git a/bootstrap/src/index.ts b/bootstrap/src/index.ts index 0fb621f101..82e3f7d894 100644 --- a/bootstrap/src/index.ts +++ b/bootstrap/src/index.ts @@ -1,21 +1,16 @@ -import { Requester, logger } from '@chainlink/external-adapter' -import { types } from 'util' -import { withCache, defaultOptions, redactOptions } from './lib/cache' -import * as util from './lib/util' -import * as server from './lib/server' -import * as gcp from './lib/gcp' +import { logger, Requester } from '@chainlink/external-adapter' +import { AdapterHealthCheck, AdapterRequest, Execute, ExecuteSync } from '@chainlink/types' import * as aws from './lib/aws' -import { - ExecuteSync, - AdapterRequest, - ExecuteWrappedResponse, - AdapterHealthCheck, -} from '@chainlink/types' +import { defaultOptions, redactOptions, withCache } from './lib/cache' +import * as gcp from './lib/gcp' +import * as metrics from './lib/metrics' +import * as server from './lib/server' +import * as util from './lib/util' -type Middleware = (execute: ExecuteWrappedResponse) => Promise +export type Middleware = (execute: Execute, options?: O) => Promise // Try to initialize, pass through on error -const skipOnError = (middleware: Middleware) => async (execute: ExecuteWrappedResponse) => { +const skipOnError = (middleware: Middleware) => async (execute: Execute) => { try { return await middleware(execute) } catch (error) { @@ -25,10 +20,11 @@ const skipOnError = (middleware: Middleware) => async (execute: ExecuteWrappedRe } // Make sure data has the same statusCode as the one we got as a result -const withStatusCode = (execute: ExecuteWrappedResponse) => async (data_: AdapterRequest) => { - const { statusCode, data } = await execute(data_) +const withStatusCode: Middleware = async (execute) => async (input) => { + const { statusCode, data, ...rest } = await execute(input) if (data && typeof data === 'object' && data.statusCode) { return { + ...rest, statusCode, data: { ...data, @@ -37,14 +33,14 @@ const withStatusCode = (execute: ExecuteWrappedResponse) => async (data_: Adapte } } - return { statusCode, data } + return { ...rest, statusCode, data } } // Log adapter input & output data -const withLogger = (execute: ExecuteWrappedResponse) => async (data: AdapterRequest) => { - logger.debug('Input: ', { input: data }) +const withLogger: Middleware = async (execute) => async (input: AdapterRequest) => { + logger.debug('Input: ', { input }) try { - const result = await execute(data) + const result = await execute(input) logger.debug(`Output: [${result.statusCode}]: `, { output: result.data }) return result } catch (error) { @@ -53,10 +49,43 @@ const withLogger = (execute: ExecuteWrappedResponse) => async (data: AdapterRequ } } -const middleware = [withLogger, skipOnError(withCache), withStatusCode] +const withMetrics: Middleware = async (execute) => async (input: AdapterRequest) => { + const recordMetrics = () => { + const labels: Parameters[0] = { + method: 'POST', + } + const end = metrics.httpRequestDurationSeconds.startTimer() + + return (statusCode?: number, type?: metrics.HttpRequestType) => { + labels.type = type + labels.status_code = metrics.normalizeStatusCode(statusCode) + end() + metrics.httpRequestsTotal.labels(labels).inc() + } + } + + const record = recordMetrics() + try { + const result = await execute(input) + record( + result.statusCode, + result.data.maxAge || (result as any).maxAge + ? metrics.HttpRequestType.CACHE_HIT + : metrics.HttpRequestType.DATA_PROVIDER_HIT, + ) + return result + } catch (error) { + record() + throw error + } +} + +const middleware = [withLogger, skipOnError(withCache), withStatusCode].concat( + metrics.METRICS_ENABLED ? [withMetrics] : [], +) // Init all middleware, and return a wrapped execute fn -const withMiddleware = async (execute: ExecuteWrappedResponse) => { +const withMiddleware = async (execute: Execute) => { // Init and wrap middleware one by one for (let i = 0; i < middleware.length; i++) { execute = await middleware[i](execute) @@ -64,15 +93,8 @@ const withMiddleware = async (execute: ExecuteWrappedResponse) => { return execute } -// Transform sync execute function to async -const withAsync = (execute: ExecuteWrappedResponse | ExecuteSync): ExecuteWrappedResponse => { - // Check if execute is already a Promise - if (types.isAsyncFunction(execute)) return execute as ExecuteWrappedResponse - return (data: AdapterRequest) => util.toAsync(execute as ExecuteSync, data) -} - // Execution helper async => sync -const executeSync = (execute: ExecuteWrappedResponse): ExecuteSync => { +const executeSync = (execute: Execute): ExecuteSync => { // TODO: Try to init middleware only once // const initMiddleware = withMiddleware(execute) @@ -81,17 +103,14 @@ const executeSync = (execute: ExecuteWrappedResponse): ExecuteSync => { // We init on every call because of cache connection broken state issue return withMiddleware(execute) .then((executeWithMiddleware) => executeWithMiddleware(data)) - .then((result) => callback(result.statusCode, result.data)) + .then((result) => callback(result.statusCode, result)) .catch((error) => callback(error.statusCode || 500, Requester.errored(data.id, error))) } } -export const expose = ( - execute: ExecuteWrappedResponse | ExecuteSync, - checkHealth?: AdapterHealthCheck, -) => { +export const expose = (execute: Execute, checkHealth?: AdapterHealthCheck) => { // Add middleware to the execution flow - const _execute = executeSync(withAsync(execute)) + const _execute = executeSync(execute) return { server: server.initHandler(_execute, checkHealth), gcpHandler: gcp.initHandler(_execute), @@ -109,4 +128,4 @@ export type ExecuteHandlers = ReturnType const cacheOptions = defaultOptions() if (cacheOptions.enabled) logger.info('Cache enabled: ', redactOptions(cacheOptions)) -export { util } +export { util, server } diff --git a/bootstrap/src/lib/cache/index.ts b/bootstrap/src/lib/cache/index.ts index 40d436049d..3530a267f2 100644 --- a/bootstrap/src/lib/cache/index.ts +++ b/bootstrap/src/lib/cache/index.ts @@ -3,8 +3,9 @@ import hash from 'object-hash' import * as local from './local' import * as redis from './redis' import { parseBool, uuid, delay, exponentialBackOffMs, getWithCoalescing } from '../util' -import { ExecuteWrappedResponse, AdapterRequest, WrappedAdapterResponse } from '@chainlink/types' +import { AdapterRequest, AdapterResponse } from '@chainlink/types' import { RedisOptions } from './redis' +import { Middleware } from '../../index' const DEFAULT_CACHE_TYPE = 'local' const DEFAULT_CACHE_KEY_GROUP = uuid() @@ -62,7 +63,7 @@ const defaultCacheBuilder = () => { } } // Options without sensitive data -export const redactOptions = (options: CacheOptions) => ({ +export const redactOptions = (options: CacheOptions): CacheOptions => ({ ...options, cacheOptions: options.cacheOptions.type === 'redis' @@ -70,10 +71,7 @@ export const redactOptions = (options: CacheOptions) => ({ : local.redactOptions(options.cacheOptions), }) -export const withCache = async ( - execute: ExecuteWrappedResponse, - options: CacheOptions = defaultOptions(), -) => { +export const withCache: Middleware = async (execute, options = defaultOptions()) => { // If disabled noop if (!options.enabled) return (data: AdapterRequest) => execute(data) @@ -110,9 +108,9 @@ export const withCache = async ( const coalescingKey = _getCoalescingKey(key) const maxAge = _getMaxAge(data) // Add successful result to cache - const _cacheOnSuccess = async ({ statusCode, data }: WrappedAdapterResponse) => { + const _cacheOnSuccess = async ({ statusCode, data, result }: AdapterResponse) => { if (statusCode === 200) { - const entry = { statusCode, data, maxAge } + const entry = { statusCode, data, result, maxAge } await cache.set(key, entry, maxAge) logger.debug(`Cache: SET ${key}`, entry) // Notify pending requests by removing the in-flight mark @@ -156,7 +154,7 @@ export const withCache = async ( if (maxAge >= 0) { logger.debug(`Cache: GET ${key}`, entry) if (maxAge !== entry.maxAge) await _cacheOnSuccess(entry) - return entry + return { jobRunID: data.id, ...entry } } logger.debug(`Cache: SKIP(maxAge < 0)`) } @@ -170,10 +168,12 @@ export const withCache = async ( } // Middleware wrapped execute fn which cleans up after - return async (data: AdapterRequest) => { - const result = await _executeWithCache(data) - // Clean the connection - await cache.close() - return result + return async (input) => { + try { + return await _executeWithCache(input) + } finally { + // Close the cache connection in any case + await cache.close() + } } } diff --git a/bootstrap/src/lib/cache/redis.ts b/bootstrap/src/lib/cache/redis.ts index 1755d279c1..c613f06743 100644 --- a/bootstrap/src/lib/cache/redis.ts +++ b/bootstrap/src/lib/cache/redis.ts @@ -61,16 +61,19 @@ export class RedisCache { _get: any _set: any _del: any + _quit: any constructor(options: RedisOptions) { this.options = options const client = createClient({ ...options, retry_strategy: retryStrategy }) client.on('error', (err) => logger.error('Error connecting to Redis. ', err)) + client.on('end', () => logger.error('Redis connection ended.')) this._auth = promisify(client.auth).bind(client) this._get = promisify(client.get).bind(client) this._set = promisify(client.set).bind(client) this._del = promisify(client.del).bind(client) + this._quit = promisify(client.quit).bind(client) this.client = client } @@ -108,7 +111,16 @@ export class RedisCache { * The alternative is to use: `context.callbackWaitsForEmtpyEventLoop = false` */ async close() { - // No further commands will be processed - this.client.end(true) + if (!this.client) return + + try { + // No further commands will be processed + const res = await timeout(this._quit(), this.options.timeout) + logger.debug(`Redis connection shutdown completed with: ${res}`) + } catch (err) { + logger.error(`Redis connection shutdown failed with: ${err}`) + } finally { + this.client.removeAllListeners() + } } } diff --git a/bootstrap/src/lib/metrics/index.ts b/bootstrap/src/lib/metrics/index.ts new file mode 100644 index 0000000000..743a07a936 --- /dev/null +++ b/bootstrap/src/lib/metrics/index.ts @@ -0,0 +1,58 @@ +import * as client from 'prom-client' +import { parseBool } from '../util' + +client.collectDefaultMetrics() +client.register.setDefaultLabels( + // we'll inject both name and versions in + // when EAEE gets merged, because it'll be a lot easier + // to refactor with full type coverage support + { app_name: process.env.METRICS_NAME || 'N/A', app_version: 'N/A' }, +) +export const METRICS_ENABLED = parseBool(process.env.EXPERIMENTAL_METRICS_ENABLED) + +export enum HttpRequestType { + CACHE_HIT = 'cacheHit', + DATA_PROVIDER_HIT = 'dataProviderHit', +} + +export const httpRequestsTotal = new client.Counter({ + name: 'http_requests_total', + help: 'The number of http requests this external adapter has serviced for its entire uptime', + labelNames: ['method', 'status_code', 'retry', 'type'] as const, +}) + +export const httpRequestDurationSeconds = new client.Histogram({ + name: 'http_request_duration_seconds', + help: 'A histogram bucket of the distribution of http request durations', + // we should tune these as we collect data, this is the default + // bucket distribution that prom comes with + buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10], +}) + +/** + * Normalizes http status codes. + * + * Returns strings in the format (2|3|4|5)XX. + * + * @author https://github.com/joao-fontenele/express-prometheus-middleware + * @param {!number} status - status code of the requests + * @returns {string} the normalized status code. + */ +export function normalizeStatusCode(status?: number): string { + if (!status) { + return '5XX' + } + + if (status >= 200 && status < 300) { + return '2XX' + } + + if (status >= 300 && status < 400) { + return '3XX' + } + + if (status >= 400 && status < 500) { + return '4XX' + } + return '5XX' +} diff --git a/bootstrap/src/lib/server.ts b/bootstrap/src/lib/server.ts index 3fd7c662f7..98de505dd5 100644 --- a/bootstrap/src/lib/server.ts +++ b/bootstrap/src/lib/server.ts @@ -1,15 +1,18 @@ import { logger } from '@chainlink/external-adapter' -import { toObjectWithNumbers } from './util' +import { AdapterHealthCheck, AdapterResponse, ExecuteSync } from '@chainlink/types' import express from 'express' +import * as client from 'prom-client' import { HTTP_ERROR_NOT_IMPLEMENTED, HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE, HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE_MESSAGE, } from './errors' -import { ExecuteSync, AdapterResponse, AdapterHealthCheck } from '@chainlink/types' +import { METRICS_ENABLED } from './metrics' +import { toObjectWithNumbers } from './util' const app = express() const port = process.env.EA_PORT || 8080 +const baseUrl = process.env.BASE_URL || '/' export const HEADER_CONTENT_TYPE = 'Content-Type' export const CONTENT_TYPE_APPLICATION_JSON = 'application/json' @@ -22,9 +25,12 @@ export const initHandler = ( execute: ExecuteSync, checkHealth = notImplementedHealthCheck, ) => (): void => { + if (METRICS_ENABLED) { + setupMetricsServer() + } app.use(express.json()) - app.post('/', (req, res) => { + app.post(baseUrl, (req, res) => { if (!req.is(CONTENT_TYPE_APPLICATION_JSON)) { return res .status(HTTP_ERROR_UNSUPPORTED_MEDIA_TYPE) @@ -39,7 +45,7 @@ export const initHandler = ( }) }) - app.get('/health', (_, res) => { + app.get(`${baseUrl}/health`, (_, res) => { logger.debug('Health check request') checkHealth((status: number, result: AdapterResponse) => { logger.debug(`Health check result [${status}]: `, { output: result }) @@ -48,8 +54,19 @@ export const initHandler = ( }) app.listen(port, () => logger.info(`Listening on port ${port}!`)) - process.on('SIGINT', () => { process.exit() }) } + +function setupMetricsServer() { + const metricsApp = express() + const metricsPort = process.env.METRICS_PORT || 9080 + + metricsApp.get('/metrics', async (_, res) => { + res.type('txt') + res.send(await client.register.metrics()) + }) + + metricsApp.listen(metricsPort, () => logger.info(`Monitoring listening on port ${metricsPort}!`)) +} diff --git a/bootstrap/src/lib/util.ts b/bootstrap/src/lib/util.ts index 2fe1c379e0..98d3e9e622 100644 --- a/bootstrap/src/lib/util.ts +++ b/bootstrap/src/lib/util.ts @@ -1,11 +1,4 @@ -import { - Execute, - ExecuteSync, - AdapterRequest, - AdapterResponse, - WrappedAdapterResponse, - AdapterImplementation, -} from '@chainlink/types' +import { AdapterImplementation } from '@chainlink/types' import { v4 as uuidv4 } from 'uuid' import { Decimal } from 'decimal.js' @@ -24,6 +17,14 @@ export const toObjectWithNumbers = (obj: any) => { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, toNumber(v)])) } +// pick a random string from env var after splitting with the delimiter ("a&b&c" "&" -> choice(["a","b","c"])) +export const getRandomEnv = (name: string, delimiter = ',', prefix = '') => { + const val = getEnv(name, prefix) + if (!val) return val + const items = val.split(delimiter) + return items[Math.floor(Math.random() * items.length)] +} + // pick a random string from env var after splitting with the delimiter ("a&b&c" "&" -> choice(["a","b","c"])) export const getRandomRequiredEnv = (name: string, delimiter = ',', prefix = '') => { const val = getRequiredEnv(name, prefix) @@ -37,14 +38,6 @@ export const uuid = (): string => { return process.env.UUID } -export const toAsync = ( - execute: ExecuteSync, - data: AdapterRequest, -): Promise => - new Promise((resolve) => - execute(data, (statusCode: number, data: AdapterResponse) => resolve({ statusCode, data })), - ) - export const delay = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) @@ -120,18 +113,6 @@ export const getRequiredEnv = (name: string, prefix = ''): string => { return val } -// TODO: clean this ASAP -// @see WrappedAdapterResponse -export const wrapExecute = (execute: Execute) => async ( - request: AdapterRequest, -): Promise => { - const resp = await execute(request) - return { - statusCode: resp.statusCode, - data: resp, - } -} - /** * @description * Takes an Array, and a grouping function, diff --git a/bootstrap/test/cache.test.ts b/bootstrap/test/cache.test.ts index 8a1c6468c2..6c0b99caef 100644 --- a/bootstrap/test/cache.test.ts +++ b/bootstrap/test/cache.test.ts @@ -3,7 +3,7 @@ import { useFakeTimers } from 'sinon' import { withCache, CacheImplOptions, defaultOptions } from '../src/lib/cache' import { LocalLRUCache } from '../src/lib/cache/local' import { CacheOptions } from '../src/lib/cache' -import { ExecuteWrappedResponse } from '@chainlink/types' +import { Execute } from '@chainlink/types' const callAndExpect = async (fn: any, n: number, result: any) => { while (n--) { @@ -13,9 +13,12 @@ const callAndExpect = async (fn: any, n: number, result: any) => { } // Helper test function: a stateful counter -const counterFrom = (i = 0): ExecuteWrappedResponse => async (request) => { +const counterFrom = (i = 0): Execute => async (request) => { + const result = i++ return { - data: { jobRunID: request.id, statusCode: 200, data: request, result: i++ }, + jobRunID: request.id, + data: { jobRunID: request.id, statusCode: 200, data: request, result }, + result, statusCode: 200, } } diff --git a/bravenewcoin-vwap/.eslintrc.js b/bravenewcoin-vwap/.eslintrc.js deleted file mode 100644 index 7d045a59de..0000000000 --- a/bravenewcoin-vwap/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../.eslintrc.js') diff --git a/bravenewcoin-vwap/README.md b/bravenewcoin-vwap/README.md deleted file mode 100644 index 9fa3306d34..0000000000 --- a/bravenewcoin-vwap/README.md +++ /dev/null @@ -1,45 +0,0 @@ -# Chainlink External Adapter for [BraveNewCoin's 24 Hour USD VWAP](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_8b8774ba-b368-4399-9c4a-dc78f13fc786) - -## Input Params - -- `base`, `from`, `coin`, `symbol`, `assetId`, `indexId`, or `asset`: Retrieve all the OHLCV values for a particular asset or market -- `indexType`: Restrict the OHLCV results to the index type. Either MWA or GWA -- `timestamp`: Retrieve all daily OHLCV records from the timestamp provided. All dates are stored in UTC. Timestamp strings should be in the form YYYY-MM-DDThh:mm:ssZ - -## Environment Variables - -- `API_KEY`: Your RapidAPI API key -- `CLIENT_ID`: Your RapidAPI client ID - -Credentials can be obtained from [this](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_d040b5cb-b6da-4628-bb86-fef663f635dc) page (requires being logged in). - -## Output - -```json -{ - "jobRunID": "1", - "data": { - "content": [ - { - "indexId": "551cdbbe-2a97-4af8-b6bc-3254210ed021", - "indexType": "GWA", - "open": 1.9248204798140678, - "high": 2.5557035027423054, - "low": 1.891225386234147, - "close": 2.4208656452222885, - "volume": 665942.7213355688, - "vwap": 2.12777657752828, - "twap": 2.07318626293901, - "startTimestamp": "2020-07-08T00:00:00Z", - "endTimestamp": "2020-07-08T23:59:59.999Z", - "timestamp": "2020-07-08T00:00:00Z", - "id": "637e68c3-681f-49c2-a69f-c239c14e1d18" - } - ], - "nextId": "637e68c3-681f-49c2-a69f-c239c14e1d18", - "result": 2.12777657752828 - }, - "result": 2.12777657752828, - "statusCode": 200 -} -``` diff --git a/bravenewcoin-vwap/adapter.js b/bravenewcoin-vwap/adapter.js deleted file mode 100644 index f0c9ecad15..0000000000 --- a/bravenewcoin-vwap/adapter.js +++ /dev/null @@ -1,55 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { apiHeaders, authenticate, getAssetId, host } = require('../helpers/bravenewcoin/helpers') - -const customParams = { - symbol: ['base', 'from', 'coin', 'symbol', 'assetId', 'indexId', 'asset'], - indexType: false, - timestamp: false, -} - -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 yesterday = new Date() - yesterday.setDate(yesterday.getDate() - 1) - - const _onResponse = (resp) => { - const path = ['content', 0, 'vwap'] - resp.data.result = Requester.validateResultNumber(resp.data, path) - callback(resp.status, Requester.success(jobRunID, resp)) - } - - const _onError = (error) => callback(500, Requester.errored(jobRunID, error)) - - _execute({ - url: `https://${host}/ohlcv`, - symbol: validator.validated.data.symbol, - indexType: 'GWA', - timestamp: yesterday, - }) - .then(_onResponse) - .catch(_onError) -} - -const _execute = async (input) => { - const token = await authenticate() - const assetId = await getAssetId(input.symbol) - return await Requester.request({ - url: input.url, - headers: { - ...apiHeaders, - authorization: `Bearer ${token}`, - useQueryString: true, - }, - params: { - indexId: assetId, - indexType: input.indexType, - timestamp: input.timestamp, - size: 1, - }, - }) -} - -module.exports.execute = execute diff --git a/bravenewcoin-vwap/index.js b/bravenewcoin-vwap/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/bravenewcoin-vwap/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/bravenewcoin-vwap/package.json b/bravenewcoin-vwap/package.json deleted file mode 100644 index 3c4168daf1..0000000000 --- a/bravenewcoin-vwap/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/bravenewcoin-vwap-adapter", - "version": "0.0.3", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "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" - }, - "dependencies": {} -} diff --git a/bravenewcoin-vwap/test/adapter_test.js b/bravenewcoin-vwap/test/adapter_test.js deleted file mode 100644 index a00e557041..0000000000 --- a/bravenewcoin-vwap/test/adapter_test.js +++ /dev/null @@ -1,62 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { name: 'id not supplied', testData: { data: { base: 'AMPL' } } }, - { name: 'base/quote', testData: { id: jobID, data: { base: 'AMPL' } } }, - { name: 'from/to', testData: { id: jobID, data: { from: 'AMPL' } } }, - { name: 'coin/market', testData: { id: jobID, data: { coin: 'AMPL' } } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - { name: 'base not supplied', testData: { id: jobID, data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/bravenewcoin/.eslintrc.js b/bravenewcoin/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/bravenewcoin/.eslintrc.js +++ b/bravenewcoin/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/bravenewcoin/README.md b/bravenewcoin/README.md index 02cf9ed16c..3f752b20f4 100644 --- a/bravenewcoin/README.md +++ b/bravenewcoin/README.md @@ -1,9 +1,10 @@ -# Chainlink External Adapter for [BraveNewCoin's AssetTicker endpoint](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_836afc67-19d2-45ae-bb56-c576cec9f602) +# Chainlink External Adapter for BraveNewCoin -## Input Params +### Input Parameters -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [example](#Example-Endpoint) | example | ## Environment Variables @@ -12,7 +13,20 @@ Credentials can be obtained from [this](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_d040b5cb-b6da-4628-bb86-fef663f635dc) page (requires being logged in). -## Output +--- + +## Price endpoint + +[BraveNewCoin's AssetTicker endpoint](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_836afc67-19d2-45ae-bb56-c576cec9f602) + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | `BTC`, `ETH`, `USD` | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | `BTC`, `ETH`, `USD` | + +### Output ```json { @@ -24,3 +38,48 @@ Credentials can be obtained from [this](https://rapidapi.com/BraveNewCoin/api/br "statusCode": 200 } ``` + +--- + +## VWAP endpoint + +[BraveNewCoin's 24 Hour USD VWAP](https://rapidapi.com/BraveNewCoin/api/bravenewcoin?endpoint=apiendpoint_8b8774ba-b368-4399-9c4a-dc78f13fc786) + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :----------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------: | :----------: | :---------: | +| ✅ | `base`, `from`, `coin`, `symbol`, `assetId`, `indexId`, or `asset` | Retrieve all the OHLCV values for a particular asset or market | | | +| | `indexType` | Restrict the OHLCV results to the index type. | `MWA`, `GWA` | `GWA` | +| | `timestamp` | Retrieve all daily OHLCV records from the timestamp provided. All dates are stored in UTC. Timestamp strings should be in the form YYYY-MM-DDThh:mm:ssZ | | | + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "content": [ + { + "indexId": "551cdbbe-2a97-4af8-b6bc-3254210ed021", + "indexType": "GWA", + "open": 1.9248204798140678, + "high": 2.5557035027423054, + "low": 1.891225386234147, + "close": 2.4208656452222885, + "volume": 665942.7213355688, + "vwap": 2.12777657752828, + "twap": 2.07318626293901, + "startTimestamp": "2020-07-08T00:00:00Z", + "endTimestamp": "2020-07-08T23:59:59.999Z", + "timestamp": "2020-07-08T00:00:00Z", + "id": "637e68c3-681f-49c2-a69f-c239c14e1d18" + } + ], + "nextId": "637e68c3-681f-49c2-a69f-c239c14e1d18", + "result": 2.12777657752828 + }, + "result": 2.12777657752828, + "statusCode": 200 +} +``` diff --git a/bravenewcoin/adapter.js b/bravenewcoin/adapter.js deleted file mode 100644 index 8b9cb89c56..0000000000 --- a/bravenewcoin/adapter.js +++ /dev/null @@ -1,29 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { authenticate, convert, getAssetId } = require('../helpers/bravenewcoin/helpers') - -const customParams = { - from: ['base', 'from', 'coin'], - to: ['quote', 'to', 'market'], -} - -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 from = validator.validated.data.from - const to = validator.validated.data.to - - _execute({ from, to }) - .then((resp) => callback(resp.status, Requester.success(jobRunID, resp))) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -const _execute = async (input) => { - const token = await authenticate() - const baseAssetId = await getAssetId(input.from) - const quoteAssetId = input.to.toUpperCase() === 'USD' ? 'USD' : await getAssetId(input.to) - return await convert(token, baseAssetId, quoteAssetId) -} - -module.exports.execute = execute diff --git a/bravenewcoin/index.js b/bravenewcoin/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/bravenewcoin/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/bravenewcoin/package.json b/bravenewcoin/package.json index fe25720cab..2608ecbd36 100644 --- a/bravenewcoin/package.json +++ b/bravenewcoin/package.json @@ -1,15 +1,44 @@ { "name": "@chainlink/bravenewcoin-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/bravenewcoin/src/adapter.ts b/bravenewcoin/src/adapter.ts new file mode 100644 index 0000000000..0b052542fa --- /dev/null +++ b/bravenewcoin/src/adapter.ts @@ -0,0 +1,38 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price, vwap } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + case vwap.NAME: { + return await vwap.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/bravenewcoin/src/config.ts b/bravenewcoin/src/config.ts new file mode 100644 index 0000000000..2fad783a96 --- /dev/null +++ b/bravenewcoin/src/config.ts @@ -0,0 +1,6 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'price' + +export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) diff --git a/bravenewcoin/src/endpoint/index.ts b/bravenewcoin/src/endpoint/index.ts new file mode 100644 index 0000000000..50bda3b27c --- /dev/null +++ b/bravenewcoin/src/endpoint/index.ts @@ -0,0 +1,2 @@ +export * as price from './price' +export * as vwap from './vwap' diff --git a/bravenewcoin/src/endpoint/price.ts b/bravenewcoin/src/endpoint/price.ts new file mode 100644 index 0000000000..e9bff84c60 --- /dev/null +++ b/bravenewcoin/src/endpoint/price.ts @@ -0,0 +1,27 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { authenticate, convert, getAssetId } from '../helpers' + +export const NAME = 'price' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const from = validator.validated.data.base + const to = validator.validated.data.quote + + const token = await authenticate() + const baseAssetId = await getAssetId(from) + const quoteAssetId = to.toUpperCase() === 'USD' ? 'USD' : await getAssetId(to) + + const response = await convert(token, baseAssetId, quoteAssetId) + + return Requester.success(jobRunID, response) +} diff --git a/bravenewcoin/src/endpoint/vwap.ts b/bravenewcoin/src/endpoint/vwap.ts new file mode 100644 index 0000000000..29393bb984 --- /dev/null +++ b/bravenewcoin/src/endpoint/vwap.ts @@ -0,0 +1,50 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { authenticate, apiHeaders, getAssetId, host } from '../helpers' + +export const NAME = 'vwap' + +const customParams = { + symbol: ['base', 'from', 'coin', 'symbol', 'assetId', 'indexId', 'asset'], + indexType: false, + timestamp: false, // TODO: currently unused, deprecate or utilize me +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const yesterday = new Date() + yesterday.setDate(yesterday.getDate() - 1) + const symbol = validator.validated.data.symbol + + const url = `https://${host}/ohlcv` + const indexType = 'GWA' + const token = await authenticate() + const assetId = await getAssetId(symbol) + + const options = { + url, + headers: { + ...apiHeaders, + authorization: `Bearer ${token}`, + useQueryString: true, + }, + params: { + indexId: assetId, + indexType: indexType, + timestamp: yesterday, + size: 1, + }, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['content', 0, 'vwap']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/helpers/bravenewcoin/helpers.js b/bravenewcoin/src/helpers.ts similarity index 67% rename from helpers/bravenewcoin/helpers.js rename to bravenewcoin/src/helpers.ts index e22da4a22c..76222ffd35 100644 --- a/helpers/bravenewcoin/helpers.js +++ b/bravenewcoin/src/helpers.ts @@ -1,12 +1,12 @@ -const { Requester } = require('@chainlink/external-adapter') +import { Requester } from '@chainlink/external-adapter' -const host = 'bravenewcoin.p.rapidapi.com' -const apiHeaders = { +export const host = 'bravenewcoin.p.rapidapi.com' +export const apiHeaders = { 'x-rapidapi-host': host, - 'x-rapidapi-key': process.env.API_KEY + 'x-rapidapi-key': process.env.API_KEY, } -const authenticate = async () => { +export const authenticate = async () => { const response = await Requester.request({ method: 'POST', url: `https://${host}/oauth/token`, @@ -14,34 +14,34 @@ const authenticate = async () => { 'content-type': 'application/json', accept: 'application/json', useQueryString: true, - ...apiHeaders + ...apiHeaders, }, data: { audience: 'https://api.bravenewcoin.com', client_id: process.env.CLIENT_ID, - grant_type: 'client_credentials' - } + grant_type: 'client_credentials', + }, }) return response.data.access_token } -const getAssetId = async (symbol) => { +export const getAssetId = async (symbol: any) => { const response = await Requester.request({ url: `https://${host}/asset`, headers: { 'content-type': 'application/octet-stream', useQueryString: true, - ...apiHeaders + ...apiHeaders, }, params: { status: 'ACTIVE', - symbol - } + symbol, + }, }) return response.data.content[0].id } -const convert = async (token, baseAssetId, quoteAssetId) => { +export const convert = async (token: any, baseAssetId: any, quoteAssetId: any) => { const url = `https://${host}/market-cap` const path = ['content', 0, 'price'] const base = await Requester.request({ @@ -49,11 +49,11 @@ const convert = async (token, baseAssetId, quoteAssetId) => { headers: { ...apiHeaders, authorization: `Bearer ${token}`, - useQueryString: true + useQueryString: true, }, params: { - assetId: baseAssetId - } + assetId: baseAssetId, + }, }) const basePrice = Requester.validateResultNumber(base.data, path) if (quoteAssetId.toUpperCase() === 'USD') { @@ -61,7 +61,7 @@ const convert = async (token, baseAssetId, quoteAssetId) => { return { status: 200, data: { result }, - result + result, } } const quote = await Requester.request({ @@ -69,23 +69,17 @@ const convert = async (token, baseAssetId, quoteAssetId) => { headers: { ...apiHeaders, authorization: `Bearer ${token}`, - useQueryString: true + useQueryString: true, }, params: { - assetId: quoteAssetId - } + assetId: quoteAssetId, + }, }) const quotePrice = Requester.validateResultNumber(quote.data, path) const result = basePrice / quotePrice return { status: 200, data: { result }, - result + result, } } - -exports.host = host -exports.apiHeaders = apiHeaders -exports.authenticate = authenticate -exports.convert = convert -exports.getAssetId = getAssetId diff --git a/bravenewcoin/src/index.ts b/bravenewcoin/src/index.ts new file mode 100644 index 0000000000..299a365ea0 --- /dev/null +++ b/bravenewcoin/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'BRAVENEWCOIN' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/bravenewcoin/test/adapter_test.js b/bravenewcoin/test/price.test.ts similarity index 54% rename from bravenewcoin/test/adapter_test.js rename to bravenewcoin/test/price.test.ts index c3a1d3adee..e3334cc95f 100644 --- a/bravenewcoin/test/adapter_test.js +++ b/bravenewcoin/test/price.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', () => { +describe('price endpoint', () => { const jobID = '1' + const execute = makeExecute() context('successful calls @integration', () => { const requests = [ @@ -26,13 +29,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) }) }) }) @@ -52,11 +53,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) + } }) }) }) @@ -74,11 +77,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/bravenewcoin/test/vwap.test.ts b/bravenewcoin/test/vwap.test.ts new file mode 100644 index 0000000000..9cd12c2350 --- /dev/null +++ b/bravenewcoin/test/vwap.test.ts @@ -0,0 +1,67 @@ +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('price endpoint', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { name: 'id not supplied', testData: { data: { endpoint: 'vwap', base: 'AMPL' } } }, + { name: 'base/quote', testData: { id: jobID, data: { endpoint: 'vwap', base: 'AMPL' } } }, + { name: 'from/to', testData: { id: jobID, data: { endpoint: 'vwap', from: 'AMPL' } } }, + { name: 'coin/market', testData: { id: jobID, data: { endpoint: 'vwap', coin: 'AMPL' } } }, + ] + + 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: { endpoint: 'vwap' } }, + { name: 'empty data', testData: { data: { endpoint: 'vwap' } } }, + { name: 'base not supplied', testData: { id: jobID, data: { endpoint: 'vwap' } } }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { endpoint: 'vwap', base: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/bravenewcoin/tsconfig.json b/bravenewcoin/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/bravenewcoin/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/btc.com/package.json b/btc.com/package.json index 7dccf8425f..3c288b8967 100644 --- a/btc.com/package.json +++ b/btc.com/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/btc.com-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink adapter to query BTC address balance from BTC.com.", "keywords": [ "Chainlink", diff --git a/btc.com/src/index.ts b/btc.com/src/index.ts index c0939d32c9..14756ebf5a 100644 --- a/btc.com/src/index.ts +++ b/btc.com/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'BTC_COM' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/cfbenchmarks/package.json b/cfbenchmarks/package.json index 995d2138b3..4338e1c1de 100644 --- a/cfbenchmarks/package.json +++ b/cfbenchmarks/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cfbenchmarks-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink cfbenchmarks adapter.", "keywords": [ "Chainlink", diff --git a/cfbenchmarks/src/index.ts b/cfbenchmarks/src/index.ts index a510ba4404..76c6e61be8 100644 --- a/cfbenchmarks/src/index.ts +++ b/cfbenchmarks/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'cfbenchmarks' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coinapi/.eslintrc.js b/coinapi/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/coinapi/.eslintrc.js +++ b/coinapi/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coinapi/README.md b/coinapi/README.md index f56163a716..0c1f8be8d0 100644 --- a/coinapi/README.md +++ b/coinapi/README.md @@ -1,25 +1,44 @@ # Chainlink CoinApi External Adapter -Obtain an API key from [CoinAPI.io](https://www.coinapi.io/pricing). +### Environment Variables -## Input Params +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://www.coinapi.io/pricing) | | | -- `base`, `from`, or `coin`: The coin to query (required) -- `quote`, `to`, or `market`: The currency to convert to (required) +--- -## Output +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :----------------------------------------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | [List](https://docs.coinapi.io/#list-all-assets) | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | [List](https://docs.coinapi.io/#list-all-assets) | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { - "jobRunID": "1", - "data": { - "time": "2020-04-15T14:24:15.3834439Z", - "asset_id_base": "ETH", - "asset_id_quote": "USD", - "rate": 159.1132487376848, - "result": 159.1132487376848 - }, - "result": 159.1132487376848, - "statusCode": 200 + "jobRunID": "1", + "data": { + "time": "2020-04-15T14:24:15.3834439Z", + "asset_id_base": "ETH", + "asset_id_quote": "USD", + "rate": 159.1132487376848, + "result": 159.1132487376848 + }, + "result": 159.1132487376848, + "statusCode": 200 } ``` diff --git a/coinapi/adapter.js b/coinapi/adapter.js deleted file mode 100644 index 450818a291..0000000000 --- a/coinapi/adapter.js +++ /dev/null @@ -1,31 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], -} - -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 coin = validator.validated.data.base.toUpperCase() - const market = validator.validated.data.quote.toUpperCase() - const url = `https://rest.coinapi.io/v1/exchangerate/${coin}/${market}` - const config = { - url, - params: { - apikey: util.getRandomRequiredEnv('API_KEY'), - }, - } - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['rate']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/coinapi/index.js b/coinapi/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coinapi/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/coinapi/package.json b/coinapi/package.json index 5047147a5a..9c40a15fc7 100644 --- a/coinapi/package.json +++ b/coinapi/package.json @@ -1,15 +1,38 @@ { "name": "@chainlink/coinapi-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/coinapi/src/adapter.ts b/coinapi/src/adapter.ts new file mode 100644 index 0000000000..e7c19bba45 --- /dev/null +++ b/coinapi/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coinapi/src/config.ts b/coinapi/src/config.ts new file mode 100644 index 0000000000..edfa5cb3c2 --- /dev/null +++ b/coinapi/src/config.ts @@ -0,0 +1,19 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'COINAPI' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://rest.coinapi.io/v1/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + apikey: config.apiKey, + }, + } + return config +} diff --git a/coinapi/src/endpoint/index.ts b/coinapi/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/coinapi/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/coinapi/src/endpoint/price.ts b/coinapi/src/endpoint/price.ts new file mode 100644 index 0000000000..a6e0213a4d --- /dev/null +++ b/coinapi/src/endpoint/price.ts @@ -0,0 +1,36 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const symbol = validator.overrideSymbol(AdapterName).toUpperCase() + const quote = validator.validated.data.quote.toUpperCase() + const url = `exchangerate/${symbol}/${quote}` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['rate']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinapi/src/index.ts b/coinapi/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/coinapi/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coinapi/test/adapter_test.js b/coinapi/test/price.test.ts similarity index 56% rename from coinapi/test/adapter_test.js rename to coinapi/test/price.test.ts index 97ce68e8e7..aa4d6d434e 100644 --- a/coinapi/test/adapter_test.js +++ b/coinapi/test/price.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -27,30 +31,14 @@ 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) => { - 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) }) }) }) @@ -70,11 +58,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) + } }) }) }) @@ -92,11 +82,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/coinapi/tsconfig.json b/coinapi/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coinapi/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/coinbase/.editorconfig b/coinbase/.editorconfig deleted file mode 100644 index 9e4f06fb03..0000000000 --- a/coinbase/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 2 -insert_final_newline = true diff --git a/coinbase/.eslintrc.js b/coinbase/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/coinbase/.eslintrc.js +++ b/coinbase/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coinbase/README.md b/coinbase/README.md index 9288701654..5d368ff164 100644 --- a/coinbase/README.md +++ b/coinbase/README.md @@ -1,25 +1,38 @@ -# Chainlink Coinbase External Adapter +# Chainlink External Adapter for Coinbase -https://developers.coinbase.com/api/v2#prices +Query information from [Coinbase's API](https://developers.coinbase.com/api/v2) -## Input Params +### Input Parameters -- `base`, `from`, `coin`: The coin to query (required) -- `quote`, `to`, `market`: The currency to convert to (required) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | -## Endpoint +--- -``` -https://api.coinbase.com/v2/prices/BTC-USD/spot -``` +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | `BTC`, `ETH`, `USD` | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | `BTC`, `ETH`, `USD` | | -## Output +### Output ```json { + "jobRunID": "1", "data": { - "amount": "1015.00", - "currency": "USD" - } + "data": { + "base": "BTC", + "currency": "USD", + "amount": "46823.85" + }, + "result": 46823.85 + }, + "result": 46823.85, + "statusCode": 200 } ``` diff --git a/coinbase/adapter.js b/coinbase/adapter.js deleted file mode 100644 index bc5596d35a..0000000000 --- a/coinbase/adapter.js +++ /dev/null @@ -1,37 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - symbol: ['base', 'from', 'coin', 'sym', 'symbol'], - convert: ['quote', 'to', 'market', 'convert'], - cid: false, -} - -const currencyPairToken = ':currency_pair' -const baseUrl = `https://api.coinbase.com/v2/prices/${currencyPairToken}/spot` - -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 symbol = validator.validated.data.symbol - const convert = validator.validated.data.convert - const currencyPair = `${symbol}-${convert}`.toUpperCase() - const url = baseUrl.replace(currencyPairToken, currencyPair) - const params = { - symbol, - convert, - } - const config = { - url, - params, - } - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['data', 'amount']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/coinbase/index.js b/coinbase/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coinbase/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/coinbase/package.json b/coinbase/package.json index f78a3bd042..bd48f539eb 100644 --- a/coinbase/package.json +++ b/coinbase/package.json @@ -1,15 +1,38 @@ { "name": "@chainlink/coinbase-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 5000 -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 5000 --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/coinbase/src/adapter.ts b/coinbase/src/adapter.ts new file mode 100644 index 0000000000..e7c19bba45 --- /dev/null +++ b/coinbase/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coinbase/src/config.ts b/coinbase/src/config.ts new file mode 100644 index 0000000000..0c8cfb8eca --- /dev/null +++ b/coinbase/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://api.coinbase.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseUrl || DEFAULT_API_ENDPOINT + return config +} diff --git a/coinbase/src/endpoint/index.ts b/coinbase/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/coinbase/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/coinbase/src/endpoint/price.ts b/coinbase/src/endpoint/price.ts new file mode 100644 index 0000000000..228d974fc1 --- /dev/null +++ b/coinbase/src/endpoint/price.ts @@ -0,0 +1,40 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'price' + +const customParams = { + symbol: ['base', 'from', 'coin', 'sym', 'symbol'], + convert: ['quote', 'to', 'market', 'convert'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const symbol = validator.validated.data.symbol + const convert = validator.validated.data.convert + const currencyPair = `${symbol}-${convert}`.toUpperCase() + const url = `/v2/prices/${currencyPair}/spot` + + const params = { + symbol, + convert, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['data', 'amount']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinbase/src/index.ts b/coinbase/src/index.ts new file mode 100644 index 0000000000..0ce56cf69b --- /dev/null +++ b/coinbase/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'COINBASE' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coinbase/test/adapter_test.js b/coinbase/test/adapter_test.js deleted file mode 100644 index cf7ae9f847..0000000000 --- a/coinbase/test/adapter_test.js +++ /dev/null @@ -1,81 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const expectedJobRunId = '10' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'base/quote', - testData: { id: expectedJobRunId, data: { base: 'ETH', quote: 'USD' } }, - }, - { - name: 'from/to', - testData: { id: expectedJobRunId, data: { from: 'ETH', to: 'USD' } }, - }, - { - name: 'coin/market', - testData: { id: expectedJobRunId, data: { coin: 'ETH', market: 'USD' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, expectedJobRunId) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: { id: expectedJobRunId } }, - { name: 'empty data', testData: { id: expectedJobRunId, data: {} } }, - { - name: 'base not supplied', - testData: { id: expectedJobRunId, data: { quote: 'USD' } }, - }, - { - name: 'quote not supplied', - testData: { id: expectedJobRunId, data: { base: 'ETH' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, expectedJobRunId) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: expectedJobRunId, data: { base: 'not_real', quote: 'USD' } }, - }, - { - name: 'unknown quote', - testData: { id: expectedJobRunId, data: { base: 'ETH', quote: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, expectedJobRunId) - done() - }) - }) - }) - }) -}) diff --git a/alphavantage/test/adapter_test.js b/coinbase/test/price.test.ts similarity index 54% rename from alphavantage/test/adapter_test.js rename to coinbase/test/price.test.ts index a4d82c68e4..134f92905c 100644 --- a/alphavantage/test/adapter_test.js +++ b/coinbase/test/price.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -26,13 +30,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -52,11 +54,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) + } }) }) }) @@ -74,11 +78,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/coinbase/tsconfig.json b/coinbase/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coinbase/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/coincodex/package.json b/coincodex/package.json index c3402390df..1d5b4a2613 100644 --- a/coincodex/package.json +++ b/coincodex/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/coincodex-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink coincodex adapter.", "keywords": [ "Chainlink", diff --git a/coincodex/src/index.ts b/coincodex/src/index.ts index cfcdf7f5b2..ebd3870fae 100644 --- a/coincodex/src/index.ts +++ b/coincodex/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'CoinCodex' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/coingecko/.editorconfig b/coingecko/.editorconfig deleted file mode 100644 index 9e4f06fb03..0000000000 --- a/coingecko/.editorconfig +++ /dev/null @@ -1,6 +0,0 @@ -[*] -charset = utf-8 -end_of_line = lf -indent_style = space -indent_size = 2 -insert_final_newline = true diff --git a/coingecko/.eslintrc.js b/coingecko/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/coingecko/.eslintrc.js +++ b/coingecko/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coingecko/README.md b/coingecko/README.md index a18e620605..2fad15437c 100644 --- a/coingecko/README.md +++ b/coingecko/README.md @@ -1,17 +1,25 @@ -# Chainlink CoinGecko External Adapter +# Chainlink External Adapter for CoinGecko -## Price API (default) +### Input Parameters -### Endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :-------------------------------------------------------------------------------------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint), [globalmarketcap](#Global-Market-Capitalization-Endpoint), [dominance](#Dominance-Endpoint) | price | -https://api.coingecko.com/api/v3/simple/price +--- + +## Price Endpoint + +Query the price from [Coingecko](https://api.coingecko.com/api/v3/simple/price) ### Input Params -- `coinid`: The CoinGecko id of the coin to query (required if not using `from`) -- `base`, `from`, or `coin`: The ticker of the coin to query (required if not using `coinid`) -- `quote`, `to`, or `market`: The currency to convert to -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "globalMarketCap", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :------------------------: | :------------------------: | :--------------------------------------: | :------------------------------------------------------------------------------------: | :---------: | +| 🟡 (if not using `base`) | `coinid` | The CoinGecko id of the coin to query | [See list here](https://www.coingecko.com/api/documentations/v3#/coins/get_coins_list) | | +| 🟡 (if not using `coinid`) | `base`, `from`, or `coin` | The symbol of the currency to query | ↑ | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | ↑ | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ### Output @@ -29,16 +37,15 @@ https://api.coingecko.com/api/v3/simple/price } ``` -## Global API - -### Endpoint +## Global Market Capitalization Endpoint -https://api.coingecko.com/api/v3/global +Query the global market cap from [Coingecko](https://api.coingecko.com/api/v3/global) ### Input Params -- `market`, `to`, or `quote`: The ticker of the coin to query (required) -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "globalMarketCap", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------------------------------: | :-----------------------------: | :----------------------------------------------------------: | :---------: | +| ✅ | `market`, `to`, `quote` or `coin` | The ticker of the coin to query | [Supported tickers](https://api.coingecko.com/api/v3/global) | | ### Output @@ -97,3 +104,71 @@ https://api.coingecko.com/api/v3/global "statusCode": 200 } ``` + +## Dominance Endpoint + +Query the market dominance percentage from [Coingecko](https://api.coingecko.com/api/v3/global) + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------------------------------: | :-----------------------------: | :----------------------------------------------------------: | :---------: | +| ✅ | `market`, `to`, `quote` or `coin` | The ticker of the coin to query | [Supported tickers](https://api.coingecko.com/api/v3/global) | | + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "data": { + "active_cryptocurrencies": 5957, + "upcoming_icos": 0, + "ongoing_icos": 52, + "ended_icos": 3373, + "markets": 541, + "total_market_cap": { + "btc": 30989831.79926188, + "eth": 1002584380.7395577, + "ltc": 7874292256.0918255, + "bch": 1532212026.6933346, + "bnb": 12865279127.650606, + "eos": 146311079039.56436, + "xrp": 1516169090335.9976, + "xlm": 4552439205624.754, + "link": 37340996120.73051, + ... + }, + "total_volume": { + "btc": 3796204.0720707024, + "eth": 122814958.57775663, + "ltc": 964588014.5745924, + "bch": 187693485.16268748, + "bnb": 1575975801.6459033, + "eos": 17922869592.737896, + "xrp": 185728251510.49503, + "xlm": 557666410137.1028, + "link": 4574211388.00985, + ... + }, + "market_cap_percentage": { + "btc": 59.76998558746993, + "eth": 11.279054372152645, + "usdt": 4.2918601818054105, + "xrp": 2.9844952413180397, + "bch": 1.2106035362038838, + "bnb": 1.1507246890199336, + "link": 1.050511825669937, + "dot": 0.9735970943267678, + "ada": 0.8605029833772749, + "ltc": 0.8346408581868999 + }, + "market_cap_change_percentage_24h_usd": -0.2995489834770486, + "updated_at": 1603237580 + }, + "result": 30989831.79926188 + }, + "result": 59.76998558746993, + "statusCode": 200 +} +``` diff --git a/coingecko/adapter.js b/coingecko/adapter.js deleted file mode 100644 index cd83f519fa..0000000000 --- a/coingecko/adapter.js +++ /dev/null @@ -1,132 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const ENDPOINT_PRICE = 'price' -const ENDPOINT_MKTCAP = 'globalmarketcap' -const ENDPOINT_DOMINANCE = 'dominance' - -const DEFAULT_ENDPOINT = ENDPOINT_PRICE - -const customError = (data) => { - if (Object.keys(data).length === 0) return true - return false -} - -const presetTickers = { - COMP: 'compound-governance-token', - FNX: 'finnexus', - UNI: 'uniswap', -} - -const convertFromTicker = (ticker, coinId, callback) => { - if (typeof coinId !== 'undefined') return callback(coinId.toLowerCase()) - - // Correct common tickers that are misidentified - if (ticker in presetTickers) { - return callback(presetTickers[ticker]) - } - - Requester.request( - { - url: 'https://api.coingecko.com/api/v3/coins/list', - }, - customError, - ) - .then((response) => { - const coin = response.data.find((x) => x.symbol.toLowerCase() === ticker.toLowerCase()) - if (typeof coin === 'undefined') { - return callback('undefined') - } - return callback(coin.id.toLowerCase()) - }) - .catch(() => callback('Could not find data')) -} - -const priceParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - coinid: false, -} - -const price = (jobRunID, input, callback) => { - const validator = new Validator(input, priceParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const symbol = validator.validated.data.base - convertFromTicker(symbol, validator.validated.data.coinid, (coin) => { - const url = 'https://api.coingecko.com/api/v3/simple/price' - const market = validator.validated.data.quote - - const params = { - ids: coin, - vs_currencies: market, - } - - const config = { - url: url, - params, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - coin.toLowerCase(), - market.toLowerCase(), - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) - }) -} - -const globalParams = { - market: ['quote', 'to', 'market', 'coin'], -} - -const global = (jobRunID, input, path, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const market = validator.validated.data.market - const url = 'https://api.coingecko.com/api/v3/global' - - const config = { - url: url, - } - - const _handleResponse = (response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - path, - market.toLowerCase(), - ]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config, customError).then(_handleResponse).catch(_handleError) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint.toLowerCase()) { - case ENDPOINT_PRICE: - return price(jobRunID, input, callback) - case ENDPOINT_MKTCAP: - return global(jobRunID, input, 'total_market_cap', callback) - case ENDPOINT_DOMINANCE: - return global(jobRunID, input, 'market_cap_percentage', callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/coingecko/index.js b/coingecko/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coingecko/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/coingecko/package.json b/coingecko/package.json index 64f7d79cbe..1c5de19393 100644 --- a/coingecko/package.json +++ b/coingecko/package.json @@ -1,15 +1,38 @@ { "name": "@chainlink/coingecko-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", - "main": "index.js", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "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 5000 -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 5000 --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/coingecko/src/adapter.ts b/coingecko/src/adapter.ts new file mode 100644 index 0000000000..81554b2d39 --- /dev/null +++ b/coingecko/src/adapter.ts @@ -0,0 +1,42 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price, global } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case price.NAME: + return await price.execute(request, config) + + case 'globalmarketcap': + request.data.path = 'total_market_cap' + return await global.execute(request, config) + + case 'dominance': + request.data.path = 'market_cap_percentage' + return await global.execute(request, config) + + default: + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coingecko/src/config.ts b/coingecko/src/config.ts new file mode 100644 index 0000000000..0e3791389b --- /dev/null +++ b/coingecko/src/config.ts @@ -0,0 +1,13 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'COINGECKO' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://api.coingecko.com/api/v3' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseUrl || DEFAULT_API_ENDPOINT + return config +} diff --git a/coingecko/src/endpoint/global.ts b/coingecko/src/endpoint/global.ts new file mode 100644 index 0000000000..c88390da0d --- /dev/null +++ b/coingecko/src/endpoint/global.ts @@ -0,0 +1,39 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'global' + +const customError = (data: any) => { + if (Object.keys(data).length === 0) return true + return false +} + +const customParams = { + market: ['quote', 'to', 'market', 'coin'], + path: true, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const market = validator.validated.data.market.toLowerCase() + const path = validator.validated.data.path + const url = '/global' + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['data', path, market]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coingecko/src/endpoint/index.ts b/coingecko/src/endpoint/index.ts new file mode 100644 index 0000000000..b83c630de2 --- /dev/null +++ b/coingecko/src/endpoint/index.ts @@ -0,0 +1,2 @@ +export * as price from './price' +export * as global from './global' diff --git a/coingecko/src/endpoint/price.ts b/coingecko/src/endpoint/price.ts new file mode 100644 index 0000000000..2afac84749 --- /dev/null +++ b/coingecko/src/endpoint/price.ts @@ -0,0 +1,78 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const customError = (data: any) => { + if (Object.keys(data).length === 0) return true + return false +} + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + coinid: false, +} + +const getCoinId = async (config: Config, symbol: string): Promise => { + const url = '/coins/list' + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const coin = response.data.find((x: any) => x.symbol.toLowerCase() === symbol.toLowerCase()) + + if (typeof coin === 'undefined') { + throw new Error('Coin id not found') + } + + return coin.id.toLowerCase() +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const symbol = validator.overrideSymbol(AdapterName) + const quote = validator.validated.data.quote + const coinid = validator.validated.data.coinid as string | undefined + + // If coinid was provided or base was overridden, that symbol will be fetched + let coin = coinid?.toLowerCase() || (symbol !== validator.validated.data.base && symbol) + if (!coin) { + try { + coin = await getCoinId(config, symbol) + } catch (e) { + throw new AdapterError({ jobRunID, statusCode: 400, message: e.message }) + } + } + + const url = '/simple/price' + const params = { + ids: coin, + vs_currencies: quote, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [ + coin.toLowerCase(), + quote.toLowerCase(), + ]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coingecko/src/index.ts b/coingecko/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/coingecko/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coingecko/test/adapter_test.js b/coingecko/test/adapter_test.js deleted file mode 100644 index 8c5abb69f5..0000000000 --- a/coingecko/test/adapter_test.js +++ /dev/null @@ -1,134 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { base: 'ETH', quote: 'USD' } }, - }, - { - name: 'base/quote', - testData: { id: jobID, data: { base: 'ETH', quote: 'USD' } }, - }, - { - name: 'from/to', - testData: { id: jobID, data: { from: 'ETH', to: 'USD' } }, - }, - { - name: 'coin/market', - testData: { id: jobID, data: { coin: 'ETH', market: 'USD' } }, - }, - { - name: 'with coinid', - testData: { - id: jobID, - data: { coin: 'ETH', market: 'USD', coinid: 'ethereum' }, - }, - }, - { - name: 'global marketcap', - testData: { - id: jobID, - data: { to: 'BTC', endpoint: 'globalmarketcap' }, - }, - }, - { - name: 'BTC dominance', - testData: { - id: jobID, - data: { market: 'BTC', endpoint: 'dominance' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - { - name: 'base not supplied', - testData: { id: jobID, data: { quote: 'USD' } }, - }, - { - name: 'quote not supplied', - testData: { id: jobID, data: { base: 'ETH' } }, - }, - { - name: 'market cap: market not supplied', - testData: { - id: jobID, - data: { endpoint: 'globalMarketCap' }, - }, - }, - { - name: 'market dominance: market not supplied', - testData: { - id: jobID, - data: { endpoint: 'dominance' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real', quote: 'USD' } }, - }, - { - name: 'unknown quote', - testData: { id: jobID, data: { base: 'ETH', quote: 'not_real' } }, - }, - { - name: 'market cap: unknown market', - testData: { - id: jobID, - data: { endpoint: 'globalMarketCap', market: 'not_real' }, - }, - }, - { - name: 'market dominance: unknown market', - testData: { - id: jobID, - data: { endpoint: 'dominance', market: 'not_real' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/cryptoapis/test/stats.test.ts b/coingecko/test/dominance.test.ts similarity index 64% rename from cryptoapis/test/stats.test.ts rename to coingecko/test/dominance.test.ts index f7eb10b9dc..72c3a6aa57 100644 --- a/cryptoapis/test/stats.test.ts +++ b/coingecko/test/dominance.test.ts @@ -4,38 +4,18 @@ import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' import { AdapterRequest } from '@chainlink/types' import { makeExecute } from '../src/adapter' -describe('bc_info endpoint', () => { +describe('dominane endpoint', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ { - name: 'BTC difficulty', + name: 'BTC dominance', testData: { id: jobID, - data: { blockchain: 'BTC', endpoint: 'difficulty' }, - }, - }, - { - name: 'BTC testnet difficulty', - testData: { - id: jobID, - 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' }, + data: { market: 'BTC', endpoint: 'dominance' }, }, }, ] @@ -55,12 +35,34 @@ describe('bc_info endpoint', () => { { name: 'empty body', testData: {} }, { name: 'empty data', testData: { data: {} } }, { - name: 'unknown blockchain', - testData: { id: jobID, data: { blockchain: 'not_real' } }, + name: 'market not supplied', + testData: { + id: jobID, + data: { endpoint: 'dominance' }, + }, }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ { - name: 'unknown network', - testData: { id: jobID, data: { blockchain: 'BTC', network: 'not_real' } }, + name: 'unknown market', + testData: { + id: jobID, + data: { endpoint: 'dominance', market: 'not_real' }, + }, }, ] @@ -70,7 +72,7 @@ describe('bc_info endpoint', () => { await execute(req.testData as AdapterRequest) } catch (error) { const errorResp = Requester.errored(jobID, error) - assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) } }) }) diff --git a/coingecko/test/global.test.ts b/coingecko/test/global.test.ts new file mode 100644 index 0000000000..be26f81cfa --- /dev/null +++ b/coingecko/test/global.test.ts @@ -0,0 +1,80 @@ +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('global endpoint', () => { + const jobID = '1' + const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'global marketcap', + testData: { + id: jobID, + data: { to: 'BTC', endpoint: 'globalMarketCap' }, + }, + }, + ] + + 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: {} } }, + { + name: 'market not supplied', + testData: { + id: jobID, + data: { endpoint: 'globalMarketCap' }, + }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown market', + testData: { + id: jobID, + data: { endpoint: 'globalMarketCap', market: 'not_real' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/coingecko/test/price.test.ts b/coingecko/test/price.test.ts new file mode 100644 index 0000000000..0896e9b75a --- /dev/null +++ b/coingecko/test/price.test.ts @@ -0,0 +1,98 @@ +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('price endpoint', () => { + const jobID = '1' + const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { base: 'ETH', quote: 'USD' } }, + }, + { + name: 'base/quote', + testData: { id: jobID, data: { base: 'ETH', quote: 'USD' } }, + }, + { + name: 'from/to', + testData: { id: jobID, data: { from: 'ETH', to: 'USD' } }, + }, + { + name: 'coin/market', + testData: { id: jobID, data: { coin: 'ETH', market: 'USD' } }, + }, + { + name: 'with coinid', + testData: { + id: jobID, + data: { coin: 'ETH', market: 'USD', coinid: 'ethereum' }, + }, + }, + ] + + 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: {} } }, + { + name: 'base not supplied', + testData: { id: jobID, data: { quote: 'USD' } }, + }, + { + name: 'quote not supplied', + testData: { id: jobID, data: { base: 'ETH' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { base: 'not_real', quote: 'USD' } }, + }, + { + name: 'unknown quote', + testData: { id: jobID, data: { base: 'ETH', quote: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/coingecko/tsconfig.json b/coingecko/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coingecko/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/coinlore/.eslintrc.js b/coinlore/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/coinlore/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coinlore/README.md b/coinlore/README.md index f252eea6b2..ee5b20f1d3 100644 --- a/coinlore/README.md +++ b/coinlore/README.md @@ -1,19 +1,38 @@ # Chainlink External Adapter for Coin Lore -## Global API +### Input Parameters -### Endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [global](#Global-Endpoint) | global | -https://api.coinlore.net/api/global/ +There are two Chainlink "endpoint" behaviors (`dominance` and `globalmarketcap`) that use the `global` API endpoint. +The default is to use the Chainlink `dominance` behavior. -Use the `API_DEFAULT_ENDPOINT` env variable to set the default API endpoint to use (either `dominance` or `globalmarketcap`). Defaults to `dominance`. +--- + +## Global Endpoint ### Input Params -- `market`, `to`, `quote`: The coin to query (required) -- `endpoint`: The endpoint to use (defaults to "dominance", one of "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-----------------------------------------------------------------------------: | :----------: | :---------: | +| | `field` | The object path to access the value that will be returned as the result | | `d` | +| | `base` | When using a field of `d`, the currency to prefix the field with (e.g. `usd_d`) | `btc`, `eth` | `btc` | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "eth", + "field": "d" + } +} +``` -### Output +### Sample Output ```json { @@ -30,9 +49,9 @@ Use the `API_DEFAULT_ENDPOINT` env variable to set the default API endpoint to u "avg_change_percent": "-0.23", "volume_ath": 281440138896.8489, "mcap_ath": 825596367558, - "result": 60.17 + "result": 11.87 }, - "result": 60.17, + "result": 11.87, "statusCode": 200 } ``` diff --git a/coinlore/adapter.js b/coinlore/adapter.js deleted file mode 100644 index 2f49b9893f..0000000000 --- a/coinlore/adapter.js +++ /dev/null @@ -1,53 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const ENDPOINT_DOMINANCE = 'dominance' -const ENDPOINT_MKTCAP = 'globalmarketcap' - -const DEFAULT_ENDPOINT = process.env.API_DEFAULT_ENDPOINT || ENDPOINT_DOMINANCE - -const globalParams = { - base: ['market', 'to', 'quote'], -} - -const global = (jobRunID, input, path, coinPrefix, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const url = 'https://api.coinlore.net/api/global/' - const config = { url } - - const symbol = validator.validated.data.base.toLowerCase() - const dataKey = coinPrefix ? `${symbol}_${path}` : path - - const _handleResponse = (response) => { - response.data = response.data[0] - response.data.result = Requester.validateResultNumber(response.data, [dataKey]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config).then(_handleResponse).catch(_handleError) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint.toLowerCase()) { - case ENDPOINT_DOMINANCE: - return global(jobRunID, input, 'd', true, callback) - case ENDPOINT_MKTCAP: - return global(jobRunID, input, 'total_mcap', false, callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/coinlore/index.js b/coinlore/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coinlore/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/coinlore/package.json b/coinlore/package.json index 15d548ffa9..d0e28ec338 100644 --- a/coinlore/package.json +++ b/coinlore/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/coinlore-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Coin Lore adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/coinlore/src/adapter.ts b/coinlore/src/adapter.ts new file mode 100644 index 0000000000..d67f984520 --- /dev/null +++ b/coinlore/src/adapter.ts @@ -0,0 +1,40 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { global } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case 'dominance': + case global.NAME: { + return await global.execute(request, config) + } + case 'globalmarketcap': { + request.data.field = 'total_mcap' + return await global.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coinlore/src/config.ts b/coinlore/src/config.ts new file mode 100644 index 0000000000..97fa01b6eb --- /dev/null +++ b/coinlore/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'dominance' +export const DEFAULT_BASE_URL = 'https://api.coinlore.net/api' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/coinlore/src/endpoint/global.ts b/coinlore/src/endpoint/global.ts new file mode 100644 index 0000000000..73a22602d6 --- /dev/null +++ b/coinlore/src/endpoint/global.ts @@ -0,0 +1,34 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'global' + +const customParams = { + base: false, + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = `${NAME}` + const symbol = validator.validated.data.base || 'btc' + let field = validator.validated.data.field || 'd' + if (field === 'd') field = `${symbol.toLowerCase()}_d` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data[0], [field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinlore/src/endpoint/index.ts b/coinlore/src/endpoint/index.ts new file mode 100644 index 0000000000..c0999a40a4 --- /dev/null +++ b/coinlore/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as global from './global' diff --git a/coinlore/src/index.ts b/coinlore/src/index.ts new file mode 100644 index 0000000000..692437d008 --- /dev/null +++ b/coinlore/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'COINLORE' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coinlore/test/adapter_test.js b/coinlore/test/adapter_test.js deleted file mode 100644 index afdd078eea..0000000000 --- a/coinlore/test/adapter_test.js +++ /dev/null @@ -1,79 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { market: 'BTC' } }, - }, - { - name: 'id is supplied', - testData: { id: jobID, data: { market: 'BTC' } }, - }, - { - name: 'to', - testData: { id: jobID, data: { to: 'BTC' } }, - }, - { - name: 'quote', - testData: { id: jobID, data: { quote: 'BTC' } }, - }, - { - name: 'global marketcap', - testData: { id: jobID, data: { endpoint: 'globalmarketcap', quote: 'USD' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { - name: 'market not supplied', - testData: { id: jobID, data: {} }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown market', - testData: { id: jobID, data: { market: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/coinlore/test/global.test.ts b/coinlore/test/global.test.ts new file mode 100644 index 0000000000..7f30d61cad --- /dev/null +++ b/coinlore/test/global.test.ts @@ -0,0 +1,56 @@ +import { assertError, assertSuccess } from '@chainlink/adapter-test-helpers' +import { Requester } from '@chainlink/external-adapter' +import { AdapterRequest } from '@chainlink/types' +import { assert } from 'chai' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { base: 'BTC' } }, + }, + { + name: 'id is supplied', + testData: { id: jobID, data: { base: 'ETH' } }, + }, + { + name: 'global marketcap', + testData: { id: jobID, data: { endpoint: 'globalmarketcap', base: 'USD' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.isAbove(data.result, 0) + assert.isAbove(data.data.result, 0) + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown market', + testData: { id: jobID, data: { base: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/coinlore/tsconfig.json b/coinlore/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coinlore/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/coinmarketcap/.eslintrc.js b/coinmarketcap/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/coinmarketcap/.eslintrc.js +++ b/coinmarketcap/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coinmarketcap/README.md b/coinmarketcap/README.md index 6a24b5584b..f1e66b9620 100644 --- a/coinmarketcap/README.md +++ b/coinmarketcap/README.md @@ -1,17 +1,34 @@ # Chainlink CoinMarketCap Pro External Adapter -## Price API (default) +### Environment Variables -### Endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :----------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://coinmarketcap.com/api/) | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------------------------------------------------------------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint), [dominance](#Dominance-Endpoint), [globalmarketcap](#Market-Capitalization-Endpoint) | `price` | + +--- + +## Price Endpoint https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest ### Input Params -- `base`, `from`, `coin`, or `sym`: The coin to query (required) -- `quote`, `to`, `market`, or `convert`: The currency to convert to (required) -- `cid`: The CMC coin ID (optional, use in place of `sym` or `coin`) -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------------------------------: | :----------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, `sym` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market`, `convert` | The symbol of the currency to convert to | | | +| 🟡 | `cid` | The CMC coin ID (optional to use in place of `base`) | | | +| 🟡 | `slug` | The CMC coin name (optional to use in place of `base`) | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ### Output @@ -62,16 +79,15 @@ https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest } ``` -## Dominance - -### Endpoint +## Dominance Endpoint https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest ### Input Params -- `market`, `to`, or `quote`: The coin to query (required) -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------: | :----------: | :---------: | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to query | `BTC`, `ETH` | | ### Output @@ -125,3 +141,66 @@ https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest "statusCode": 200 } ``` + +## Market Capitalization Endpoint + +https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to query | `USD` | | + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "status": { + "timestamp": "2021-02-09T22:07:14.787Z", + "error_code": 0, + "error_message": null, + "elapsed": 24, + "credit_count": 1, + "notice": null + }, + "data": { + "active_cryptocurrencies": 4039, + "total_cryptocurrencies": 8417, + "active_market_pairs": 33557, + "active_exchanges": 365, + "total_exchanges": 1271, + "eth_dominance": 14.492658185967, + "btc_dominance": 62.891411561113, + "defi_volume_24h": 16138915987.218157, + "defi_volume_24h_reported": 27444522785.122807, + "defi_market_cap": 58964101882.03461, + "defi_24h_percentage_change": 1.045042749245, + "stablecoin_volume_24h": 169445823565.66757, + "stablecoin_volume_24h_reported": 312699703386.8201, + "stablecoin_market_cap": 47755045514.65084, + "stablecoin_24h_percentage_change": 5.670684885241, + "derivatives_volume_24h": 131232253607.2902, + "derivatives_volume_24h_reported": 131237691538.61276, + "derivatives_24h_percentage_change": 2.369666048721, + "quote": { + "USD": { + "total_market_cap": 1393964637158.628, + "total_volume_24h": 215007147237, + "total_volume_24h_reported": 404197132217.27, + "altcoin_volume_24h": 116627624277.44908, + "altcoin_volume_24h_reported": 199039183261.18234, + "altcoin_market_cap": 517280600186.8229, + "last_updated": "2021-02-09T22:05:18.000Z" + } + }, + "last_updated": "2021-02-09T22:05:18.000Z" + }, + "result": 1393964637158.628 + }, + "result": 1393964637158.628, + "statusCode": 200 +} +``` diff --git a/coinmarketcap/adapter.js b/coinmarketcap/adapter.js deleted file mode 100644 index e5c85668d1..0000000000 --- a/coinmarketcap/adapter.js +++ /dev/null @@ -1,184 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const ENDPOINT_PRICE = 'price' -const ENDPOINT_DOMINANCE = 'dominance' -const ENDPOINT_MKTCAP = 'globalmarketcap' - -const DEFAULT_ENDPOINT = ENDPOINT_PRICE - -const customError = (data) => { - if (Object.keys(data).length === 0) return true - return false -} - -// Defaults we use when there are multiple currencies with the same symbol -const presetSlugs = { - COMP: 'compound', - BNT: 'bancor', - RCN: 'ripio-credit-network', - UNI: 'uniswap', - CRV: 'curve-dao-token', - FNX: 'finnexus', - ETC: 'ethereum-classic', - BAT: 'basic-attention-token', - CRO: 'crypto-com-coin', - LEO: 'unus-sed-leo', - FTT: 'ftx-token', - HT: 'huobi-token', - OKB: 'okb', - KCS: 'kucoin-shares', -} - -// TODO: fix validation. CMC should support at least one "id" or "slug" or "symbol" for this request. -const priceParams = { - symbol: ['base', 'from', 'coin', 'sym', 'symbol'], - convert: ['quote', 'to', 'market', 'convert'], - cid: false, - slug: false, -} - -const price = (jobRunID, input, callback) => { - const url = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest' - const validator = new Validator(input, priceParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const symbol = validator.validated.data.symbol - // CMC allows a coin name to be specified instead of a symbol - const slug = validator.validated.data.slug - // CMC allows a coin ID to be specified instead of a symbol - const cid = validator.validated.data.cid || '' - // Free CMCPro API only supports a single symbol to convert - const convert = validator.validated.data.convert - - const params = { convert } - if (cid) { - params.id = cid - } else if (slug) { - params.slug = slug - } else { - const slugForSymbol = presetSlugs[symbol] - if (slugForSymbol) { - params.slug = slugForSymbol - } else { - params.symbol = symbol - } - } - - const config = { - url, - headers: { - 'X-CMC_PRO_API_KEY': util.getRandomRequiredEnv('API_KEY'), - }, - params, - } - - // CMC API currently uses ID as key in response, when querying with "slug" param - const _keyForSlug = (data, slug) => { - if (!data || !data.data) return - // First try to find slug key in response (in case the API starts returning keys correctly) - if (Object.keys(data.data).includes(slug)) return slug - // Fallback to ID - const _iEqual = (s1, s2) => s1.toUpperCase() === s2.toUpperCase() - const o = Object.values(data.data).find((o) => _iEqual(o.slug, slug)) - return o && o.id - } - - Requester.request(config) - .then((response) => { - const key = params.id || _keyForSlug(response.data, params.slug || '') || params.symbol - const path = ['data', key, 'quote', convert, 'price'] - response.data.result = Requester.validateResultNumber(response.data, path) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -const globalParams = { - market: ['market', 'to', 'quote'], -} - -const dominance = (jobRunID, input, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const url = 'https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest' - - const headers = { - 'X-CMC_PRO_API_KEY': util.getRandomRequiredEnv('API_KEY'), - } - - const config = { - url, - headers, - } - - const symbol = validator.validated.data.market.toLowerCase() - const dataKey = `${symbol}_dominance` - - const _handleResponse = (response) => { - response.data.result = Requester.validateResultNumber(response.data, ['data', dataKey]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config, customError).then(_handleResponse).catch(_handleError) -} - -const marketcap = (jobRunID, input, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const convert = validator.validated.data.market.toUpperCase() - const url = 'https://pro-api.coinmarketcap.com/v1/global-metrics/quotes/latest' - - const params = { convert } - const headers = { - 'X-CMC_PRO_API_KEY': util.getRandomRequiredEnv('API_KEY'), - } - - const config = { - url, - params, - headers, - } - - const _handleResponse = (response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - 'quote', - convert, - 'total_market_cap', - ]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config, customError).then(_handleResponse).catch(_handleError) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint.toLowerCase()) { - case ENDPOINT_PRICE: - return price(jobRunID, input, callback) - case ENDPOINT_DOMINANCE: - return dominance(jobRunID, input, callback) - case ENDPOINT_MKTCAP: - return marketcap(jobRunID, input, callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/coinmarketcap/index.js b/coinmarketcap/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coinmarketcap/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/coinmarketcap/package.json b/coinmarketcap/package.json index b120b1d2a1..cda5346c63 100644 --- a/coinmarketcap/package.json +++ b/coinmarketcap/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/coinmarketcap-adapter", - "version": "0.0.4", + "version": "0.0.5", + "description": "Coinmarketcap adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "coinmarketcap" + ], + "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 4000 -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 4000 --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/coinmarketcap/src/adapter.ts b/coinmarketcap/src/adapter.ts new file mode 100644 index 0000000000..0a251165f8 --- /dev/null +++ b/coinmarketcap/src/adapter.ts @@ -0,0 +1,41 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { dominance, marketcap, price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case price.NAME: { + return await price.execute(request, config) + } + case dominance.NAME: { + return await dominance.execute(request, config) + } + case marketcap.NAME: { + return await marketcap.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coinmarketcap/src/config.ts b/coinmarketcap/src/config.ts new file mode 100644 index 0000000000..b5b0c8549f --- /dev/null +++ b/coinmarketcap/src/config.ts @@ -0,0 +1,16 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'COINMARKETCAP' + +export const DEFAULT_ENDPOINT = 'price' +const DEFAULT_API_ENDPOINT = 'https://pro-api.coinmarketcap.com/v1/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api.baseURL = config.api.baseUrl || DEFAULT_API_ENDPOINT + config.api.headers = { + 'X-CMC_PRO_API_KEY': config.apiKey, + } + return config +} diff --git a/coinmarketcap/src/endpoint/dominance.ts b/coinmarketcap/src/endpoint/dominance.ts new file mode 100644 index 0000000000..e351c270b8 --- /dev/null +++ b/coinmarketcap/src/endpoint/dominance.ts @@ -0,0 +1,33 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'dominance' + +const dominanceParams = { + market: ['market', 'to', 'quote'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, dominanceParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const url = 'global-metrics/quotes/latest' + + const options = { + ...config.api, + url, + } + + const symbol = validator.validated.data.market.toLowerCase() + const dataKey = `${symbol}_dominance` + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['data', dataKey]) + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinmarketcap/src/endpoint/index.ts b/coinmarketcap/src/endpoint/index.ts new file mode 100644 index 0000000000..f44ee9afd8 --- /dev/null +++ b/coinmarketcap/src/endpoint/index.ts @@ -0,0 +1,3 @@ +export * as dominance from './dominance' +export * as price from './price' +export * as marketcap from './marketcap' diff --git a/coinmarketcap/src/endpoint/marketcap.ts b/coinmarketcap/src/endpoint/marketcap.ts new file mode 100644 index 0000000000..139c9e922c --- /dev/null +++ b/coinmarketcap/src/endpoint/marketcap.ts @@ -0,0 +1,38 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'globalmarketcap' + +const marketcapParams = { + market: ['market', 'to', 'quote'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, marketcapParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const convert = validator.validated.data.market.toUpperCase() + const url = '/global-metrics/quotes/latest' + + const params = { convert } + + const options = { + ...config.api, + url, + params, + } + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, [ + 'data', + 'quote', + convert, + 'total_market_cap', + ]) + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinmarketcap/src/endpoint/price.ts b/coinmarketcap/src/endpoint/price.ts new file mode 100644 index 0000000000..5bd0d6cc5d --- /dev/null +++ b/coinmarketcap/src/endpoint/price.ts @@ -0,0 +1,104 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +// Coin IDs fetched from the ID map: https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyMap +const presetIds: { [symbol: string]: number } = { + COMP: 5692, + BNT: 1727, + RCN: 2096, + UNI: 7083, + CRV: 6538, + FNX: 5712, + ETC: 1321, + BAT: 1697, + CRO: 3635, + LEO: 3957, + FTT: 4195, + HT: 2502, + OKB: 3897, + KCS: 2087, + BTC: 1, + ETH: 1027, + BNB: 1839, + LINK: 1975, + BCH: 1831, + MKR: 1518, + AAVE: 7278, + UMA: 5617, + SNX: 2586, + REN: 2539, + KNC: 1982, + SUSHI: 6758, + YFI: 5864, + BAL: 5728, + '1INCH': 8104, +} + +const priceParams = { + base: ['base', 'from', 'coin', 'sym', 'symbol'], + convert: ['quote', 'to', 'market', 'convert'], + cid: false, + slug: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const url = 'cryptocurrency/quotes/latest' + const validator = new Validator(request, priceParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const symbol = validator.overrideSymbol(AdapterName) + // CMC allows a coin name to be specified instead of a symbol + const slug = validator.validated.data.slug + // CMC allows a coin ID to be specified instead of a symbol + const cid = validator.validated.data.cid || '' + // Free CMCPro API only supports a single symbol to convert + const convert = validator.validated.data.convert + + const params: Record = { convert } + if (cid) { + params.id = cid + } else if (slug) { + params.slug = slug + } else { + const idForSymbol = presetIds[symbol] + if (idForSymbol) { + params.id = String(idForSymbol) + } else { + params.symbol = symbol + } + } + + const options = { + ...config.api, + url, + params, + } + const response = await Requester.request(options) + + // CMC API currently uses ID as key in response, when querying with "slug" param + const _keyForSlug = (data: any, slug: string) => { + if (!data || !data.data) return + // First try to find slug key in response (in case the API starts returning keys correctly) + if (Object.keys(data.data).includes(slug)) return slug + // Fallback to ID + const _iEqual = (s1: string, s2: string) => s1.toUpperCase() === s2.toUpperCase() + const o: any = Object.values(data.data).find((o: any) => _iEqual(o.slug, slug)) + return o && o.id + } + + const key = params.id || _keyForSlug(response.data, params.slug || '') || params.symbol + const path = ['data', key.toUpperCase(), 'quote', convert.toUpperCase(), 'price'] + + const result = Requester.validateResultNumber(response.data, path) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinmarketcap/src/index.ts b/coinmarketcap/src/index.ts new file mode 100644 index 0000000000..b8e832f1bb --- /dev/null +++ b/coinmarketcap/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeConfig, makeExecute, ...expose(makeExecute()) } diff --git a/coinmarketcap/test/dominance.test.ts b/coinmarketcap/test/dominance.test.ts new file mode 100644 index 0000000000..a78566bdf7 --- /dev/null +++ b/coinmarketcap/test/dominance.test.ts @@ -0,0 +1,73 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'market dominance', + testData: { id: jobID, data: { endpoint: 'dominance', market: 'BTC' } }, + }, + { + name: 'market dominance', + testData: { id: jobID, data: { endpoint: 'dominance', market: 'ETH' } }, + }, + ] + + 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: 'dominance market not supplied', + testData: { id: jobID, data: { endpoint: 'dominance' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'dominance unknown market', + testData: { id: jobID, data: { endpoint: 'dominance', market: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/coinmarketcap/test/marketcap.test.ts b/coinmarketcap/test/marketcap.test.ts new file mode 100644 index 0000000000..a61138354b --- /dev/null +++ b/coinmarketcap/test/marketcap.test.ts @@ -0,0 +1,69 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'marketcap', + testData: { id: jobID, data: { endpoint: 'globalMarketCap', to: 'USD' } }, + }, + ] + + 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: 'marketcap market not supplied', + testData: { id: jobID, data: { endpoint: 'globalMarketCap' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'marketcap unknown market', + testData: { id: jobID, data: { endpoint: 'globalMarketCap', market: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/coinmarketcap/test/adapter_test.js b/coinmarketcap/test/price.test.ts similarity index 53% rename from coinmarketcap/test/adapter_test.js rename to coinmarketcap/test/price.test.ts index e521a50bf6..7acafa35aa 100644 --- a/coinmarketcap/test/adapter_test.js +++ b/coinmarketcap/test/price.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -27,24 +31,14 @@ describe('execute', () => { name: 'sym/convert', testData: { id: jobID, data: { sym: 'ETH', convert: 'USD' } }, }, - { - name: 'market dominance', - testData: { id: jobID, data: { endpoint: 'dominance', market: 'BTC' } }, - }, - { - name: 'marketcap', - testData: { id: jobID, data: { endpoint: 'globalMarketCap', to: 'USD' } }, - }, ] 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) }) }) }) @@ -61,18 +55,16 @@ describe('execute', () => { name: 'quote not supplied', testData: { id: jobID, data: { base: 'ETH' } }, }, - { - name: 'dominance market not supplied', - testData: { id: jobID, data: { endpoint: 'dominance' } }, - }, ] 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) + } }) }) }) @@ -87,18 +79,16 @@ describe('execute', () => { name: 'unknown quote', testData: { id: jobID, data: { base: 'ETH', quote: 'not_real' } }, }, - { - name: 'dominance unknown market', - testData: { id: jobID, data: { endpoint: 'dominance', market: 'not_real' } }, - }, ] 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/coinmarketcap/tsconfig.json b/coinmarketcap/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coinmarketcap/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/coinpaprika/.eslintrc.js b/coinpaprika/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/coinpaprika/.eslintrc.js +++ b/coinpaprika/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/coinpaprika/README.md b/coinpaprika/README.md index d79c7f4800..bad61a458f 100644 --- a/coinpaprika/README.md +++ b/coinpaprika/README.md @@ -1,17 +1,25 @@ # Chainlink CoinPaprika External Adapter -## Price API (default) +### Input Parameters -### Endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------------------------------------------------------------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint), [dominance](#Dominance-Endpoint), [globalmarketcap](#Market-Capitalization-Endpoint) | `price` | + +--- + +## Price Endpoint https://api.coinpaprika.com/v1/tickers/`{COIN}` ### Input Params -- `coinid`: The CoinPaprika id of the coin to query (required if not using `from`) -- `base`, `from`, or `coin`: The ticker of the coin to query (required if not using `coinid`) -- `quote`, `to`, or `market`: The currency to convert the coin to (required) -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "globalMarketCap", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :----------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| 🟡 | `coinid` | The coin ID (optional to use in place of `base`) | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ### Output @@ -53,16 +61,51 @@ https://api.coinpaprika.com/v1/tickers/`{COIN}` } ``` -## Global API +## Dominance Endpoint + +Returns Bitcoin's dominance from the [global endpoint](https://api.coinpaprika.com/v1/global) + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to query | `BTC` | | + +### Output + +```json +{ + "jobRunID": "1", + "data": { + "market_cap_usd": 368198248292, + "volume_24h_usd": 59351367068, + "bitcoin_dominance_percentage": 59.98, + "cryptocurrencies_number": 2435, + "market_cap_ath_value": 835692000000, + "market_cap_ath_date": "2018-01-07T11:17:00Z", + "volume_24h_ath_value": 197699715619, + "volume_24h_ath_date": "2020-03-13T10:00:00Z", + "volume_24h_percent_from_ath": -69.98, + "volume_24h_percent_to_ath": 233.1, + "market_cap_change_24h": -0.2, + "volume_24h_change_24h": 16.98, + "last_updated": 1603238207, + "result": 59.98 + }, + "result": 59.98, + "statusCode": 200 +} +``` -### Endpoint +## Market Capitalization Endpoint -https://api.coinpaprika.com/v1/global +Returns the global market capitilization from the [global endpoint](https://api.coinpaprika.com/v1/global) ### Input Params -- `market`, `to`, or `quote`: The ticker of the coin to query (required) -- `endpoint`: The endpoint to use (defaults to "price", one of "price", "globalMarketCap", "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to query | `USD` | | ### Output diff --git a/coinpaprika/adapter.js b/coinpaprika/adapter.js deleted file mode 100644 index cbb5f9e6d1..0000000000 --- a/coinpaprika/adapter.js +++ /dev/null @@ -1,133 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const ENDPOINT_PRICE = 'price' -const ENDPOINT_MKTCAP = 'globalmarketcap' -const ENDPOINT_DOMINANCE = 'dominance' - -const DEFAULT_ENDPOINT = ENDPOINT_PRICE - -const convertFromTicker = (ticker, coinid, callback) => { - if (typeof coinId !== 'undefined') return callback(coinid.toLowerCase()) - - Requester.request({ - url: 'https://api.coinpaprika.com/v1/coins', - }) - .then((response) => { - const coin = response.data - .sort((a, b) => (a.rank > b.rank ? 1 : -1)) - .find((x) => x.symbol.toLowerCase() === ticker.toLowerCase() && x.rank !== 0) - if (!coin) return callback('Could not find coin', null) - return callback(null, coin.id.toLowerCase()) - }) - .catch((error) => callback(error, null)) -} - -const priceParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - coinid: false, -} - -const price = (jobRunID, input, callback) => { - const validator = new Validator(input, priceParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const symbol = validator.validated.data.base - convertFromTicker(symbol, validator.validated.data.coinid, (error, coin) => { - if (error) return callback(500, Requester.errored(jobRunID, error)) - const url = `https://api.coinpaprika.com/v1/tickers/${coin}` - const market = validator.validated.data.quote - - const params = { - quotes: market.toUpperCase(), - } - - const config = { - url, - params, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'quotes', - market.toUpperCase(), - 'price', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) - }) -} - -const globalParams = { - market: ['market', 'to', 'quote'], -} - -const convert = { - BTC: 'bitcoin', -} - -const dominance = (jobRunID, input, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const url = 'https://api.coinpaprika.com/v1/global' - const config = { url } - - const symbol = validator.validated.data.market.toUpperCase() - - const _handleResponse = (response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - `${convert[symbol]}_dominance_percentage`, - ]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config).then(_handleResponse).catch(_handleError) -} - -const marketcap = (jobRunID, input, callback) => { - const validator = new Validator(input, globalParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const url = 'https://api.coinpaprika.com/v1/global' - const config = { url } - - const symbol = validator.validated.data.market.toLowerCase() - - const _handleResponse = (response) => { - response.data.result = Requester.validateResultNumber(response.data, [`market_cap_${symbol}`]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config).then(_handleResponse).catch(_handleError) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint.toLowerCase()) { - case ENDPOINT_PRICE: - return price(jobRunID, input, callback) - case ENDPOINT_MKTCAP: - return marketcap(jobRunID, input, callback) - case ENDPOINT_DOMINANCE: - return dominance(jobRunID, input, callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/coinpaprika/index.js b/coinpaprika/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/coinpaprika/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/coinpaprika/package.json b/coinpaprika/package.json index b2222c7ed6..d011fa2189 100644 --- a/coinpaprika/package.json +++ b/coinpaprika/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/coinpaprika-adapter", - "version": "0.0.2", + "version": "0.0.3", + "description": "Chainlink coinpaprika adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "coinpaprika" + ], + "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 5000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" }, - "dependencies": {} + "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" + } } diff --git a/coinpaprika/src/adapter.ts b/coinpaprika/src/adapter.ts new file mode 100644 index 0000000000..690cf571f9 --- /dev/null +++ b/coinpaprika/src/adapter.ts @@ -0,0 +1,41 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price, dominance, marketcap } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case price.NAME: { + return await price.execute(request, config) + } + case dominance.NAME: { + return await dominance.execute(request, config) + } + case marketcap.NAME: { + return await marketcap.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/coinpaprika/src/config.ts b/coinpaprika/src/config.ts new file mode 100644 index 0000000000..b9a94b634a --- /dev/null +++ b/coinpaprika/src/config.ts @@ -0,0 +1,16 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'COINPAPRIKA' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://api.coinpaprika.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + } + return config +} diff --git a/coinpaprika/src/endpoint/dominance.ts b/coinpaprika/src/endpoint/dominance.ts new file mode 100644 index 0000000000..ed7300d182 --- /dev/null +++ b/coinpaprika/src/endpoint/dominance.ts @@ -0,0 +1,36 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'dominance' + +const inputParams = { + market: ['market', 'to', 'quote'], +} + +const convert: { [key: string]: string } = { + BTC: 'bitcoin', +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = '/v1/global' + const options = { + ...config.api, + url, + } + const symbol: string = validator.validated.data.market.toUpperCase() + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, [ + `${convert[symbol]}_dominance_percentage`, + ]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinpaprika/src/endpoint/index.ts b/coinpaprika/src/endpoint/index.ts new file mode 100644 index 0000000000..177a917f71 --- /dev/null +++ b/coinpaprika/src/endpoint/index.ts @@ -0,0 +1,3 @@ +export * as price from './price' +export * as dominance from './dominance' +export * as marketcap from './marketcap' diff --git a/coinpaprika/src/endpoint/marketcap.ts b/coinpaprika/src/endpoint/marketcap.ts new file mode 100644 index 0000000000..b6bbc06cb8 --- /dev/null +++ b/coinpaprika/src/endpoint/marketcap.ts @@ -0,0 +1,30 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'globalmarketcap' + +const inputParams = { + market: ['market', 'to', 'quote'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = '/v1/global' + const options = { + ...config.api, + url, + } + const symbol = validator.validated.data.market.toLowerCase() + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, [`market_cap_${symbol}`]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinpaprika/src/endpoint/price.ts b/coinpaprika/src/endpoint/price.ts new file mode 100644 index 0000000000..75d7bb8824 --- /dev/null +++ b/coinpaprika/src/endpoint/price.ts @@ -0,0 +1,72 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const inputParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + coinid: false, +} + +const getCoinId = async (ticker: string): Promise => { + const response = await Requester.request({ + url: 'https://api.coinpaprika.com/v1/coins', + }) + const coin = response.data + .sort((a: { rank: number }, b: { rank: number }) => (a.rank > b.rank ? 1 : -1)) + .find( + (x: { symbol: string; rank: number }) => + x.symbol.toLowerCase() === ticker.toLowerCase() && x.rank !== 0, + ) + if (typeof coin?.id === 'undefined') { + throw new Error('Coin id not found') + } + return coin.id.toLowerCase() +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const symbol = validator.overrideSymbol(AdapterName) + const quote = validator.validated.data.quote + const coinid = validator.validated.data.coinid as string | undefined + + // If coinid was provided or base was overridden, that symbol will be fetched + let coin = coinid || (symbol !== validator.validated.data.base && symbol) + if (!coin) { + try { + coin = await getCoinId(symbol) + } catch (e) { + throw new AdapterError({ jobRunID, statusCode: 400, message: e.message }) + } + } + + const url = `v1/tickers/${coin.toLowerCase()}` + + const params = { + quotes: quote.toUpperCase(), + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, [ + 'quotes', + quote.toUpperCase(), + 'price', + ]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/coinpaprika/src/index.ts b/coinpaprika/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/coinpaprika/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/coinpaprika/test/adapter_test.js b/coinpaprika/test/adapter.test.ts similarity index 61% rename from coinpaprika/test/adapter_test.js rename to coinpaprika/test/adapter.test.ts index 3eaf317251..3084a5ed03 100644 --- a/coinpaprika/test/adapter_test.js +++ b/coinpaprika/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 = [ @@ -13,11 +16,11 @@ describe('execute', () => { { name: 'coin/market', testData: { id: jobID, data: { coin: 'ETH', market: 'USD' } } }, { name: 'with coinid', - testData: { id: jobID, data: { coin: 'ETH', market: 'USD', coinid: 'ethereum' } }, + testData: { id: jobID, data: { coin: 'ETH', market: 'USD', coinid: 'eth-ethereum' } }, }, { name: 'gets marketcap', - testData: { id: jobID, data: { endpoint: 'globalmarketcap', to: 'USD' } }, + testData: { id: jobID, data: { endpoint: 'globalMarketCap', to: 'USD' } }, }, { name: 'gets bitcoin dominance', @@ -26,13 +29,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) }) }) }) @@ -51,7 +52,7 @@ describe('execute', () => { }, { name: 'market cap: market not supplied', - testData: { id: jobID, data: { endpoint: 'globalmarketcap' } }, + testData: { id: jobID, data: { endpoint: 'globalMarketCap' } }, }, { name: 'dominance: market not supplied', @@ -60,11 +61,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) + } }) }) }) @@ -81,7 +84,7 @@ describe('execute', () => { }, { name: 'market cap: unknown market', - testData: { id: jobID, data: { market: 'not_real', endpoint: 'globalmarketcap' } }, + testData: { id: jobID, data: { market: 'not_real', endpoint: 'globalMarketCap' } }, }, { name: 'dominance: unknown market', @@ -90,11 +93,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/coinpaprika/tsconfig.json b/coinpaprika/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/coinpaprika/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/coinranking/package.json b/coinranking/package.json index 3249945fbf..c5fcfbe0d8 100644 --- a/coinranking/package.json +++ b/coinranking/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/coinranking-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink coinranking adapter.", "keywords": [ "Chainlink", diff --git a/coinranking/src/index.ts b/coinranking/src/index.ts index 89f8d18736..dbcd723dff 100644 --- a/coinranking/src/index.ts +++ b/coinranking/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'Coinranking' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/composite/apy-finance/package.json b/composite/apy-finance/package.json index 51fea0d007..12708d9b3b 100644 --- a/composite/apy-finance/package.json +++ b/composite/apy-finance/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/apy-finance-adapter", - "version": "0.0.1", + "version": "0.0.3", "description": "Chainlink APY Finance TVL adapter", "keywords": [ "Chainlink", @@ -42,7 +42,7 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/token-allocation-adapter": "^0.0.1", + "@chainlink/token-allocation-adapter": "^0.0.2", "@openzeppelin/contracts": "^3.3.0" } } diff --git a/composite/apy-finance/src/abi/IAssetAllocation.json b/composite/apy-finance/src/abi/IAssetAllocation.json index 296d1b17d5..e312e181b1 100644 --- a/composite/apy-finance/src/abi/IAssetAllocation.json +++ b/composite/apy-finance/src/abi/IAssetAllocation.json @@ -2,9 +2,9 @@ { "inputs": [ { - "internalType": "address", - "name": "token", - "type": "address" + "internalType": "bytes32", + "name": "allocationId", + "type": "bytes32" } ], "name": "balanceOf", @@ -18,14 +18,33 @@ "stateMutability": "view", "type": "function" }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "allocationId", + "type": "bytes32" + } + ], + "name": "decimalsOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, { "inputs": [], - "name": "getTokenAddresses", + "name": "getAssetAllocationIds", "outputs": [ { - "internalType": "address[]", + "internalType": "bytes32[]", "name": "", - "type": "address[]" + "type": "bytes32[]" } ], "stateMutability": "view", @@ -34,9 +53,9 @@ { "inputs": [ { - "internalType": "address", - "name": "token", - "type": "address" + "internalType": "bytes32", + "name": "allocationId", + "type": "bytes32" } ], "name": "symbolOf", diff --git a/composite/apy-finance/src/index.ts b/composite/apy-finance/src/index.ts index 53a851f883..6df94f45e0 100644 --- a/composite/apy-finance/src/index.ts +++ b/composite/apy-finance/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'APY-Finance' -export = { NAME, makeConfig, makeExecute, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeConfig, makeExecute, ...expose(makeExecute()) } diff --git a/composite/apy-finance/src/registry/index.ts b/composite/apy-finance/src/registry/index.ts index 8f459f376c..d9c72db8a5 100644 --- a/composite/apy-finance/src/registry/index.ts +++ b/composite/apy-finance/src/registry/index.ts @@ -1,26 +1,22 @@ import { BigNumber, ethers } from 'ethers' import registryAbi from '../abi/IRegistry.json' import assetAllocationAbi from '../abi/IAssetAllocation.json' -import erc20 from '@openzeppelin/contracts/build/contracts/ERC20.json' import { types } from '@chainlink/token-allocation-adapter' -export type GetAllocations = ( - registry: ethers.Contract, - decimalsOf: (address: string) => Promise, -) => () => Promise +export type GetAllocations = (registry: ethers.Contract) => () => Promise -const getAllocations: GetAllocations = (registry, decimalsOf) => async () => { - const tokenAddresses = await registry.getTokenAddresses() +const getAllocations: GetAllocations = (registry) => async () => { + const allocationIds = await registry.getAssetAllocationIds() const [components, balances, decimals]: any = await Promise.all([ - Promise.all(tokenAddresses.map((address: string) => registry.symbolOf(address))), - Promise.all(tokenAddresses.map((address: string) => registry.balanceOf(address))), - Promise.all(tokenAddresses.map(async (address: string) => decimalsOf(address))), + Promise.all(allocationIds.map((id: string) => registry.symbolOf(id))), + Promise.all(allocationIds.map((id: string) => registry.balanceOf(id))), + Promise.all(allocationIds.map((id: string) => registry.decimalsOf(id))), ]) return components.map((symbol: string, i: number) => ({ symbol, balance: BigNumber.from(balances[i]), - decimals: decimals[i], + decimals: BigNumber.from(decimals[i]).toNumber(), })) } @@ -38,11 +34,8 @@ const makeRegistry = async (address: string, rpcUrl: string): Promise provider, ) - const _getERC20 = (address: string) => new ethers.Contract(address, erc20.abi, provider) - const _decimalsOf = (address: string) => _getERC20(address).decimals() - return { - getAllocations: getAllocations(chainlinkRegistry, _decimalsOf), + getAllocations: getAllocations(chainlinkRegistry), } } diff --git a/composite/bitcoin-json-rpc/package.json b/composite/bitcoin-json-rpc/package.json index c3fcbc1ba1..1531a05ea1 100644 --- a/composite/bitcoin-json-rpc/package.json +++ b/composite/bitcoin-json-rpc/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/bitcoin-json-rpc-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "", "author": "Evangelos Barakos (evangelos@smartcontract.com)", "license": "MIT", @@ -32,6 +32,6 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/json-rpc-adapter": "^0.0.3" + "@chainlink/json-rpc-adapter": "^0.0.4" } } diff --git a/composite/bitcoin-json-rpc/src/index.ts b/composite/bitcoin-json-rpc/src/index.ts index 65e183daef..e00e982816 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 { expose } from '@chainlink/ea-bootstrap' const NAME = 'BITCOIN-JSON-RPC' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/composite/crypto-volatility-index/package.json b/composite/crypto-volatility-index/package.json index 518516c34a..20efe58695 100644 --- a/composite/crypto-volatility-index/package.json +++ b/composite/crypto-volatility-index/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/crypto-volatility-index-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "The Crypto volatility index (CVI)", "keywords": [ "Chainlink", @@ -40,8 +40,8 @@ "typescript": "^3.9.7" }, "dependencies": { - "moment": "^2.29.1", - "@chainlink/reference-data-reader": "^0.0.3", - "@chainlink/token-allocation-adapter": "^0.0.1" + "@chainlink/reference-data-reader": "^0.0.4", + "@chainlink/token-allocation-adapter": "^0.0.2", + "moment": "^2.29.1" } } diff --git a/composite/crypto-volatility-index/src/index.ts b/composite/crypto-volatility-index/src/index.ts index 99a1d236f1..27b4afba46 100644 --- a/composite/crypto-volatility-index/src/index.ts +++ b/composite/crypto-volatility-index/src/index.ts @@ -1,4 +1,4 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' -export = { execute, ...expose(util.wrapExecute(execute)) } +export = { execute, ...expose(execute) } diff --git a/composite/defi-pulse/README.md b/composite/defi-pulse/README.md index 3d597acb73..d1bb070f44 100644 --- a/composite/defi-pulse/README.md +++ b/composite/defi-pulse/README.md @@ -15,14 +15,25 @@ The ChainlinkAdapter.getAllocations(ISetToken _setToken) should be reimplemented The adapter takes the following environment variables: - `RPC_URL`: Blockchain RPC endpoint to get the needed on-chain data +- `DATA_PROVIDER`: Data provider to use. Options (Notes): + - amberdata (Doesn't support crypto quotes) + - coinapi + - coingecko + - coinmarketcap + - coinpaprika + - cryptocompare + - kaiko + - nomics +- `API_KEY`: For those data providers who need an api key +- `DEFAULT_QUOTE` (Optional): Currency that the price will be fetched by default. `USD` used by default -This adapter relies on [`token-allocation`](../token-allocation/README.md) adapter. Required `token-allocation` input params and configuration apply to this adapter as well. ## Input Params - `address`: Address of the SetToken (required) - `adapter`: Address of the adapter contract (required) - `name`: Index Name (optional) - `asset`: Asset name (optional) +- `quote` (optional). Currency we want the price on. `DEFAULT_QUOTE` by default ## Output ```json diff --git a/composite/defi-pulse/package.json b/composite/defi-pulse/package.json index f265f87626..680d0159f8 100644 --- a/composite/defi-pulse/package.json +++ b/composite/defi-pulse/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/defi-pulse-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink defi pulse adapter", "keywords": [ "Chainlink", @@ -42,6 +42,6 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/token-allocation-adapter": "^0.0.1" + "@chainlink/token-allocation-adapter": "^0.0.2" } } diff --git a/composite/defi-pulse/src/index.ts b/composite/defi-pulse/src/index.ts index fa75da554b..f70a222288 100644 --- a/composite/defi-pulse/src/index.ts +++ b/composite/defi-pulse/src/index.ts @@ -1,5 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' -export = { makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/composite/dns-record-check/package.json b/composite/dns-record-check/package.json index 52af8d9b13..02ce93bbb9 100644 --- a/composite/dns-record-check/package.json +++ b/composite/dns-record-check/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/dns-record-check-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink DNS record check adapter", "keywords": [ "Chainlink", @@ -42,6 +42,6 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/dns-query-adapter": "^0.0.1" + "@chainlink/dns-query-adapter": "^0.0.2" } } diff --git a/composite/dns-record-check/src/index.ts b/composite/dns-record-check/src/index.ts index 1cbfef8584..18da68e8e8 100644 --- a/composite/dns-record-check/src/index.ts +++ b/composite/dns-record-check/src/index.ts @@ -1,4 +1,4 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -export = { makeExecute, ...expose(util.wrapExecute(makeExecute())) } +export = { makeExecute, ...expose(makeExecute()) } diff --git a/composite/market-closure/README.md b/composite/market-closure/README.md index 0ff7bc2174..aad866a903 100644 --- a/composite/market-closure/README.md +++ b/composite/market-closure/README.md @@ -1,50 +1,86 @@ # Chainlink Market Closure composite adapter Market Closure composite adapter adds an extra check to see if trading is halted or not for the asset that's queried. It -allows for multiple checks and multiple price data provider. If the check provider fails, it will automatically fall -back to checking the schedule. If the market is closed, the adapter will fetch the latest on-chain value from the -reference contract. +allows for multiple checks and multiple price data provider. + +If the market check provider fails, it will automatically fall +back to checking the provided schedule. If there is no schedule set it will default the market as open. + +If the market is closed, the adapter will fetch the latest on-chain value from the reference contract. ## Configuration The adapter takes the following environment variables: -- `CHECK_TYPE`: Required check type: `schedule|tradinghours` -- `PRICE_ADATER`: Required price data provider adapter type `finnhub|fcs_api` -- `CHECK_API_KEY`: Optional API key used by the check -- `RPC_URL`: ETH RPC URL to read the reference data value. Required by runlog requests. +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----------------------------------------: | :-------------------------------------------: | :------------------------: | :---------: | +| ✅ | `CHECK_TYPE` | The provider to check if a market is open | `schedule`, `tradinghours` | | +| 🟡 | `CHECK_API_KEY` (when using `tradinghours`) | An API key when needed by a check provider | | | +| ✅ | `PRICE_ADAPTER` | The provider to use for retrieving price data | `finnhub`, `fcsapi` | | +| 🟡 | `API_KEY` | An API key when needed by a price provider | | | +| ✅ | `RPC_URL` | ETH RPC URL to read the reference data value | | | + +## Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------------------------------: | :-------------------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | `referenceContract`, `contract` | The Aggregator contract to call for its latest round's price | | | +| ✅ | `multiply` | To handle big numbers, the amount to divide the output from reading the reference contract by | | | +| ✅ | `base`, `asset`, `from`, `symbol` | The target currency to query | | | +| 🟡 | `schedule` | A schedule of market times to check whether they are open | | | + +An example schedule looks like: + +```bash +{ + timezone: 'Europe/Oslo', + hours: { + monday: ['24:00-24:01'], + }, + holidays: [], + } +``` + +## Running this adapter + +### Local -## Run +Ensure that the project's dependencies are installed and that the code is compiled by running the following command from the external-adapters respository root: -First build the project: +```bash +yarn && yarn setup +``` + +Change directories into market-closure and start the server: ```bash -yarn build +cd composite/market-closure && yarn start ``` -Then run the adapter. +### Docker + +To build a Docker container for a specific `$(adapter)`, run the following command from repository root: + +```bash +make docker adapter=composite/market-closure name=market-closure +``` -Examples of combinations include: +The naming convention for Docker containers will be `$(name)-adapter`. -Tradinghours & Finnhub: +Then run it with: ```bash -env \ - LOG_LEVEL=debug \ - CHECK_TYPE=tradinghours \ - PRICE_ADATER=finnhub \ - API_KEY=your-finnhub-api-key \ - CHECK_API_KEY=your-tradinghours-api-key \ - yarn start +docker run -p 8080:8080 --env-file="~/PATH_TO_ENV" -it market-closure-adapter:latest ``` -Schedule & FCS API: +(Note: Docker environment file string values do not use " or ' quote marks) + +### Serverless + +Create the zip: ```bash -env \ - LOG_LEVEL=debug \ - CHECK_TYPE=schedule \ - PRICE_ADATER=fcs_api \ - API_KEY=your-fcs_api-api-key \ - yarn start +make zip adapter=composite/market-closure name=market-closure ``` + +The zip will be created as `./$(adapter)/dist/$(name)-adapter.zip`. diff --git a/composite/market-closure/package.json b/composite/market-closure/package.json index a27fcca522..3f8c2f162a 100644 --- a/composite/market-closure/package.json +++ b/composite/market-closure/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/market-closure-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink Market-Closure adapter. Checks if trading market for asset is open before fetching the price.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -31,9 +31,9 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/fcsapi-adapter": "^0.0.3", - "@chainlink/finnhub-adapter": "^0.0.3", - "@chainlink/reference-data-reader": "^0.0.3", + "@chainlink/fcsapi-adapter": "^0.0.4", + "@chainlink/finnhub-adapter": "^0.0.4", + "@chainlink/reference-data-reader": "^0.0.4", "market-closure": "^0.1.2" } } diff --git a/composite/market-closure/src/index.ts b/composite/market-closure/src/index.ts index 1e4315feeb..0a7035cbbe 100644 --- a/composite/market-closure/src/index.ts +++ b/composite/market-closure/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'MarketClosure' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/composite/market-closure/src/price.ts b/composite/market-closure/src/price.ts index b42936fa38..56fd68adac 100644 --- a/composite/market-closure/src/price.ts +++ b/composite/market-closure/src/price.ts @@ -11,7 +11,7 @@ const isPriceDataProvider = (envVar?: string): envVar is PriceDataProvider => Object.values(PriceDataProvider).includes(envVar as any) export const getPriceDataProvider = (): PriceDataProvider | undefined => { - const priceDataProvider = process.env.PRICE_ADATER + const priceDataProvider = process.env.PRICE_ADAPTER return isPriceDataProvider(priceDataProvider) ? (priceDataProvider as PriceDataProvider) : undefined diff --git a/composite/outlier-detection/README.md b/composite/outlier-detection/README.md index bb52ef5d2e..a099476d44 100644 --- a/composite/outlier-detection/README.md +++ b/composite/outlier-detection/README.md @@ -9,8 +9,8 @@ If all checks passes, the adapter returns the median from the source data provid The adapter takes the following environment variables: -- `SOURCE_ADAPTERS`: Required list of data source adapters. One or multiple of: `xbto|genesisvolatility|dxfeed` -- `CHECK_ADAPTERS`: List of adapters to check against. One or multiple of: `deribit|oilpriceapi|dxfeed` +- `SOURCE_ADAPTERS`: Required list of data source adapters. One or multiple of: `xbto|genesisvolatility|dxfeed|tradermade` +- `CHECK_ADAPTERS`: List of adapters to check against. One or multiple of: `deribit|oilpriceapi|dxfeed|tradermade` - `CHECK_THRESHOLD`: Set a percentage deviation threshold against the check data sources. Set to 0 or empty to not perform this check. - `ONCHAIN_THRESHOLD`: Set a percentage deviation threshold against the on-chain value. Set to 0 or empty to not @@ -27,11 +27,11 @@ E.g. dxFeed endpoint URL: ```ebnf # E.g.: SOURCE_ADAPTERS=xbto,dxfeed ::= ( "," )* - ::= "xbto" | "genesisvolatility" | "dxfeed" + ::= "xbto" | "genesisvolatility" | "dxfeed" | "tradermade" # E.g.: CHECK_ADAPTERS=oilpriceapi,dxfeed ::= ( "," )* - ::= "deribit" | "oilpriceapi" | "dxfeed" + ::= "deribit" | "oilpriceapi" | "dxfeed" | "tradermade" ``` ## Run diff --git a/composite/outlier-detection/package.json b/composite/outlier-detection/package.json index 7cd0256a20..26124c73e7 100644 --- a/composite/outlier-detection/package.json +++ b/composite/outlier-detection/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/outlier-detection-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink Outlier Detection adapter.", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -31,11 +31,12 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/reference-data-reader": "^0.0.3", - "@chainlink/oilpriceapi-adapter": "^0.0.3", - "@chainlink/deribit-adapter": "^0.0.1", - "@chainlink/genesis-volatility-adapter": "^0.0.3", - "@chainlink/xbto-adapter": "^0.0.3", - "@chainlink/dxfeed-adapter": "^0.0.3" + "@chainlink/deribit-adapter": "^0.0.2", + "@chainlink/dxfeed-adapter": "^0.0.4", + "@chainlink/genesis-volatility-adapter": "^0.0.4", + "@chainlink/oilpriceapi-adapter": "^0.0.4", + "@chainlink/reference-data-reader": "^0.0.4", + "@chainlink/tradermade-adapter": "^0.0.5", + "@chainlink/xbto-adapter": "^0.0.4" } } diff --git a/composite/outlier-detection/src/check.ts b/composite/outlier-detection/src/check.ts index 368b0002b2..042853f1d4 100644 --- a/composite/outlier-detection/src/check.ts +++ b/composite/outlier-detection/src/check.ts @@ -2,12 +2,14 @@ import { Execute } from '@chainlink/types' import * as OilpriceAPI from '@chainlink/oilpriceapi-adapter' import * as Deribit from '@chainlink/deribit-adapter' import * as dxFeed from '@chainlink/dxfeed-adapter' +import * as Tradermade from '@chainlink/tradermade-adapter' import { util } from '@chainlink/ea-bootstrap' export enum CheckDataProvider { OilpriceAPI = 'oilpriceapi', Deribit = 'deribit', dxFeed = 'dxfeed', + Tradermade = 'tradermade', } export const ENV_CHECK_ADAPTERS = 'CHECK_ADAPTERS' @@ -31,6 +33,8 @@ export const getCheckImpl = (type: CheckDataProvider): Execute => { return Deribit.makeExecute(Deribit.makeConfig(prefix)) case CheckDataProvider.dxFeed: return dxFeed.makeExecute(dxFeed.makeConfig(prefix)) + case CheckDataProvider.Tradermade: + return Tradermade.makeExecute(Tradermade.makeConfig(prefix)) default: throw Error(`Unknown source data provider adapter type: ${type}`) } diff --git a/composite/outlier-detection/src/index.ts b/composite/outlier-detection/src/index.ts index 60cfba0132..06cdff2597 100644 --- a/composite/outlier-detection/src/index.ts +++ b/composite/outlier-detection/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeConfig } from './config' import { makeExecute } from './adapter' const NAME = 'OutlierDetection' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/composite/outlier-detection/src/source.ts b/composite/outlier-detection/src/source.ts index 97d194226d..0d2b48758e 100644 --- a/composite/outlier-detection/src/source.ts +++ b/composite/outlier-detection/src/source.ts @@ -2,12 +2,14 @@ import { Execute } from '@chainlink/types' import * as GenesisVolatility from '@chainlink/genesis-volatility-adapter' import * as XBTO from '@chainlink/xbto-adapter' import * as dxFeed from '@chainlink/dxfeed-adapter' +import * as Tradermade from '@chainlink/tradermade-adapter' import { util } from '@chainlink/ea-bootstrap' export enum SourceDataProvider { XBTO = 'xbto', GenesisVolatility = 'genesisvolatility', dxFeed = 'dxfeed', + Tradermade = 'tradermade', } export const ENV_SOURCE_ADAPTERS = 'SOURCE_ADAPTERS' @@ -33,6 +35,8 @@ export const getSourceImpl = (type: SourceDataProvider): Execute => { return GenesisVolatility.makeExecute(GenesisVolatility.makeConfig(prefix)) case SourceDataProvider.dxFeed: return dxFeed.makeExecute(dxFeed.makeConfig(prefix)) + case SourceDataProvider.Tradermade: + return Tradermade.makeExecute(Tradermade.makeConfig(prefix)) default: throw Error(`Unknown source data provider adapter type: ${type}`) } diff --git a/composite/proof-of-reserves/package.json b/composite/proof-of-reserves/package.json index 29c5a8524f..8398e8c6a2 100644 --- a/composite/proof-of-reserves/package.json +++ b/composite/proof-of-reserves/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/proof-of-reserves-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink BTC Proof of Reserves composite adapter. Combines multiple adapters to find total balance in custody for wBTC or renBTC.", "keywords": [ "Chainlink", @@ -46,15 +46,15 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/amberdata-adapter": "^0.0.3", - "@chainlink/blockchain.com-adapter": "^0.0.3", - "@chainlink/blockchair-adapter": "^0.0.3", - "@chainlink/blockcypher-adapter": "^0.0.3", - "@chainlink/btc.com-adapter": "^0.0.1", - "@chainlink/cryptoapis-adapter": "^0.0.3", - "@chainlink/reduce-adapter": "^0.0.3", - "@chainlink/renvm-address-set-adapter": "^0.0.3", - "@chainlink/sochain-adapter": "^0.0.1", - "@chainlink/wbtc-address-set-adapter": "^0.0.3" + "@chainlink/amberdata-adapter": "^0.0.4", + "@chainlink/blockchain.com-adapter": "^0.0.4", + "@chainlink/blockchair-adapter": "^0.0.4", + "@chainlink/blockcypher-adapter": "^0.0.4", + "@chainlink/btc.com-adapter": "^0.0.2", + "@chainlink/cryptoapis-adapter": "^0.0.4", + "@chainlink/reduce-adapter": "^0.0.4", + "@chainlink/renvm-address-set-adapter": "^0.0.4", + "@chainlink/sochain-adapter": "^0.0.2", + "@chainlink/wbtc-address-set-adapter": "^0.0.4" } } diff --git a/composite/proof-of-reserves/src/index.ts b/composite/proof-of-reserves/src/index.ts index 99a1d236f1..27b4afba46 100644 --- a/composite/proof-of-reserves/src/index.ts +++ b/composite/proof-of-reserves/src/index.ts @@ -1,4 +1,4 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' -export = { execute, ...expose(util.wrapExecute(execute)) } +export = { execute, ...expose(execute) } diff --git a/composite/synth-index/.eslintrc.js b/composite/synth-index/.eslintrc.js new file mode 100644 index 0000000000..30c00e34a1 --- /dev/null +++ b/composite/synth-index/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../../.eslintrc.ts.js'), +} diff --git a/composite/synth-index/README.md b/composite/synth-index/README.md new file mode 100644 index 0000000000..9f8300b746 --- /dev/null +++ b/composite/synth-index/README.md @@ -0,0 +1,139 @@ +# Chainlink External Adapter to Synthetix Index Value + +The adapter calculates a Synthetix Index value in the currency selected + +## Configuration + +- `DEFAULT_NETWORK` (Optional). Network to fetch the Synth Index +- `DATA_PROVIDER`: Data provider to use. Options (Notes): + - amberdata (Doesn't support crypto quotes) + - coinapi + - coingecko + - coinmarketcap + - coinpaprika + - cryptocompare + - kaiko + - nomics +- `API_KEY`: For those data providers who need an api key +- `DEFAULT_QUOTE` (Optional): Currency that the price will be fetched by default. `USD` used by default + +E.g. if we wish to use CoinMarketCap as data provider, we should run (docker): +``` +docker run -p 8080:8080 -e DATA_PROVIDER=coinmarketcap -e API_KEY=xxx-xxx -it synth-index-adapter:latest +``` + +## Input Params + +- `base`, `asset` or `from`: Synthx Index asset to fetch +- `network` (optional): Network to fetch. `mainnet` by default +- `quote` (optional). Currency we want the price on. `DEFAULT_QUOTE` by default + + +```json +{ + "jobID": "1", + "data": { + "base": "sDEFI" + } +} +``` + +## Output + +```json +{ + "jobRunID": "1", + "data": { + "sources": [], + "payload": { + "COMP": { + "quote": { + "USD": { + "price": "192.23560377" + } + } + }, + "MKR": { + "quote": { + "USD": { + "price": "1285.07135635" + } + } + }, + "AAVE": { + "quote": { + "USD": { + "price": "173.18073267" + } + } + }, + "UMA": { + "quote": { + "USD": { + "price": "10.20122460" + } + } + }, + "SNX": { + "quote": { + "USD": { + "price": "13.62490473" + } + } + }, + "REN": { + "quote": { + "USD": { + "price": "0.68786796" + } + } + }, + "UNI": { + "quote": { + "USD": { + "price": "8.23570852" + } + } + }, + "KNC": { + "quote": { + "USD": { + "price": "1.26141751" + } + } + }, + "CRV": { + "quote": { + "USD": { + "price": "1.75094267" + } + } + }, + "WNXM": { + "quote": { + "USD": { + "price": "39.17240510" + } + } + }, + "YFI": { + "quote": { + "USD": { + "price": "30967.53680584" + } + } + }, + "BAL": { + "quote": { + "USD": { + "price": "20.15973056" + } + } + } + }, + "result": 6902.49494069292 + }, + "result": 6902.49494069292, + "statusCode": 200 +} +``` diff --git a/composite/synth-index/package.json b/composite/synth-index/package.json new file mode 100644 index 0000000000..e29994b049 --- /dev/null +++ b/composite/synth-index/package.json @@ -0,0 +1,48 @@ +{ + "name": "@chainlink/synth-index-adapter", + "version": "0.0.2", + "description": "Chainlink Synth Index adapter", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 6000 -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 6000 --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": { + "@chainlink/token-allocation-adapter": "^0.0.2", + "synthetix": "2.39.2" + } +} diff --git a/composite/synth-index/src/adapter.ts b/composite/synth-index/src/adapter.ts new file mode 100644 index 0000000000..187110d1dc --- /dev/null +++ b/composite/synth-index/src/adapter.ts @@ -0,0 +1,67 @@ +import { Validator, AdapterError } from '@chainlink/external-adapter' +import { AdapterResponse, Execute, AdapterRequest } from '@chainlink/types' +import * as ta from '@chainlink/token-allocation-adapter' +import snx from 'synthetix' +import { makeConfig, Config } from './config' +import Decimal from 'decimal.js' + +const customParams = { + base: ['base', 'asset', 'from'], + network: false, +} + +type Synth = { + name: string + asset: string + index: { + asset: string + category: string + units: number + }[] + inverted: Record +} + +/** + * Covert number to max number of decimals, trim trailing zeros + * + * @param num number to convert to fixed max number of decimals + * @param decimals max number of decimals + */ +const toFixedMax = (num: number | string | Decimal, decimals: number): string => + new Decimal(num) + .toFixed(decimals) + // remove trailing zeros + .replace(/(\.\d*?[1-9])0+$/g, '$1') + // remove decimal part if all zeros (or only decimal point) + .replace(/\.0*$/g, '') + +const getAllocations = (synth: Synth): ta.types.TokenAllocations => { + return synth.index.map((index) => { + const decimals = 18 + const balanceDec = new Decimal(index.units).mul(10 ** decimals) + const balance = toFixedMax(balanceDec, decimals) + return { symbol: index.asset, balance, decimals } + }) +} + +export const execute = async (input: AdapterRequest, config: Config): Promise => { + const validator = new Validator(input, customParams) + if (validator.error) throw validator.error + + const { base, network = config.defaultNetwork } = validator.validated.data + + const synths: Synth[] = snx.getSynths({ network: network.toLowerCase() }) + const synth = synths + .filter(({ index, inverted }) => index && !inverted) + .find((d) => d.name.toLowerCase() === base.toLowerCase()) + + if (!synth) throw new AdapterError({ message: `Synth not found`, statusCode: 400 }) + + const allocations = getAllocations(synth) + const _execute = ta.makeExecute(config.taConfig) + return await _execute({ id: validator.validated.id, data: { ...input.data, allocations } }) +} + +export const makeExecute = (config?: Config): Execute => { + return async (request: AdapterRequest) => execute(request, config || makeConfig()) +} diff --git a/composite/synth-index/src/config.ts b/composite/synth-index/src/config.ts new file mode 100644 index 0000000000..966256c76d --- /dev/null +++ b/composite/synth-index/src/config.ts @@ -0,0 +1,14 @@ +import { util } from '@chainlink/ea-bootstrap' +import * as ta from '@chainlink/token-allocation-adapter' + +export type Config = { + defaultNetwork: string + taConfig: ta.types.Config +} + +export const makeConfig = (prefix = ''): Config => { + return { + defaultNetwork: util.getEnv('DEFAULT_NETWORK') || 'mainnet', + taConfig: ta.makeConfig(prefix), + } +} diff --git a/composite/synth-index/src/index.ts b/composite/synth-index/src/index.ts new file mode 100644 index 0000000000..7332dc08b7 --- /dev/null +++ b/composite/synth-index/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'SYNTH-INDEX' + +export = { NAME, makeConfig, makeExecute, ...expose(makeExecute()) } diff --git a/composite/synth-index/test/adapter.test.ts b/composite/synth-index/test/adapter.test.ts new file mode 100644 index 0000000000..80537f5429 --- /dev/null +++ b/composite/synth-index/test/adapter.test.ts @@ -0,0 +1,108 @@ +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' +import * as ta from '@chainlink/token-allocation-adapter' + +const makeMockConfig = (provider: string) => { + return { + defaultNetwork: 'mainnet', + taConfig: ta.makeConfig('', provider), + } +} + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute(makeMockConfig('coingecko')) + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { asset: 'sDEFI' } }, + }, + { + name: 'asset', + testData: { + id: jobID, + data: { asset: 'sDEFI' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + }) + }) + }) + + context('validation error', () => { + const requests = [ + { name: 'empty body', testData: {} }, + { name: 'empty data', testData: { data: {} } }, + { + name: 'asset not supplied', + testData: { id: jobID, 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'invalid asset', + testData: { + id: jobID, + data: { asset: 'INVALID_ASSET' }, + }, + }, + ] + + 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) + } + }) + }) + }) + + context('succeed calls @integration', () => { + const requests = [ + { + name: 'valid asset', + testData: { + id: jobID, + data: { asset: 'sDEFI' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 200, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/composite/synth-index/tsconfig.json b/composite/synth-index/tsconfig.json new file mode 100644 index 0000000000..2e4c1b9ec1 --- /dev/null +++ b/composite/synth-index/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "typeRoots": [ + "../../node_modules/@types", + "../../typings", + "./typings" + ], + "resolveJsonModule": true + }, + "include": [ + "src/**/*", + "src/**/*.json" + ], + "exclude": [ + "dist", + "**/*.spec.ts", + "**/*.test.ts" + ] +} diff --git a/composite/token-allocation/package.json b/composite/token-allocation/package.json index ff4673db6f..6f66f1456c 100644 --- a/composite/token-allocation/package.json +++ b/composite/token-allocation/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/token-allocation-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink token allocation adapter", "keywords": [ "Chainlink", diff --git a/composite/token-allocation/src/adapter.ts b/composite/token-allocation/src/adapter.ts index 7fd27dbf0a..66bba9abaf 100644 --- a/composite/token-allocation/src/adapter.ts +++ b/composite/token-allocation/src/adapter.ts @@ -1,9 +1,10 @@ import { AdapterResponse, Execute, AdapterRequest } from '@chainlink/types' import { Requester, Validator } from '@chainlink/external-adapter' -import { Config, DEFAULT_TOKEN_BALANCE, DEFAULT_TOKEN_DECIMALS, makeConfig } from './config' -import { TokenAllocations, ResponsePayload } from './types' +import { DEFAULT_TOKEN_BALANCE, DEFAULT_TOKEN_DECIMALS, makeConfig } from './config' +import { TokenAllocations, ResponsePayload, Config } from './types' import { Decimal } from 'decimal.js' import { AdapterError } from '@chainlink/external-adapter' +import { BigNumber } from 'ethers' export const priceTotalValue = ( allocations: TokenAllocations, @@ -35,19 +36,36 @@ export const marketCapTotalValue = ( .toNumber() } -const inputParams = { - allocations: true, - quote: false, -} - -const toValidAllocations = (allocations: TokenAllocations): TokenAllocations => { - if (!allocations.every((t) => !!t.symbol)) - throw new AdapterError({ message: `Symbol not available for all tokens.`, statusCode: 400 }) - return allocations.map((t) => ({ - symbol: t.symbol.toUpperCase(), - decimals: t.decimals || DEFAULT_TOKEN_DECIMALS, - balance: t.balance || DEFAULT_TOKEN_BALANCE * 10 ** (t.decimals || DEFAULT_TOKEN_DECIMALS), - })) +const toValidAllocations = (allocations: any[]): TokenAllocations => { + const _toValidSymbol = (symbol: string) => { + if (!symbol) + throw new AdapterError({ message: `Symbol not available for all tokens.`, statusCode: 400 }) + return symbol.toUpperCase() + } + const _toValidDecimals = (decimals: number | undefined) => { + if (decimals === undefined) return DEFAULT_TOKEN_DECIMALS + return Number.isInteger(decimals) && decimals >= 0 ? decimals : DEFAULT_TOKEN_DECIMALS + } + const _toValidBalance = (balance: number | string | undefined, decimals: number) => { + if (!balance) return DEFAULT_TOKEN_BALANCE * 10 ** decimals + let BNbalance + try { + BNbalance = BigNumber.from(balance.toString()) + } catch (e) { + throw new AdapterError({ message: `Invalid balance: ${e.message}`, statusCode: 400 }) + } + if (BNbalance.isNegative()) + throw new AdapterError({ message: `Balance cannot be negative`, statusCode: 400 }) + return balance + } + return allocations.map((t: any) => { + const decimals = _toValidDecimals(t.decimals) + return { + symbol: _toValidSymbol(t.symbol), + decimals, + balance: _toValidBalance(t.balance, decimals), + } + }) } const computePrice = async (config: Config, allocations: TokenAllocations, quote: string) => { @@ -88,12 +106,18 @@ const computeMarketCap = async (config: Config, allocations: TokenAllocations, q return { payload, result } } +const inputParams = { + allocations: true, + quote: false, + method: false, +} + export const execute = async (input: AdapterRequest, config: Config): Promise => { const validator = new Validator(input, inputParams) if (validator.error) throw validator.error const jobRunID = validator.validated.id - const { quote = 'USD' } = validator.validated.data + const { quote = config.defaultQuote, method = config.defaultMethod } = validator.validated.data const allocations = toValidAllocations(validator.validated.data.allocations) const _success = (payload: ResponsePayload, result: number) => @@ -103,13 +127,12 @@ export const execute = async (input: AdapterRequest, config: Config): Promise = { [DataProvider.Kaiko]: kaiko, } -export type PriceAdapter = { - getPrices: (baseSymbols: string[], quote: string) => Promise> - getMarketCaps: (baseSymbols: string[], quote: string) => Promise> -} - -export type Config = { - priceAdapter: PriceAdapter - defaultMethod: string -} - export const DEFAULT_TOKEN_DECIMALS = 18 export const DEFAULT_TOKEN_BALANCE = 1 -export const makeConfig = (prefix = ''): Config => { - const dataProvider = util.getRequiredEnv('DATA_PROVIDER', prefix) +export const makeConfig = (prefix = '', provider = ''): Config => { + const dataProvider = provider || util.getRequiredEnv('DATA_PROVIDER', prefix) return { priceAdapter: providers[dataProvider], defaultMethod: util.getEnv('DEFAULT_METHOD', prefix) || 'price', + defaultQuote: util.getEnv('DEFAULT_QUOTE') || 'USD', } } diff --git a/composite/token-allocation/src/data-providers/coingecko.ts b/composite/token-allocation/src/data-providers/coingecko.ts index c5a8cad03a..b3839e6519 100644 --- a/composite/token-allocation/src/data-providers/coingecko.ts +++ b/composite/token-allocation/src/data-providers/coingecko.ts @@ -9,10 +9,10 @@ const getCoinList = async () => { return response.data } -const getPriceData = async (id: string, currency: string, marketcap = false) => { +const getPriceData = async (ids: string, currency: string, marketcap = false) => { const url = 'https://api.coingecko.com/api/v3/simple/price' const params = { - ids: id, + ids, vs_currencies: currency.toLowerCase(), include_market_cap: marketcap, } @@ -33,17 +33,17 @@ const coingeckoBlacklist = [ 'unicorn-token', ] -const toAssetPrice = (data: Record, coinId: string, currency: string) => { - const price = data[coinId] && data[coinId][currency.toLowerCase()] +const toAssetPrice = (data: Record, currency: string) => { + const price = data && data[currency.toLowerCase()] if (!price || price <= 0) { throw new Error('invalid price') } return price } -const toMarketCap = (data: Record, coinId: string, currency: string) => { +const toMarketCap = (data: Record, currency: string) => { const resultKey = `${currency.toLowerCase()}_market_cap` - const marketCap = data[coinId] && data[coinId][resultKey] + const marketCap = data && data[resultKey] if (!marketCap || marketCap <= 0) { throw new Error('invalid marketCap') } @@ -55,20 +55,15 @@ export const getPrices = async ( quote: string, ): Promise> => { const coinList = await getCoinList() - - const entries = await Promise.all( - baseSymbols.map(async (symbol) => { - const coin = coinList.find( - (d: any) => - d.symbol.toLowerCase() === symbol.toLowerCase() && - !coingeckoBlacklist.includes(d.id.toLowerCase()), - ) - const data = await getPriceData(coin.id, quote) - return [symbol, toAssetPrice(data, coin.id, quote)] - }), + const idToSymbol = getIdtoSymbol(baseSymbols, coinList) + const ids = Object.keys(idToSymbol).join(',') + const response: Record = await getPriceData(ids, quote) + return Object.fromEntries( + Object.entries(response).map(([coinId, data]) => [ + idToSymbol[coinId], + toAssetPrice(data, quote), + ]), ) - - return Object.fromEntries(entries) } export const getMarketCaps = async ( @@ -76,18 +71,28 @@ export const getMarketCaps = async ( quote: string, ): Promise> => { const coinList = await getCoinList() - - const entries = await Promise.all( - baseSymbols.map(async (symbol) => { - const coin = coinList.find( - (d: any) => - d.symbol.toLowerCase() === symbol.toLowerCase() && - !coingeckoBlacklist.includes(d.id.toLowerCase()), - ) - const data = await getPriceData(coin.id, quote, true) - return [symbol, toMarketCap(data, coin.id, quote)] - }), + const idToSymbol = getIdtoSymbol(baseSymbols, coinList) + const ids = Object.keys(idToSymbol).join(',') + const response: Record = await getPriceData(ids, quote, true) + return Object.fromEntries( + Object.entries(response).map(([coinId, data]) => [ + idToSymbol[coinId], + toMarketCap(data, quote), + ]), ) +} - return Object.fromEntries(entries) +const getIdtoSymbol = (symbols: string[], coinList: any) => { + const idToSymbol: Record = {} + symbols.forEach((symbol) => { + const coin = coinList.find( + (d: any) => + d.symbol.toLowerCase() === symbol.toLowerCase() && + !coingeckoBlacklist.includes(d.id.toLowerCase()), + ) + if (coin && coin.id) { + idToSymbol[coin.id] = symbol + } + }) + return idToSymbol } diff --git a/composite/token-allocation/src/data-providers/coinmarketcap.ts b/composite/token-allocation/src/data-providers/coinmarketcap.ts index c04907dce3..889acb6410 100644 --- a/composite/token-allocation/src/data-providers/coinmarketcap.ts +++ b/composite/token-allocation/src/data-providers/coinmarketcap.ts @@ -1,15 +1,36 @@ import { Requester } from '@chainlink/external-adapter' -// Defaults we use when there are multiple currencies with the same symbol -const presetSlugs: Record = { - COMP: 'compound', - BNT: 'bancor', - RCN: 'ripio-credit-network', - UNI: 'uniswap', - CRV: 'curve-dao-token', - FNX: 'finnexus', - ETC: 'ethereum-classic', - BAT: 'basic-attention-token', +// Coin IDs fetched from the ID map: https://coinmarketcap.com/api/documentation/v1/#operation/getV1CryptocurrencyMap +const presetIds: { [symbol: string]: number } = { + COMP: 5692, + BNT: 1727, + RCN: 2096, + UNI: 7083, + CRV: 6538, + FNX: 5712, + ETC: 1321, + BAT: 1697, + CRO: 3635, + LEO: 3957, + FTT: 4195, + HT: 2502, + OKB: 3897, + KCS: 2087, + BTC: 1, + ETH: 1027, + BNB: 1839, + LINK: 1975, + BCH: 1831, + MKR: 1518, + AAVE: 7278, + UMA: 5617, + SNX: 2586, + REN: 2539, + KNC: 1982, + SUSHI: 6758, + YFI: 5864, + BAL: 5728, + '1INCH': 8104, } const getPriceData = async (assets: string[], convert: string) => { @@ -28,15 +49,15 @@ const getPriceData = async (assets: string[], convert: string) => { } // We map some symbols as slugs - const slugs = assets.map((s) => presetSlugs[s]).filter(Boolean) - const symbols = assets.filter((s) => !presetSlugs[s]) + const ids = assets.map((s) => presetIds[s]).filter(Boolean) + const symbols = assets.filter((s) => !presetIds[s]) let data: Record = {} // We need to make two separate requests, one querying slugs - if (slugs.length > 0) { - const slugPrices = await _getPriceData({ slug: slugs.join(), convert }) - data = { ...data, ...slugPrices.data } + if (ids.length > 0) { + const idPrices = await _getPriceData({ id: ids.join(), convert }) + data = { ...data, ...idPrices.data } } // The other one querying symbols diff --git a/composite/token-allocation/src/index.ts b/composite/token-allocation/src/index.ts index 6a51db11aa..d2214e68dd 100644 --- a/composite/token-allocation/src/index.ts +++ b/composite/token-allocation/src/index.ts @@ -1,9 +1,9 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' import * as types from './types' const NAME = 'Token-Allocation' -const handlers = expose(util.wrapExecute(makeExecute())) +const handlers = expose(makeExecute()) export { NAME, types, makeExecute, makeConfig, handlers } diff --git a/composite/token-allocation/src/types.ts b/composite/token-allocation/src/types.ts index 8efc1bd1bf..1e926d6629 100644 --- a/composite/token-allocation/src/types.ts +++ b/composite/token-allocation/src/types.ts @@ -18,3 +18,14 @@ export type ResponsePayload = { } } } + +export type PriceAdapter = { + getPrices: (baseSymbols: string[], quote: string) => Promise> + getMarketCaps: (baseSymbols: string[], quote: string) => Promise> +} + +export type Config = { + priceAdapter: PriceAdapter + defaultMethod: string + defaultQuote: string +} diff --git a/composite/token-allocation/test/adapter.test.ts b/composite/token-allocation/test/adapter.test.ts index 2906d1b7af..07de4c876b 100644 --- a/composite/token-allocation/test/adapter.test.ts +++ b/composite/token-allocation/test/adapter.test.ts @@ -9,8 +9,7 @@ import { BigNumber } from 'ethers' describe('execute', () => { const jobID = '1' - process.env.DATA_PROVIDER = 'coingecko' - const execute = makeExecute(makeConfig()) + const execute = makeExecute(makeConfig('', 'coingecko')) context('successful calls @integration', () => { const requests = [ diff --git a/conflux/.eslintrc.js b/conflux/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/conflux/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/conflux/README.md b/conflux/README.md new file mode 100644 index 0000000000..442e7227e9 --- /dev/null +++ b/conflux/README.md @@ -0,0 +1,20 @@ +# Chainlink External Adapter for conflux + +## Input Params + +- `base`, `from`, or `coin`: The symbol of the currency to query +- `quote`, `to`, or `market`: The symbol of the currency to convert to +- `endpoint`: Optional endpoint param + +## Output + +```json +{ + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { + "price": 77777.77, + "result": 77777.77 + }, + "statusCode": 200 +} +``` diff --git a/conflux/package.json b/conflux/package.json new file mode 100644 index 0000000000..f541fe6357 --- /dev/null +++ b/conflux/package.json @@ -0,0 +1,48 @@ +{ + "name": "@chainlink/conflux-adapter", + "version": "0.0.3", + "description": "Chainlink conflux adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "conflux" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": { + "js-conflux-sdk": "^1.5.11" + } +} diff --git a/conflux/src/adapter.ts b/conflux/src/adapter.ts new file mode 100644 index 0000000000..d904793f77 --- /dev/null +++ b/conflux/src/adapter.ts @@ -0,0 +1,38 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { AdapterRequest, Execute, AdapterResponse } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT, Config } from './config' +import { conflux } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute = async ( + request: AdapterRequest, + config: Config, +): Promise => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case conflux.NAME: { + return await conflux.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute = (config?: Config): Execute => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/conflux/src/config.ts b/conflux/src/config.ts new file mode 100644 index 0000000000..e59f8eb022 --- /dev/null +++ b/conflux/src/config.ts @@ -0,0 +1,19 @@ +import { util } from '@chainlink/ea-bootstrap' + +export const DEFAULT_ENDPOINT = 'conflux' + +export type Config = { + api: any + rpcUrl: string + networkId: number + privateKey: string +} + +export const makeConfig = (): Config => { + return { + api: {}, + rpcUrl: util.getRequiredEnv('RPC_URL'), + networkId: util.getRequiredEnv('NETWORK_ID'), + privateKey: util.getRequiredEnv('PRIVATE_KEY'), + } +} diff --git a/conflux/src/endpoint/conflux.ts b/conflux/src/endpoint/conflux.ts new file mode 100644 index 0000000000..2304ce059e --- /dev/null +++ b/conflux/src/endpoint/conflux.ts @@ -0,0 +1,84 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { AdapterRequest, AdapterResponse } from '@chainlink/types' +import { Conflux } from 'js-conflux-sdk' +import { ethers } from 'ethers' +import { Config } from '../config' + +const sendFulfillment = async ( + provider: any, + account: any, + to: string, + dataPrefix: string, + functionSelector: string, + value: number, +) => { + const dataPrefixBz = ethers.utils.arrayify(dataPrefix) + const functionSelectorBz = ethers.utils.arrayify(functionSelector) + const valueBz = ethers.utils.zeroPad(ethers.utils.arrayify(Number(value)), 32) + const data = ethers.utils.concat([functionSelectorBz, dataPrefixBz, valueBz]) + + const tx = { + from: account.address, + to: to, + data: ethers.utils.hexlify(data), + gas: 500000, + gasPrice: 1, + } + + return await provider.sendTransaction(tx) +} + +// const customError = (data: any) => data.Response === 'Error' + +const customParams = { + // Use two sets of possible keys in case the node operator + // is using a non-EI initiator where the primary keys are reserved. + address: ['address', 'cfxAddress'], + dataPrefix: ['dataPrefix', 'cfxDataPrefix'], + functionSelector: ['functionSelector', 'cfxFunctionSelector'], + value: ['result', 'value'], +} + +export const NAME = 'conflux' + +export const execute = async ( + request: AdapterRequest, + config: Config, +): Promise => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const provider = new Conflux({ + url: config.rpcUrl, + networkId: Number(config.networkId), + defaultGasRatio: 1.3, + defaultStorageRatio: 1.3, + }) + const account = provider.wallet.addPrivateKey(config.privateKey) + + const jobRunID = validator.validated.id + const address = validator.validated.data.address + const dataPrefix = validator.validated.data.dataPrefix + const functionSelector = validator.validated.data.functionSelector + const value = validator.validated.data.value + + // handling the multiplying + // if (request.data.times !== undefined) { + // value = String(Math.round(Number(value)*Number(request.data.times))) + // } + + const txHash = await sendFulfillment( + provider, + account, + address, + dataPrefix, + functionSelector, + value, + ) + + return Requester.success(jobRunID, { + data: { result: txHash }, + result: txHash, + status: 200, + }) +} diff --git a/conflux/src/endpoint/index.ts b/conflux/src/endpoint/index.ts new file mode 100644 index 0000000000..c192773c00 --- /dev/null +++ b/conflux/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as conflux from './conflux' diff --git a/conflux/src/index.ts b/conflux/src/index.ts new file mode 100644 index 0000000000..5657374b40 --- /dev/null +++ b/conflux/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'conflux' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/conflux/test/adapter.test.ts b/conflux/test/adapter.test.ts new file mode 100644 index 0000000000..b79d741c9e --- /dev/null +++ b/conflux/test/adapter.test.ts @@ -0,0 +1,71 @@ +import { Requester } from '@chainlink/external-adapter' +import { assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +const TestOracleAddress = '0x8688ebA9Bf38cBb1Fa27A8C3Fda11414D6057887' +const FunctionSelector = '0x4ab0d190' +const DataPrefixExample = + '0x9b8c5ab8dc03bb19d6a5047c213537c5000d151e66e49458e054c68cb504904f0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000869795b26b0f5f00ba2b3cf3380634fb5f4806094357855e000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000060346c9e' + +describe('execute', () => { + const jobID = 'fd26a90e0aa84040bc6b4d6f5036a23a' + const execute = makeExecute({ rpcUrl: '', privateKey: '', networkId: 1, api: {} }) + + context('validation error', () => { + const requests = [ + { name: 'empty body', testData: {} }, + { name: 'empty data', testData: { data: {} } }, + { + name: 'value not supplied', + testData: { + id: jobID, + data: { + address: TestOracleAddress, + dataPrefix: DataPrefixExample, + functionSelector: FunctionSelector, + }, + }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'invalid id', + testData: { + id: jobID, + data: { + address: TestOracleAddress, + dataPrefix: DataPrefixExample, + functionSelector: FunctionSelector, + value: '168007', + }, + }, + }, + ] + + 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/conflux/tsconfig.json b/conflux/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/conflux/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/covid-tracker/.eslintrc.js b/covid-tracker/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/covid-tracker/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/covid-tracker/README.md b/covid-tracker/README.md index 987335aa47..78bc7553be 100644 --- a/covid-tracker/README.md +++ b/covid-tracker/README.md @@ -1,25 +1,34 @@ # Chainlink COVID Tracker External Adapter -## Country Data API (default) +Notice: The COVID Tracking Project is ending all data collection on March 7, 2021. The existing API will continue to work until May 2021, but will only include data up to March 7, 2021. -### Endpoint +### Input Parameters -https://api.covidtracking.com/v1/us/current.json +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------: | :---------: | +| | endpoint | The endpoint to use | [us](#US-Endpoint) | us | + +--- + +## US Endpoint ### Input Params -- `location`: The location to retrieve data on (required, one of "usa") -- `field`: The data field to return. Must be in camel case style. (required, see Current US Values on https://covidtracking.com/data/api for a full list) -- `date`: The date to query formatted by `[YEAR][MONTH][DAY]` e.g. `20201012`, if not given defaults to the most recent date with available data (optional) -- `endpoint`: The endpoint to use (optional, defaults to "country", one of "country") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------------------------------------------------------------------: | :-----: | :----------------------------------: | +| | `field` | The object path to access the value that will be returned as the result | | `death` | +| | `date` | The date to query formatted by `[YEAR][MONTH][DAY]` (e.g. `20201012`) | | Most recent date with available data | -### Example Usage +### Sample Input ```json -{ "field": "death", "location": "usa" } +{ + "id": "1", + "data": { "field": "death", "location": "usa" } +} ``` -#### Output +### Sample Output ```json { diff --git a/covid-tracker/adapter.js b/covid-tracker/adapter.js deleted file mode 100644 index 1df950bbd0..0000000000 --- a/covid-tracker/adapter.js +++ /dev/null @@ -1,78 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const ENDPOINT_COUNTRY = 'country' - -const DEFAULT_ENDPOINT = ENDPOINT_COUNTRY - -const countryParams = { - location: ['location'], - field: ['field'], - date: false, -} - -const validDate = (date) => { - if (date) { - if (isNaN(Number(date))) return false - if (date.length != 8) return false - } - return true -} - -const findDay = (payload, date) => { - if (!date) return payload[0] - // All historical dates are given, find the the correct one - for (const index in payload) { - if (payload[index].date === Number(date)) { - return payload[index] - } - // Response body is sorted by descending data. If we see an earlier date we know our result doesn't exist. - if (payload[index].date < Number(date)) { - return null - } - } - return null -} - -const country = (jobRunID, input, callback) => { - const validator = new Validator(input, countryParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const field = validator.validated.data.field - const date = validator.validated.data.date - if (!validDate(date)) return callback(400, Requester.errored(jobRunID, 'Invalid date format')) - - const suffix = date ? 'daily' : 'current' - const url = `https://api.covidtracking.com/v1/us/${suffix}.json` - const config = { url } - - const _handleResponse = (response) => { - response.data = findDay(response.data, date) - if (!response.data) return callback(400, Requester.errored(jobRunID, 'Date not found')) - response.data.result = Requester.validateResultNumber(response.data, [`${field}`]) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config).then(_handleResponse).catch(_handleError) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint.toLowerCase()) { - case ENDPOINT_COUNTRY: - return country(jobRunID, input, callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/covid-tracker/index.js b/covid-tracker/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/covid-tracker/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/covid-tracker/package.json b/covid-tracker/package.json index f69b01c27f..327fba6f3c 100644 --- a/covid-tracker/package.json +++ b/covid-tracker/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/covid-tracker-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink COVID tracker adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/covid-tracker/src/adapter.ts b/covid-tracker/src/adapter.ts new file mode 100644 index 0000000000..0057d5002c --- /dev/null +++ b/covid-tracker/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { us } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case us.NAME: { + return await us.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/covid-tracker/src/config.ts b/covid-tracker/src/config.ts new file mode 100644 index 0000000000..2d2018d2cc --- /dev/null +++ b/covid-tracker/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'us' +export const DEFAULT_BASE_URL = 'https://api.covidtracking.com/v1' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/covid-tracker/src/endpoint/index.ts b/covid-tracker/src/endpoint/index.ts new file mode 100644 index 0000000000..538b7c5a47 --- /dev/null +++ b/covid-tracker/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as us from './us' diff --git a/covid-tracker/src/endpoint/us.ts b/covid-tracker/src/endpoint/us.ts new file mode 100644 index 0000000000..c2e8c892aa --- /dev/null +++ b/covid-tracker/src/endpoint/us.ts @@ -0,0 +1,70 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'us' + +const customParams = { + field: false, + date: false, +} + +const validDate = (date: any) => { + if (date) { + if (isNaN(Number(date))) return false + if (date.length != 8) return false + } + return true +} + +const findDay = (payload: any, date: any) => { + if (!date) return payload[0] + // All historical dates are given, find the the correct one + for (const index in payload) { + if (payload[index].date === Number(date)) { + return payload[index] + } + // Response body is sorted by descending data. If we see an earlier date we know our result doesn't exist. + if (payload[index].date < Number(date)) { + return null + } + } + return null +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const date = validator.validated.data.date + const field = validator.validated.data.field || 'death' + if (!validDate(date)) + throw new AdapterError({ + jobRunID, + message: 'Invalid date format', + statusCode: 400, + }) + const suffix = date ? 'daily' : 'current' + const url = `us/${suffix}.json` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const day = findDay(response.data, date) + if (!day) + throw new AdapterError({ + jobRunID, + message: 'Date not found in response data', + statusCode: 400, + }) + const result = Requester.validateResultNumber(day, [field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/covid-tracker/src/index.ts b/covid-tracker/src/index.ts new file mode 100644 index 0000000000..d93f7dff0e --- /dev/null +++ b/covid-tracker/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'COVID_TRACKER' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/covid-tracker/test/adapter.test.ts b/covid-tracker/test/adapter.test.ts new file mode 100644 index 0000000000..a0ece511ad --- /dev/null +++ b/covid-tracker/test/adapter.test.ts @@ -0,0 +1,90 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'no params supplied', + testData: { id: jobID, data: {} }, + }, + { + name: 'id not supplied', + testData: { data: { field: 'totalTestResults' } }, + }, + { + name: 'without date', + testData: { id: jobID, data: { field: 'totalTestResultsIncrease' } }, + }, + { + name: 'with date', + testData: { id: jobID, data: { field: 'death', date: '20201010' } }, + }, + ] + + 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: {} } }, + { + name: 'unknown date format', + testData: { id: jobID, data: { field: 'deaths', date: 'not_real' } }, + }, + { + name: 'unknown date format 2', + testData: { id: jobID, data: { field: 'deaths', date: '2020111' } }, + }, + { + name: 'date not found', + testData: { id: jobID, data: { field: 'deaths', date: '17601010' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown field', + testData: { id: jobID, data: { field: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/covid-tracker/test/adapter_test.js b/covid-tracker/test/adapter_test.js deleted file mode 100644 index 9ea1099f55..0000000000 --- a/covid-tracker/test/adapter_test.js +++ /dev/null @@ -1,89 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { field: 'totalTestResults', location: 'USA' } }, - }, - { - name: 'without date', - testData: { id: jobID, data: { field: 'totalTestResultsIncrease', location: 'USA' } }, - }, - { - name: 'with date', - testData: { id: jobID, data: { field: 'death', date: '20201010', location: 'USA' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - { - name: 'field not supplied', - testData: { id: jobID, data: { location: 'USA' } }, - }, - { - name: 'location not supplied', - testData: { id: jobID, data: { field: 'deaths' } }, - }, - { - name: 'unknown date format', - testData: { id: jobID, data: { field: 'deaths', date: 'not_real', location: 'USA' } }, - }, - { - name: 'unknown date format 2', - testData: { id: jobID, data: { field: 'deaths', date: '2020111', location: 'USA' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'date not found', - testData: { id: jobID, data: { field: 'deaths', date: '17601010', location: 'USA' } }, - }, - { - name: 'unknown field', - testData: { id: jobID, data: { field: 'not_real', location: 'USA' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/covid-tracker/tsconfig.json b/covid-tracker/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/covid-tracker/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/cryptoapis/README.md b/cryptoapis/README.md index 8b100f2262..5669fc7b2e 100644 --- a/cryptoapis/README.md +++ b/cryptoapis/README.md @@ -1,20 +1,34 @@ -# Chainlink External Adapter for CryptoAPIs +# Chainlink CryptoAPIs External Adapter -## Configuration +### Environment Variables -The adapter takes the following environment variables: +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------: | :-------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://cryptoapis.io/pricing/) | | | +| | API_TIMEOUT | Timeout parameter | | `30000` | -- `API_KEY`: Optional Blochair API key to use -- `API_TIMEOUT`: Optional timeout param, defaults to `30000` +--- -## Input Params +### Input Parameters -- `endpoint`: The requested data point. One of (`price`|`difficulty`|`height`|`balance`). Defaults: `price`. +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price], [difficulty], [height], [balance] | `price` | -### Price +--- -- `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). +## Price Endpoint + +https://docs.cryptoapis.io/rest-apis/crypto-market-data-apis/index#exchange-rates + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------------------------------: | :----------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, `sym` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market`, `convert` | The symbol of the currency to convert to | | | +| 🟡 | `cid` | The CMC coin ID (optional to use in place of `base`) | | | +| 🟡 | `slug` | The CMC coin name (optional to use in place of `base`) | | | ### Output @@ -37,10 +51,16 @@ The adapter takes the following environment variables: } ``` -### Difficulty +## Difficulty & Height Endpoint + +https://docs.cryptoapis.io/rest-apis/blockchain-as-a-service-apis/common/index#common -- `blockchain` or `coin`: The blockchain name (required). -- `network`: The blockchain network name. Default: `mainnet` +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :----------------------------: | :---------------------------------: | :------------------------------------------------: | :---------: | +| ✅ | `blockchain`, `coin`, `market` | The blockchain to retrieve info for | `BTC`, `ETH`, `LTC`, `ETC`, `BCH`, `DOGE`, `DASH`. | | +| | `network` | The blockchain network name | `mainnet`, `testnet` | `mainnet` | ### Output @@ -67,36 +87,6 @@ The adapter takes the following environment variables: } ``` -### 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 diff --git a/cryptoapis/package.json b/cryptoapis/package.json index 95971b66bd..9fc17efe8d 100644 --- a/cryptoapis/package.json +++ b/cryptoapis/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cryptoapis-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "main": "dist/index.js", "scripts": { @@ -10,7 +10,7 @@ "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "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:unit": "mocha --exit --timeout 0 --grep @integration --invert -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()'", diff --git a/cryptoapis/src/endpoint/price.ts b/cryptoapis/src/endpoint/price.ts index b7d4536875..e3264ce4ff 100644 --- a/cryptoapis/src/endpoint/price.ts +++ b/cryptoapis/src/endpoint/price.ts @@ -12,7 +12,6 @@ export const execute = async (config: Config, request: AdapterRequest) => { const validator = new Validator(request, priceParams) if (validator.error) throw validator.error - const jobRunID = validator.validated.id const coin = validator.validated.data.base const market = validator.validated.data.quote const url = `/v1/exchange-rates/${coin}/${market}` diff --git a/cryptoapis/src/index.ts b/cryptoapis/src/index.ts index 5966aeae41..498aea104a 100644 --- a/cryptoapis/src/index.ts +++ b/cryptoapis/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'CRYPTOAPIS' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/cryptoapis/test/bc_info.test.ts b/cryptoapis/test/bc_info.test.ts new file mode 100644 index 0000000000..575d210ea3 --- /dev/null +++ b/cryptoapis/test/bc_info.test.ts @@ -0,0 +1,67 @@ +import { assert } from 'chai' +import { Requester, AdapterError } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('bc_info endpoint', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { blockchain: 'BTC', endpoint: 'difficulty' } }, + }, + { + 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) => { + 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: {} } }, + //this is validation request in this case, since of the default behaviour + { + name: 'unknown blockchain', + testData: { id: jobID, data: { blockchain: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, new AdapterError(error)) + assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/cryptocompare/.eslintrc.js b/cryptocompare/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/cryptocompare/.eslintrc.js +++ b/cryptocompare/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/cryptocompare/README.md b/cryptocompare/README.md index d1603aa8f4..bdf01edf65 100644 --- a/cryptocompare/README.md +++ b/cryptocompare/README.md @@ -1,19 +1,40 @@ # Chainlink External Adapter for CryptoCompare -## Input Params +### Environment Variables -- `base`, `from`, `coin`, or `fsym`: The symbol of the currency to query -- `quote`, `to`, `market`, or `tsyms`: The symbol of the currency to convert to +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :------------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://min-api.cryptocompare.com/pricing) | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | `price` | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { - "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", - "data": { - "USD": 164.02, - "result": 164.02 - }, - "statusCode": 200 + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { + "USD": 164.02, + "result": 164.02 + }, + "statusCode": 200 } ``` diff --git a/cryptocompare/adapter.js b/cryptocompare/adapter.js deleted file mode 100644 index e90549a601..0000000000 --- a/cryptocompare/adapter.js +++ /dev/null @@ -1,49 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customError = (data) => { - if (data.Response === 'Error') return true - return false -} - -const customParams = { - base: ['base', 'from', 'coin', 'fsym'], - quote: ['quote', 'to', 'market', 'tsyms'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'price' - const url = `https://min-api.cryptocompare.com/data/${endpoint}` - const fsym = validator.validated.data.base.toUpperCase() - const tsyms = validator.validated.data.quote.toUpperCase() - - const params = { - fsym, - tsyms, - } - - const config = { - url, - params, - } - - if (process.env.API_KEY) { - config.headers = { - authorization: `Apikey ${util.getRandomRequiredEnv('API_KEY')}`, - } - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [tsyms]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/cryptocompare/index.js b/cryptocompare/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/cryptocompare/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/cryptocompare/package.json b/cryptocompare/package.json index b767d0fe48..d7f003762f 100644 --- a/cryptocompare/package.json +++ b/cryptocompare/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/cryptocompare-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink cryptocompare adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "cryptocompare" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/cryptocompare/src/adapter.ts b/cryptocompare/src/adapter.ts new file mode 100644 index 0000000000..d29317227d --- /dev/null +++ b/cryptocompare/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/cryptocompare/src/config.ts b/cryptocompare/src/config.ts new file mode 100644 index 0000000000..86060ef765 --- /dev/null +++ b/cryptocompare/src/config.ts @@ -0,0 +1,18 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'CRYPTOCOMPARE' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://min-api.cryptocompare.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api.baseURL = config.api.baseUrl || DEFAULT_API_ENDPOINT + if (config.apiKey) + config.api.headers = { + ...config.api.headers, + authorization: `Apikey ${config.apiKey}`, + } + return config +} diff --git a/cryptocompare/src/endpoint/index.ts b/cryptocompare/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/cryptocompare/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/cryptocompare/src/endpoint/price.ts b/cryptocompare/src/endpoint/price.ts new file mode 100644 index 0000000000..4660e7a8e5 --- /dev/null +++ b/cryptocompare/src/endpoint/price.ts @@ -0,0 +1,43 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || 'price' + const url = `/data/${endpoint}` + const symbol = validator.overrideSymbol(AdapterName) + const quote = validator.validated.data.quote.toUpperCase() + + const params = { + fsym: symbol.toUpperCase(), + tsyms: quote, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [quote]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/cryptocompare/src/index.ts b/cryptocompare/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/cryptocompare/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/cryptocompare/test/adapter_test.js b/cryptocompare/test/adapter.test.ts similarity index 56% rename from cryptocompare/test/adapter_test.js rename to cryptocompare/test/adapter.test.ts index e829e64338..0ea5b0e87d 100644 --- a/cryptocompare/test/adapter_test.js +++ b/cryptocompare/test/adapter.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -30,13 +34,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) }) }) }) @@ -56,11 +58,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) + } }) }) }) @@ -78,11 +82,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/cryptocompare/tsconfig.json b/cryptocompare/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/cryptocompare/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/cryptoid/package.json b/cryptoid/package.json index ad214dede7..81d8dda813 100644 --- a/cryptoid/package.json +++ b/cryptoid/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/cryptoid-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink cryptoid adapter.", "keywords": [ "Chainlink", diff --git a/cryptoid/src/index.ts b/cryptoid/src/index.ts index 51bc23e101..a061d38c88 100644 --- a/cryptoid/src/index.ts +++ b/cryptoid/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'CRYPTO-ID' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/cryptomkt/.eslintrc.js b/cryptomkt/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/cryptomkt/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/cryptomkt/README.md b/cryptomkt/README.md index 09b7a42392..b3e86e7b94 100644 --- a/cryptomkt/README.md +++ b/cryptomkt/README.md @@ -1,33 +1,54 @@ # Chainlink External Adapter for CryptoMKT -## Input Params +### Input Parameters -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param (default: "ticker") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [ticker](#Ticker-Endpoint) | ticker | -## Output +--- + +## Ticker Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------------------------------------------: | :-----: | :----------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| | `field` | The object path to access the value that will be returned as the result | | `last_price` | + +### Sample Input + +```json +{ + "id": "1", + "data": { "coin": "BTC", "market": "ARS" } +} +``` + +### Sample Output ```json { - "jobRunID":"1", - "data":{ - "status":"success", - "data":[ - { - "timestamp":"2020-10-06T10:51:51.332281", - "market":"BTCARS", - "bid":"1559980", - "ask":"1578940", - "last_price":"1559980", - "low":"1530000", - "high":"1595500", - "volume":"3.754635486362988398" - } - ], - "result":1559980 - }, - "result":1559980, - "statusCode":200 + "jobRunID": "1", + "data": { + "status": "success", + "data": [ + { + "timestamp": "2020-10-06T10:51:51.332281", + "market": "BTCARS", + "bid": "1559980", + "ask": "1578940", + "last_price": "1559980", + "low": "1530000", + "high": "1595500", + "volume": "3.754635486362988398" + } + ], + "result": 1559980 + }, + "result": 1559980, + "statusCode": 200 } ``` diff --git a/cryptomkt/adapter.js b/cryptomkt/adapter.js deleted file mode 100644 index 18ab736fdb..0000000000 --- a/cryptomkt/adapter.js +++ /dev/null @@ -1,35 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'ticker' - const url = `https://api.cryptomkt.com/v1/${endpoint}` - const base = validator.validated.data.base.toUpperCase() - const quote = validator.validated.data.quote.toUpperCase() - const market = base + quote - - const params = { market } - const config = { url, params } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - 0, - 'last_price', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/cryptomkt/index.js b/cryptomkt/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/cryptomkt/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/cryptomkt/package.json b/cryptomkt/package.json index 52c57daee3..47ce11a8ce 100644 --- a/cryptomkt/package.json +++ b/cryptomkt/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/cryptomkt-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink cryptomkt adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/cryptomkt/src/adapter.ts b/cryptomkt/src/adapter.ts new file mode 100644 index 0000000000..6671088a13 --- /dev/null +++ b/cryptomkt/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { ticker } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case ticker.NAME: { + return await ticker.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/cryptomkt/src/config.ts b/cryptomkt/src/config.ts new file mode 100644 index 0000000000..72dfbb1ccc --- /dev/null +++ b/cryptomkt/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'ticker' +export const DEFAULT_BASE_URL = 'https://api.cryptomkt.com/v1/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/cryptomkt/src/endpoint/index.ts b/cryptomkt/src/endpoint/index.ts new file mode 100644 index 0000000000..1f6d4dc303 --- /dev/null +++ b/cryptomkt/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as ticker from './ticker' diff --git a/cryptomkt/src/endpoint/ticker.ts b/cryptomkt/src/endpoint/ticker.ts new file mode 100644 index 0000000000..a8e36aef2e --- /dev/null +++ b/cryptomkt/src/endpoint/ticker.ts @@ -0,0 +1,39 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'ticker' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = NAME + const base = validator.validated.data.base.toUpperCase() + const quote = validator.validated.data.quote.toUpperCase() + const field = validator.validated.data.field || 'last_price' + const market = base + quote + + const params = { market } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['data', 0, field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/cryptomkt/src/index.ts b/cryptomkt/src/index.ts new file mode 100644 index 0000000000..140fb515ab --- /dev/null +++ b/cryptomkt/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'CRYPTOMKT' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/cryptomkt/test/adapter_test.js b/cryptomkt/test/ticker.test.ts similarity index 52% rename from cryptomkt/test/adapter_test.js rename to cryptomkt/test/ticker.test.ts index 68602b3e75..f709ac5d24 100644 --- a/cryptomkt/test/adapter_test.js +++ b/cryptomkt/test/ticker.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 = [ @@ -14,13 +17,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) }) }) }) @@ -34,11 +35,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) + } }) }) }) @@ -50,11 +53,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/cryptomkt/tsconfig.json b/cryptomkt/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/cryptomkt/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/currencylayer/.eslintrc.js b/currencylayer/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/currencylayer/.eslintrc.js +++ b/currencylayer/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/currencylayer/README.md b/currencylayer/README.md index 8f08514907..f9ed3bb414 100644 --- a/currencylayer/README.md +++ b/currencylayer/README.md @@ -1,33 +1,53 @@ # Chainlink CurrencyLayer External Adapter -## Input Params: +### Environment Variables -- `base` or `from`: Specify the currency to convert from (required) -- `quote` or `to`: Specify the currency to convert to (required) -- `endpoint`: The endpoint to call (optional) -- `amount`: Specify the amount to convert (optional) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :----------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://currencylayer.com/product) | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | `price` | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| 🟡 | `amount` | An amount of the currency | | 1 | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { - "jobRunID": "1", - "data": { - "success": true, - "query": { - "from": "GBP", - "to": "JPY", - "amount": 1 - }, - "info": { - "timestamp": 1519328414, - "rate": 148.972231 + "jobRunID": "2", + "data": { + "success": true, + "terms": "https://currencylayer.com/terms", + "privacy": "https://currencylayer.com/privacy", + "query": { + "from": "BTC", + "to": "USD", + "amount": 1 + }, + "info": { + "timestamp": 1612912326, + "quote": 46500.7849 + }, + "result": 46500.7849 }, - "historical": "", - "date": "2018-02-22", - "result": 148.972231 - }, - "result": 148.972231, - "statusCode": 200 + "result": 46500.7849, + "statusCode": 200 } ``` diff --git a/currencylayer/adapter.js b/currencylayer/adapter.js deleted file mode 100644 index 29f4de5759..0000000000 --- a/currencylayer/adapter.js +++ /dev/null @@ -1,43 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customParams = { - base: ['base', 'from'], - quote: ['quote', 'to'], - endpoint: false, - amount: false, -} - -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 endpoint = validator.validated.data.endpoint || 'convert' - const url = `https://api.currencylayer.com/${endpoint}` - const from = validator.validated.data.base.toUpperCase() - const to = validator.validated.data.quote.toUpperCase() - const amount = validator.validated.data.amount || 1 - const access_key = util.getRandomRequiredEnv('API_KEY') // eslint-disable-line camelcase - - const params = { - from, - to, - amount, - access_key, - } - - const config = { - url, - params, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['result']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/currencylayer/index.js b/currencylayer/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/currencylayer/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/currencylayer/package.json b/currencylayer/package.json index 4af129433d..0453d51ae1 100644 --- a/currencylayer/package.json +++ b/currencylayer/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/currencylayer-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink currencylayer adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "currencylayer" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/currencylayer/src/adapter.ts b/currencylayer/src/adapter.ts new file mode 100644 index 0000000000..9a6c18769b --- /dev/null +++ b/currencylayer/src/adapter.ts @@ -0,0 +1,36 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { convert } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case 'price': + case convert.NAME: { + return await convert.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/currencylayer/src/config.ts b/currencylayer/src/config.ts new file mode 100644 index 0000000000..e4210c05fc --- /dev/null +++ b/currencylayer/src/config.ts @@ -0,0 +1,19 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'CURRENCYLAYER' + +export const DEFAULT_ENDPOINT = 'convert' +export const DEFAULT_API_ENDPOINT = 'https://api.currencylayer.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + access_key: config.apiKey, + }, + } + return config +} diff --git a/currencylayer/src/endpoint/convert.ts b/currencylayer/src/endpoint/convert.ts new file mode 100644 index 0000000000..a502a49929 --- /dev/null +++ b/currencylayer/src/endpoint/convert.ts @@ -0,0 +1,46 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'convert' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + amount: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = `/convert` + const from = validator.overrideSymbol(AdapterName).toUpperCase() + const to = validator.validated.data.quote.toUpperCase() + const amount = validator.validated.data.amount || 1 + + const params = { + ...config.api.params, + from, + to, + amount, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['result']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/currencylayer/src/endpoint/index.ts b/currencylayer/src/endpoint/index.ts new file mode 100644 index 0000000000..20381662bb --- /dev/null +++ b/currencylayer/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as convert from './convert' diff --git a/currencylayer/src/index.ts b/currencylayer/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/currencylayer/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/1forge/test/adapter_test.js b/currencylayer/test/adapter.test.ts similarity index 52% rename from 1forge/test/adapter_test.js rename to currencylayer/test/adapter.test.ts index 4f163acbd8..acd8a65058 100644 --- a/1forge/test/adapter_test.js +++ b/currencylayer/test/adapter.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -22,13 +26,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -48,11 +50,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) + } }) }) }) @@ -70,11 +74,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/currencylayer/tsconfig.json b/currencylayer/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/currencylayer/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/deribit/package.json b/deribit/package.json index db1581e850..4e5a00647c 100644 --- a/deribit/package.json +++ b/deribit/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/deribit-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink Deribit adapter.", "keywords": [ "Chainlink", diff --git a/deribit/src/index.ts b/deribit/src/index.ts index ad93988910..fbcf995b72 100644 --- a/deribit/src/index.ts +++ b/deribit/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeConfig, makeExecute } from './adapter' const NAME = 'Deribit' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/dns-query/package.json b/dns-query/package.json index 54f338e651..7d1a1dbd5e 100644 --- a/dns-query/package.json +++ b/dns-query/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/dns-query-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink DNS query adapter", "keywords": [ "Chainlink", diff --git a/dns-query/src/index.ts b/dns-query/src/index.ts index c299932844..53d44fbaf9 100644 --- a/dns-query/src/index.ts +++ b/dns-query/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'DNS-Query' -export = { NAME, makeConfig, makeExecute, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeConfig, makeExecute, ...expose(makeExecute()) } diff --git a/dxfeed-secondary/README.md b/dxfeed-secondary/README.md index 4f4d9f2257..3f7eb84c5f 100644 --- a/dxfeed-secondary/README.md +++ b/dxfeed-secondary/README.md @@ -6,18 +6,40 @@ An adapter to add secondary mapping for symbols: TSLA ➡️ 'TSLA.US:TEI' ``` -## Configuration +### Environment Variables -This adapter supports the following environment variables: +| Required? | Name | Description | Options | Defaults to | +| :-------: | :----------: | :--------------------------: | :-----: | :----------------------------------------: | +| ✅ | API_USERNAME | | | | +| ✅ | API_PASSWORD | | | | +| 🟡 | API_ENDPOINT | The endpoint for your dxFeed | | `https://tools.dxfeed.com/webservice/rest` | -- `API_USERNAME`: Your API username -- `API_PASSWORD`: Your API password -- `API_ENDPOINT`: The endpoint for your dxFeed. Defaults to the demo endpoint (`https://tools.dxfeed.com/webservice/rest`) +--- -## Input Params +### Input Parameters -- `base`, `from`, or `asset`: The symbol of the asset to query -- `endpoint`: Optional endpoint param, defaults to using the price endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | `price` | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, `market` | The symbol of the currency to query | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +`overrides` should contain the following symbol conversions: + +```bash +N225: 'NKY.IND:TEI', +FTSE: 'UKX.IND:TEI', +TSLA: 'TSLA.US:TEI', +``` ## Output diff --git a/dxfeed-secondary/package.json b/dxfeed-secondary/package.json index 8eb5a68f67..41de8b28dd 100644 --- a/dxfeed-secondary/package.json +++ b/dxfeed-secondary/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/dxfeed-secondary-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink dxfeed secondary adapter.", "keywords": [ "Chainlink", @@ -43,6 +43,6 @@ "typescript": "^3.9.7" }, "dependencies": { - "@chainlink/dxfeed-adapter": "0.0.3" + "@chainlink/dxfeed-adapter": "0.0.4" } } diff --git a/dxfeed-secondary/src/config.ts b/dxfeed-secondary/src/config.ts index 2fad783a96..3784694d41 100644 --- a/dxfeed-secondary/src/config.ts +++ b/dxfeed-secondary/src/config.ts @@ -1,6 +1,8 @@ -import { Requester } from '@chainlink/external-adapter' +import * as dxfeed from '@chainlink/dxfeed-adapter' import { Config } from '@chainlink/types' +export const NAME = 'DXFEED_SECONDARY' + export const DEFAULT_ENDPOINT = 'price' -export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) +export const makeConfig = (prefix?: string): Config => dxfeed.makeConfig(prefix) diff --git a/dxfeed-secondary/src/endpoint/price.ts b/dxfeed-secondary/src/endpoint/price.ts index ef514a13a6..230360039f 100644 --- a/dxfeed-secondary/src/endpoint/price.ts +++ b/dxfeed-secondary/src/endpoint/price.ts @@ -1,27 +1,19 @@ import { Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' import * as dxfeed from '@chainlink/dxfeed-adapter' +import { NAME as AdapterName } from '../config' export const NAME = 'price' const customParams = { - base: ['base', 'from', 'coin'], -} - -const commonSymbols: { [key: string]: string } = { - N225: 'NKY.IND:TEI', - FTSE: 'UKX.IND:TEI', - TSLA: 'TSLA.US:TEI', + base: ['base', 'from', 'coin', 'market'], } export const execute: ExecuteWithConfig = async (request, config) => { const validator = new Validator(request, customParams) if (validator.error) throw validator.error - let symbol = validator.validated.data.base.toUpperCase() - if (symbol in commonSymbols) { - symbol = commonSymbols[symbol] - } + const symbol = validator.overrideSymbol(AdapterName) request.data.base = symbol const exec = dxfeed.makeExecute(config) diff --git a/dxfeed-secondary/src/index.ts b/dxfeed-secondary/src/index.ts index 29fea729f7..21ea85958f 100644 --- a/dxfeed-secondary/src/index.ts +++ b/dxfeed-secondary/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'DXFEED_SECONDARY' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/dxfeed/README.md b/dxfeed/README.md index edf092290e..2c675d53ea 100644 --- a/dxfeed/README.md +++ b/dxfeed/README.md @@ -1,19 +1,33 @@ # Chainlink External Adapter for dxFeed -## Configuration +### Environment Variables -This adapter supports the following environment variables: +| Required? | Name | Description | Options | Defaults to | +| :-------: | :----------: | :--------------------------: | :-----: | :----------------------------------------: | +| ✅ | API_USERNAME | | | | +| ✅ | API_PASSWORD | | | | +| 🟡 | API_ENDPOINT | The endpoint for your dxFeed | | `https://tools.dxfeed.com/webservice/rest` | -- `API_USERNAME`: Your API username -- `API_PASSWORD`: Your API password -- `API_ENDPOINT`: The endpoint for your dxFeed. Defaults to the demo endpoint (`https://tools.dxfeed.com/webservice/rest`) +--- -## Input Params +### Input Parameters -- `base`, `from`, or `asset`: The symbol of the asset to query -- `endpoint`: Optional endpoint param, defaults to using the price endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | `price` | -The `base` param handles the following symbol conversions: +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, `market` | The symbol of the currency to query | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +`overrides` should contain the following symbol conversions: ```bash N225 ➡️ 'NKY.IND:TEI' @@ -22,7 +36,7 @@ TSLA ➡️ 'TSLA:BFX' TSLAX ➡️ 'TSLA.US:TEI' ``` -## Output +### Output ```json { diff --git a/dxfeed/package.json b/dxfeed/package.json index 451ed77f55..a9aa090793 100644 --- a/dxfeed/package.json +++ b/dxfeed/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/dxfeed-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink dxfeed adapter.", "keywords": [ "Chainlink", diff --git a/dxfeed/src/config.ts b/dxfeed/src/config.ts index 2fad783a96..38fc7bab0c 100644 --- a/dxfeed/src/config.ts +++ b/dxfeed/src/config.ts @@ -1,6 +1,23 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' +import { util } from '@chainlink/ea-bootstrap' + +export const NAME = 'DXFEED' export const DEFAULT_ENDPOINT = 'price' +export const DEMO_ENDPOINT = 'https://tools.dxfeed.com/webservice/rest' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEMO_ENDPOINT + if (config.api.baseURL === DEMO_ENDPOINT) + console.warn(`Using demo endpoint: ${DEMO_ENDPOINT} (Please do not use in production!)`) + + const username = util.getEnv('API_USERNAME', prefix) || '' + const password = util.getEnv('API_PASSWORD', prefix) || '' + if (username.length > 0 || password.length > 0) { + config.api.auth = { username, password } + } -export const makeConfig = (prefix?: string): Config => Requester.getDefaultConfig(prefix) + return config +} diff --git a/dxfeed/src/endpoint/price.ts b/dxfeed/src/endpoint/price.ts index 2e27338178..eec884b3c0 100644 --- a/dxfeed/src/endpoint/price.ts +++ b/dxfeed/src/endpoint/price.ts @@ -1,8 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' - -const DEMO_ENDPOINT = 'https://tools.dxfeed.com/webservice/rest' -const DEFAULT_DATA_ENDPOINT = 'events.json' +import { NAME as AdapterName } from '../config' export const NAME = 'price' @@ -12,49 +10,41 @@ const customParams = { base: ['base', 'from', 'coin', 'market'], } -const commonSymbols: { [key: string]: string } = { - N225: 'NKY.IND:TEI', - FTSE: 'UKX.IND:TEI', - TSLA: 'TSLA:BFX', - WTI: 'USO/USD:AFX', +const quoteEventSymbols: { [key: string]: boolean } = { + 'USO/USD:AFX': true, } export const execute: ExecuteWithConfig = async (request, config) => { const validator = new Validator(request, customParams) if (validator.error) throw validator.error - const username = process.env.API_USERNAME - const password = process.env.API_PASSWORD - - const apiEndpoint = process.env.API_ENDPOINT || DEMO_ENDPOINT - if (apiEndpoint === DEMO_ENDPOINT) - console.warn(`Using demo endpoint: ${DEMO_ENDPOINT} (Please do not use in production!)`) - const jobRunID = validator.validated.id - const endpoint = validator.validated.data.endpoint || DEFAULT_DATA_ENDPOINT - const url = `${apiEndpoint}/${endpoint}` - let symbols = validator.validated.data.base.toUpperCase() - if (symbols in commonSymbols) { - symbols = commonSymbols[symbols] - } + const symbol = validator.overrideSymbol(AdapterName).toUpperCase() + const events = quoteEventSymbols[symbol] ? 'Quote' : 'Trade' + + const url = 'events.json' const params = { - events: 'Trade', - symbols, + events, + symbols: symbol, } const options = { ...config.api, url, params, - auth: { username, password }, } const response = await Requester.request(options, customError) - const result = Requester.validateResultNumber(response.data, ['Trade', symbols, 'price']) + const quotePath = ['Quote', symbol, 'bidPrice'] + const tradePath = ['Trade', symbol, 'price'] + const result = Requester.validateResultNumber( + response.data, + events === 'Quote' ? quotePath : tradePath, + ) return Requester.success(jobRunID, { - data: { result }, + data: config.verbose ? { ...response.data, result } : { result }, result, status: 200, }) diff --git a/dxfeed/src/index.ts b/dxfeed/src/index.ts index 4c4aa978ad..21ea85958f 100644 --- a/dxfeed/src/index.ts +++ b/dxfeed/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'DXFEED' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/dydx-stark/package.json b/dydx-stark/package.json index cd93c6f2e5..d8059c5c3c 100644 --- a/dydx-stark/package.json +++ b/dydx-stark/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/dydx-stark-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink dydx-stark adapter.", "keywords": [ "Chainlink", diff --git a/dydx-stark/src/index.ts b/dydx-stark/src/index.ts index 9d80285da5..96feca5a97 100644 --- a/dydx-stark/src/index.ts +++ b/dydx-stark/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'dydx_stark' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/ea-factories/package.json b/ea-factories/package.json index 8804ad06c5..01ce7ea3bf 100644 --- a/ea-factories/package.json +++ b/ea-factories/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/ea-factories", - "version": "0.0.1", + "version": "0.0.2", "description": "Build an external adapter with this package", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/eodhistoricaldata/.eslintrc.js b/eodhistoricaldata/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/eodhistoricaldata/.eslintrc.js +++ b/eodhistoricaldata/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/eodhistoricaldata/README.md b/eodhistoricaldata/README.md index 8a43044a82..fc86f582c9 100644 --- a/eodhistoricaldata/README.md +++ b/eodhistoricaldata/README.md @@ -1,30 +1,49 @@ # Chainlink EOD Historical Data External Adapter -## Input Params -- `asset`, `base`, `from` or `symbol`: The symbol to get the price from (required) -- `endpoint`: The endpoint to use (optional) +### Environment Variables +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | `price` | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------------------------------: | :---------------------------------: | :-----------------------------------------------------------------------: | :---------: | +| ✅ | `base`, `asset`, `from`, `symbol` | The symbol of the currency to query | [list](https://eodhistoricaldata.com/financial-apis/category/data-feeds/) | | + +### Output ```json { - "jobRunID":"278c97ffadb54a5bbb93cfec5f7b5503", - "data":{ - "code":"CL.COMM", - "timestamp":1585167540, - "gmtoffset":0, - "open":24.37, - "high":25.24, - "low":22.91, - "close":24.3, - "volume":590048, - "previousClose":24.01, - "change":0.29, - "change_p":1.208, - "result":24.3 - }, - "result":24.3, - "statusCode":200 + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { + "code": "CL.COMM", + "timestamp": 1585167540, + "gmtoffset": 0, + "open": 24.37, + "high": 25.24, + "low": 22.91, + "close": 24.3, + "volume": 590048, + "previousClose": 24.01, + "change": 0.29, + "change_p": 1.208, + "result": 24.3 + }, + "result": 24.3, + "statusCode": 200 } ``` diff --git a/eodhistoricaldata/adapter.js b/eodhistoricaldata/adapter.js deleted file mode 100644 index 2fc0bda75b..0000000000 --- a/eodhistoricaldata/adapter.js +++ /dev/null @@ -1,46 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const commonKeys = { - N225: 'N225.INDX', - FTSE: 'FTSE.INDX', - BZ: 'BZ.COMM', -} - -const customParams = { - base: ['base', 'asset', 'from', 'symbol'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'real-time' - let symbol = validator.validated.data.base.toUpperCase() - if (commonKeys[symbol]) { - symbol = commonKeys[symbol] - } - const url = `https://eodhistoricaldata.com/api/${endpoint}/${symbol}` - const api_token = util.getRandomRequiredEnv('API_KEY') // eslint-disable-line camelcase - - const params = { - api_token, - fmt: 'json', - } - - const config = { - url, - params, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['close']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/eodhistoricaldata/index.js b/eodhistoricaldata/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/eodhistoricaldata/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/eodhistoricaldata/package.json b/eodhistoricaldata/package.json index bc6ab4d672..2b5889b241 100644 --- a/eodhistoricaldata/package.json +++ b/eodhistoricaldata/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/eodhistoricaldata-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink eodhistoricaldata adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "eodhistoricaldata" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/eodhistoricaldata/src/adapter.ts b/eodhistoricaldata/src/adapter.ts new file mode 100644 index 0000000000..d29317227d --- /dev/null +++ b/eodhistoricaldata/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/eodhistoricaldata/src/config.ts b/eodhistoricaldata/src/config.ts new file mode 100644 index 0000000000..17c0c80fa7 --- /dev/null +++ b/eodhistoricaldata/src/config.ts @@ -0,0 +1,15 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'price' +export const DEFAULT_API_ENDPOINT = 'https://eodhistoricaldata.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { api_token: config.apiKey }, + } + return config +} diff --git a/eodhistoricaldata/src/endpoint/index.ts b/eodhistoricaldata/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/eodhistoricaldata/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/eodhistoricaldata/src/endpoint/price.ts b/eodhistoricaldata/src/endpoint/price.ts new file mode 100644 index 0000000000..e1264b5481 --- /dev/null +++ b/eodhistoricaldata/src/endpoint/price.ts @@ -0,0 +1,48 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'price' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'asset', 'from', 'symbol'], +} + +const commonKeys: { [key: string]: string } = { + N225: 'N225.INDX', + FTSE: 'FTSE.INDX', + BZ: 'BZ.COMM', +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + let symbol = validator.validated.data.base.toUpperCase() + if (commonKeys[symbol]) { + symbol = commonKeys[symbol] + } + const url = `/api/real-time/${symbol}` + + const params = { + ...config.api.params, + fmt: 'json', + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['close']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/eodhistoricaldata/src/index.ts b/eodhistoricaldata/src/index.ts new file mode 100644 index 0000000000..9b1f5b139f --- /dev/null +++ b/eodhistoricaldata/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'EODHISTORICALDATA' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/eodhistoricaldata/test/adapter.test.ts b/eodhistoricaldata/test/adapter.test.ts new file mode 100644 index 0000000000..e87efdcc39 --- /dev/null +++ b/eodhistoricaldata/test/adapter.test.ts @@ -0,0 +1,67 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { name: 'id not supplied', testData: { data: { base: 'N225.INDX' } } }, + { name: 'base', testData: { id: jobID, data: { base: 'N225' } } }, + { name: 'from', testData: { id: jobID, data: { from: 'N225' } } }, + { name: 'asset', testData: { id: jobID, data: { asset: 'N225' } } }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { base: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/eodhistoricaldata/test/adapter_test.js b/eodhistoricaldata/test/adapter_test.js deleted file mode 100644 index b797d3d545..0000000000 --- a/eodhistoricaldata/test/adapter_test.js +++ /dev/null @@ -1,61 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { name: 'id not supplied', testData: { data: { base: 'N225.INDX' } } }, - { name: 'base', testData: { id: jobID, data: { base: 'N225' } } }, - { name: 'from', testData: { id: jobID, data: { from: 'N225' } } }, - { name: 'asset', testData: { id: jobID, data: { asset: 'N225' } } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(data.data.result), 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/eodhistoricaldata/tsconfig.json b/eodhistoricaldata/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/eodhistoricaldata/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/etherchain/.eslintrc.js b/etherchain/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/etherchain/.eslintrc.js +++ b/etherchain/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/etherchain/README.md b/etherchain/README.md index 0ef665715d..e64f693d24 100644 --- a/etherchain/README.md +++ b/etherchain/README.md @@ -1,15 +1,22 @@ # Chainlink External Adapter for Etherchain -## Input Params +### Input Parameters -- `speed`: The speed for gas price to get. Available choices: - - `safeLow` - - `standard` (default) - - `fast` - - `fastest` -- `endpoint`: The endpoint to use (optional, default: gasPriceOracle) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------------: | :---------: | +| | endpoint | The endpoint to use | [gasprice](#gasprice-Endpoint) | `gasprice` | -## Output Format +--- + +## Gas Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------------: | :----------------------------------: | :---------: | +| 🟡 | `speed` | The desired speed | `safeLow`,`average`,`fast`,`fastest` | `average` | + +### Output Format ```json { diff --git a/etherchain/adapter.js b/etherchain/adapter.js deleted file mode 100644 index f8c13a6022..0000000000 --- a/etherchain/adapter.js +++ /dev/null @@ -1,30 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customError = (data) => { - if (Object.keys(data).length < 1) return true - return false -} - -const customParams = { - speed: true, - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'gasPriceOracle' - const speed = validator.validated.data.speed || 'standard' - const url = `https://www.etherchain.org/api/${endpoint}` - - Requester.request(url, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [speed]) * 1e9 - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/etherchain/index.js b/etherchain/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/etherchain/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/etherchain/package.json b/etherchain/package.json index 450c93ecd2..88926c6834 100644 --- a/etherchain/package.json +++ b/etherchain/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/etherchain-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink etherchain adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "etherchain" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/etherchain/src/adapter.ts b/etherchain/src/adapter.ts new file mode 100644 index 0000000000..e15a37cda1 --- /dev/null +++ b/etherchain/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { gasprice } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case gasprice.NAME: { + return await gasprice.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/etherchain/src/config.ts b/etherchain/src/config.ts new file mode 100644 index 0000000000..f0b36ececc --- /dev/null +++ b/etherchain/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'gasprice' +export const DEFAULT_API_ENDPOINT = 'https://www.etherchain.org' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseUrl || DEFAULT_API_ENDPOINT + return config +} diff --git a/etherchain/src/endpoint/gasprice.ts b/etherchain/src/endpoint/gasprice.ts new file mode 100644 index 0000000000..dc47affa69 --- /dev/null +++ b/etherchain/src/endpoint/gasprice.ts @@ -0,0 +1,33 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'gasprice' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + speed: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const speed = validator.validated.data.speed || 'standard' + const url = `/api/gasPriceOracle` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [speed]) * 1e9 + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/etherchain/src/endpoint/index.ts b/etherchain/src/endpoint/index.ts new file mode 100644 index 0000000000..42f92d14ac --- /dev/null +++ b/etherchain/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as gasprice from './gasprice' diff --git a/etherchain/src/index.ts b/etherchain/src/index.ts new file mode 100644 index 0000000000..135854bbfd --- /dev/null +++ b/etherchain/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ETHERCHAIN' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/etherchain/test/adapter_test.js b/etherchain/test/adapter.test.ts similarity index 50% rename from etherchain/test/adapter_test.js rename to etherchain/test/adapter.test.ts index 094fac6d0d..6c10636b16 100644 --- a/etherchain/test/adapter_test.js +++ b/etherchain/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 = [ @@ -26,13 +29,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -57,24 +58,19 @@ 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) + } }) }) }) context('error calls @integration', () => { const requests = [ - { - name: 'unknown endpoint', - testData: { - id: jobID, - data: { speed: 'standard', endpoint: 'not_real' }, - }, - }, { name: 'unknown speed', testData: { @@ -85,11 +81,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/etherchain/tsconfig.json b/etherchain/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/etherchain/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/ethgasstation/.eslintrc.js b/ethgasstation/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/ethgasstation/.eslintrc.js +++ b/ethgasstation/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/ethgasstation/README.md b/ethgasstation/README.md index 8ac390d1d2..8afca85560 100644 --- a/ethgasstation/README.md +++ b/ethgasstation/README.md @@ -1,15 +1,31 @@ # Chainlink External Adapter for EthGasStation -## Input Params +### Environment Variables -- `speed`: The speed for gas price to get. Available choices: - - `safeLow` - - `average` (default) - - `fast` - - `fastest` -- `endpoint`: The endpoint to use (optional, default: ethgasAPI) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | -## Output Format +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------------: | :---------: | +| | endpoint | The endpoint to use | [gasprice](#gasprice-Endpoint) | `gasprice` | + +--- + +## Gas Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------: | :--------------------------------------: | :----------------------------------: | :----------------: | +| 🟡 | `speed` | The desired speed | `safeLow`,`average`,`fast`,`fastest` | `average` | +| 🟡 | `endpoint` | The blockchain id to get gas prices from | | `ethereum-mainnet` | + +### Output Format ```json { diff --git a/ethgasstation/adapter.js b/ethgasstation/adapter.js deleted file mode 100644 index b0c9207a23..0000000000 --- a/ethgasstation/adapter.js +++ /dev/null @@ -1,38 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customError = (data) => { - if (Object.keys(data).length < 1) return true - return false -} - -const customParams = { - speed: false, - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'ethgasAPI' - const speed = validator.validated.data.speed || 'average' - const url = `https://data-api.defipulse.com/api/v1/egs/api/${endpoint}.json?` - const config = { - url, - params: { - 'api-key': util.getRandomRequiredEnv('API_KEY'), - }, - timeout: 10000, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [speed]) * 1e8 - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/ethgasstation/index.js b/ethgasstation/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/ethgasstation/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/ethgasstation/package.json b/ethgasstation/package.json index 983da2b8d1..d41b525cfd 100644 --- a/ethgasstation/package.json +++ b/ethgasstation/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/ethgasstation-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink ethgasstation adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "ethgasstation" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/ethgasstation/src/adapter.ts b/ethgasstation/src/adapter.ts new file mode 100644 index 0000000000..e15a37cda1 --- /dev/null +++ b/ethgasstation/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { gasprice } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case gasprice.NAME: { + return await gasprice.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/ethgasstation/src/config.ts b/ethgasstation/src/config.ts new file mode 100644 index 0000000000..47ed6a7e2c --- /dev/null +++ b/ethgasstation/src/config.ts @@ -0,0 +1,17 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'gasprice' +export const DEFAULT_API_ENDPOINT = 'https://data-api.defipulse.com' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + 'api-key': config.apiKey, + }, + } + return config +} diff --git a/ethgasstation/src/endpoint/gasprice.ts b/ethgasstation/src/endpoint/gasprice.ts new file mode 100644 index 0000000000..f28e75364a --- /dev/null +++ b/ethgasstation/src/endpoint/gasprice.ts @@ -0,0 +1,34 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'gasprice' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + speed: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const speed = validator.validated.data.speed || 'average' + const url = `/api/v1/egs/api/ethgasAPI.json?` + + const options = { + ...config.api, + url, + timeout: 10000, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [speed]) * 1e8 + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/ethgasstation/src/endpoint/index.ts b/ethgasstation/src/endpoint/index.ts new file mode 100644 index 0000000000..42f92d14ac --- /dev/null +++ b/ethgasstation/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as gasprice from './gasprice' diff --git a/ethgasstation/src/index.ts b/ethgasstation/src/index.ts new file mode 100644 index 0000000000..94cb8768c9 --- /dev/null +++ b/ethgasstation/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ETHGASSTATION' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/ethgasstation/test/adapter.test.ts b/ethgasstation/test/adapter.test.ts new file mode 100644 index 0000000000..1312d18baf --- /dev/null +++ b/ethgasstation/test/adapter.test.ts @@ -0,0 +1,88 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { + name: 'empty data', + testData: { data: {} }, + }, + { + name: 'id not supplied', + testData: { + data: { + endpoint: 'ethgasAPI', + speed: 'fast', + }, + }, + }, + { + name: 'speed is average', + testData: { + id: jobID, + data: { speed: 'average' }, + }, + }, + ] + + 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: {}, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown speed', + testData: { + id: jobID, + data: { speed: 'not_real' }, + }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/ethgasstation/test/adapter_test.js b/ethgasstation/test/adapter_test.js deleted file mode 100644 index afd28b5306..0000000000 --- a/ethgasstation/test/adapter_test.js +++ /dev/null @@ -1,89 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'empty data', - testData: { data: {} }, - }, - { - name: 'id not supplied', - testData: { - data: { - endpoint: 'ethgasAPI', - speed: 'fast', - }, - }, - }, - { - name: 'speed is average', - testData: { - id: jobID, - data: { speed: 'average' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(data.data.result), 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { - name: 'empty body', - testData: {}, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown endpoint', - testData: { - id: jobID, - data: { speed: 'standard', endpoint: 'not_real' }, - }, - }, - { - name: 'unknown speed', - testData: { - id: jobID, - data: { speed: 'not_real' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/ethgasstation/tsconfig.json b/ethgasstation/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/ethgasstation/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/ethwrite/.eslintrc.js b/ethwrite/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/ethwrite/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/ethwrite/README.md b/ethwrite/README.md new file mode 100644 index 0000000000..5a85e37fd0 --- /dev/null +++ b/ethwrite/README.md @@ -0,0 +1,56 @@ +# Chainlink External Adapter for writing to Ethereum-based Blockchains + +This external adapter allows you to configure an endpoint and private key to sign and send transactions to external Ethereum-based blockchains. + +A typical workflow of a Chainlink job for this external adapter could look like: + +- Retrieve a piece of data from _some data source_ +- Parse the desired field from that data source's response +- Utilize this adapter to write the value to ChainB +- Parse the transaction object from ChainB for the transaction hash +- Write the transaction hash from ChainB to ChainA + +### Environment Variables + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------------: | :-----------------------------------------------------------------: | :-----: | :---------: | +| ✅ | RPC_URL | RPC endpoint for that client. For example `http://localhost:8545` | | | +| ✅ | PRIVATE_KEY | The private key of a funded account, to sign the transactions from. | | | +| ✅ | CONTRACT_ADDRESS | The contract address that the contract is deployed to. | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :--------: | :-------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------: | :----------: | +| ✅ | exAddr | The address for sending the transaction to. | | | +| | funcId | The setter function to call: | [`0xc2b12a73`(for bytes32), `0xa53b1c1e`(for int256),`0xd2282dc5`(for uint256)] | `0xd2282dc5` | +| | dataType | Pass this only in case you need to encode the data(normally should be already encoded). | [`bytes32`, `int256`, `uint256`] | | +| | result | The result of the previous adapter | | | +| | dataToSend | If specified, this value will be sent instead of `result`. | | | + +--- + +## Output Format + +```json +{ + "jobRunID": "1", + "data": { + "nonce": 1, + "gasPrice": { "type": "BigNumber", "hex": "0x04a817c800" }, + "gasLimit": { "type": "BigNumber", "hex": "0x53d8" }, + "to": "0xBb5696deFD9005e0CfD8bf40ae4C1f9beB6c109d", + "value": { "type": "BigNumber", "hex": "0x00" }, + "data": "0xa53b1c1e0000000000000000000000000000000000000000000000000000000000000036", + "chainId": 1337, + "v": 2710, + "r": "0x58389e578005462f7e8d0c980cd4fca9e16ab4141882f56f98deec0b572da0b3", + "s": "0x17a977f57a88c3dbbab89beac60beb78ae8ab4dffd676831f84d14151c5b03d5", + "from": "0x78C696E4cA526f17380DAc7b6C5fd54B17F3f637", + "hash": "0x311b1e57342f61379bc597813c41bfa7c3535cfc2a49e61b2456c602dd07708e" + }, + "statusCode": 200 +} +``` diff --git a/ethwrite/package.json b/ethwrite/package.json new file mode 100644 index 0000000000..64ce3cb465 --- /dev/null +++ b/ethwrite/package.json @@ -0,0 +1,43 @@ +{ + "name": "@chainlink/ethwrite-adapter", + "version": "0.0.3", + "description": "Chainlink ethwrite adapter.", + "keywords": [ + "ethtx", + "ethwrite" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --delay --exit --timeout 0 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --delay --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --delay --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/ethwrite/src/adapter.ts b/ethwrite/src/adapter.ts new file mode 100644 index 0000000000..0f38f056e1 --- /dev/null +++ b/ethwrite/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT, Config } from './config' +import { txsend } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case txsend.NAME: { + return await txsend.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/ethwrite/src/config.ts b/ethwrite/src/config.ts new file mode 100644 index 0000000000..9b514a337b --- /dev/null +++ b/ethwrite/src/config.ts @@ -0,0 +1,19 @@ +import { util } from '@chainlink/ea-bootstrap' + +export type Config = { + rpcUrl: string + network?: string + privateKey: string + api: any +} + +export const DEFAULT_ENDPOINT = 'txsend' + +export const makeConfig = (): Config => { + return { + api: {}, + rpcUrl: util.getRequiredEnv('RPC_URL'), + network: util.getEnv('NETWORK') || 'mainnet', + privateKey: util.getRequiredEnv('PRIVATE_KEY'), + } +} diff --git a/ethwrite/src/endpoint/index.ts b/ethwrite/src/endpoint/index.ts new file mode 100644 index 0000000000..09de7e2f6e --- /dev/null +++ b/ethwrite/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as txsend from './txsend' diff --git a/ethwrite/src/endpoint/txsend.ts b/ethwrite/src/endpoint/txsend.ts new file mode 100644 index 0000000000..dcdea6dd9a --- /dev/null +++ b/ethwrite/src/endpoint/txsend.ts @@ -0,0 +1,72 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig } from '@chainlink/types' +import { ethers } from 'ethers' +import { Config } from '../config' + +export const NAME = 'txsend' + +const encode = (type: any, value: any) => { + let retVal + switch (type) { + case 'bytes32': + retVal = ethers.utils.formatBytes32String(value) + break + default: + retVal = ethers.utils.defaultAbiCoder.encode([type], [value]) + break + } + return retVal.slice(2) +} + +const customParams = { + exAddr: true, + funcId: false, + dataType: false, + result: false, + dataToSend: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const provider = new ethers.providers.JsonRpcProvider(config.rpcUrl) + const wallet = new ethers.Wallet(config.privateKey, provider) + + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const getUint256 = '0xc2b12a73' + + const jobRunID = validator.validated.id + const externalAddress = validator.validated.data.exAddr + const functionId = validator.validated.data.funcId || getUint256 + // Passing this optionally, in case the data is not encrypted from the previous step + const dataType = validator.validated.data.dataType + // Prioritize data coming from a previous adapter (result), + // but allow dataToSend to be used if specified + const dataToSend = validator.validated.data.result || validator.validated.data.dataToSend || '' + // Ensure we use only 4 bytes for the functionId + let transactionData + if (dataType) { + transactionData = functionId.substring(0, 10) + encode(dataType, dataToSend) + } else { + transactionData = functionId.substring(0, 10) + dataToSend + } + + const transaction = { + to: externalAddress, + data: transactionData, + } + + try { + const tx = await wallet.sendTransaction(transaction) + return Requester.success(jobRunID, { + data: tx, + status: 200, + }) + } catch (e) { + throw new AdapterError({ + jobRunID, + message: e, + statusCode: 400, + }) + } +} diff --git a/ethwrite/src/index.ts b/ethwrite/src/index.ts new file mode 100644 index 0000000000..02b7eacf7a --- /dev/null +++ b/ethwrite/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ETHWRITE' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/ethwrite/test/adapter.test.ts b/ethwrite/test/adapter.test.ts new file mode 100644 index 0000000000..08145eccc8 --- /dev/null +++ b/ethwrite/test/adapter.test.ts @@ -0,0 +1,203 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { + assertSuccess, + assertError, + startChain, + TESTING_PRIVATE_KEY, +} from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' +import { abi, deploy } from './helpers' +import { ethers } from 'ethers' + +// using DELAYED ROOT SUITE in order to start the chain and deploy the contract +setTimeout(async function () { + const chain = await startChain(4444) + const rpcUrl = 'http://localhost:4444' + const address = await deploy(TESTING_PRIVATE_KEY, rpcUrl) + const provider = new ethers.providers.JsonRpcProvider(rpcUrl) + const contract = new ethers.Contract(address, abi, provider) + + describe('execute', async () => { + const execute = makeExecute({ rpcUrl, privateKey: TESTING_PRIVATE_KEY, api: {} }) + after(async () => { + await chain.close() + }) + const jobID = '278c97ffadb54a5bbb93cfec5f7b5503' + const bytes32FuncId = '0xc2b12a73' + const int256FuncId = '0xa53b1c1e' + const uint256FuncId = '0xd2282dc5' + context('successfully writes uint', async () => { + const requests = [ + { + name: 'without already encoded integer', + uncodedResult: 42, + testData: { + id: jobID, + data: { + exAddr: address, + funcId: uint256FuncId, + result: ethers.utils.defaultAbiCoder.encode(['uint256'], [42]).slice(2), + }, + }, + }, + { + name: 'with specifying dataToSend instead of result', + uncodedResult: 12, + testData: { + id: jobID, + data: { + exAddr: address, + funcId: uint256FuncId, + dataType: 'uint256', + dataToSend: 12, + }, + }, + }, + ] + + requests.forEach((req) => { + console.log(req) + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + const contractReadResultUint = await contract.getUint256() + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.equal(data.jobRunID, jobID) + assert.equal(req.uncodedResult, contractReadResultUint.toNumber()) + assert.isNotEmpty(data.data) + }) + }) + }) + + context('successfully writes int', async () => { + const requests = [ + { + name: 'with already encoded negative number', + uncodedResult: -42, + testData: { + id: jobID, + data: { + exAddr: address, + funcId: int256FuncId, + result: ethers.utils.defaultAbiCoder.encode(['int256'], [-42]).slice(2), + }, + }, + }, + { + name: 'with specifying dataToSend instead of result', + uncodedResult: 42, + testData: { + id: jobID, + data: { + exAddr: address, + funcId: int256FuncId, + dataType: 'int256', + dataToSend: 42, + }, + }, + }, + ] + + requests.forEach((req) => { + console.log(req) + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + const contractReadResultInt = await contract.getInt256() + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.equal(data.jobRunID, jobID) + assert.equal(req.uncodedResult, contractReadResultInt.toNumber()) + assert.isNotEmpty(data.data) + }) + }) + }) + + context('successfully writes bytes32', async () => { + const uncodedResult = 'hello world' + const requests = [ + { + name: 'with already encoded string', + uncodedResult, + testData: { + id: jobID, + data: { + exAddr: address, + funcId: bytes32FuncId, + result: ethers.utils.formatBytes32String(uncodedResult).slice(2), + }, + }, + }, + ] + requests.forEach((req) => { + console.log(req) + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + const contractReadResultBytes = await contract.getBytes32() + + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.equal(data.jobRunID, jobID) + assert.equal(req.uncodedResult, ethers.utils.parseBytes32String(contractReadResultBytes)) + assert.isNotEmpty(data.data) + }) + }) + }) + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'with sending string while int datatype and func', + testData: { + id: jobID, + data: { + exAddr: address, + dataType: 'int256', + funcId: int256FuncId, + dataToSend: 'not correct', + }, + }, + }, + { + name: 'with specifying uint256 and negative result', + testData: { + id: jobID, + data: { + exAddr: address, + dataType: 'uint256', + funcId: uint256FuncId, + result: -42, + }, + }, + }, + ] + 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) + } + }) + }) + }) + }) + run() +}, 1000) diff --git a/ethwrite/test/helpers.ts b/ethwrite/test/helpers.ts new file mode 100644 index 0000000000..5160fcc179 --- /dev/null +++ b/ethwrite/test/helpers.ts @@ -0,0 +1,37 @@ +import { ethers } from 'ethers' + +export const abi = [ + 'function setBytes32(bytes32 _value)', + 'function getBytes32() view returns (bytes32)', + 'function setInt256(int256 _value)', + 'function getInt256() view returns (int256)', + 'function setUint256(uint256 _value)', + 'function getUint256() view returns (uint256)', +] + +export const byteCode = + '0x608060405234801561001057600080fd5b50610164806100206000396000f300' + + '6080604052600436106100775763ffffffff7c01000000000000000000000000' + + '000000000000000000000000000000006000350416631f903037811461007c57' + + '806368895979146100a3578063a53b1c1e146100b8578063c2b12a73146100d2' + + '578063d2282dc5146100ea578063f5b53e1714610102575b600080fd5b348015' + + '61008857600080fd5b50610091610117565b6040805191825251908190036020' + + '0190f35b3480156100af57600080fd5b5061009161011d565b3480156100c457' + + '600080fd5b506100d0600435610123565b005b3480156100de57600080fd5b50' + + '6100d0600435610128565b3480156100f657600080fd5b506100d06004356101' + + '2d565b34801561010e57600080fd5b50610091610132565b60005481565b6001' + + '5481565b600255565b600055565b600155565b600254815600a165627a7a7230' + + '582062f6c7201e1a3698c586add5b8e7d1f047a1fcfd8ca2f518c06790fba3de' + + '22d80029' + +// https://docs.ethers.io/ethers.js/html/api-contract.html#deploying-a-contract +export async function deploy(privateKey: string, rpcUrl: string): Promise { + const provider = new ethers.providers.JsonRpcProvider(rpcUrl) + const wallet = new ethers.Wallet(privateKey, provider) + const factory = new ethers.ContractFactory(abi, byteCode, wallet) + const contract = await factory.deploy() + console.log('Contract deployed at: ', contract.address) + console.log('Transaction: ', contract.deployTransaction.hash) + await contract.deployed() + return contract.address +} diff --git a/ethwrite/tsconfig.json b/ethwrite/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/ethwrite/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/example/README.md b/example/README.md index 35030a2710..4841ccc21b 100644 --- a/example/README.md +++ b/example/README.md @@ -1,20 +1,55 @@ # Chainlink External Adapter for Example -## Input Params +A template to be used as an example for new [External Adapters](https://github.com/smartcontractkit/external-adapters-js) -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param +### Environment Variables -## Output +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :----------------------------------------------------------------: | :-----: | :---------: | +| 🟡 | API_KEY | An API key that can be obtained from the data provider's dashboard | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [example](#Example-Endpoint) | example | + +--- + +## Example Endpoint + +An example endpoint description + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | `BTC`, `ETH`, `USD` | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | `BTC`, `ETH`, `USD` | | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "ETH", + "quote": "USD" + } +} +``` + +### Sample Output ```json { - "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", - "data": { - "price": 77777.77, - "result": 77777.77 - }, - "statusCode": 200 + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { + "price": 77777.77, + "result": 77777.77 + }, + "statusCode": 200 } ``` diff --git a/example/package.json b/example/package.json index ff748b0e2f..1285cc90c1 100644 --- a/example/package.json +++ b/example/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/example-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink example adapter.", "keywords": [ "Chainlink", diff --git a/example/src/adapter.ts b/example/src/adapter.ts index 8a9569f61e..17920af450 100644 --- a/example/src/adapter.ts +++ b/example/src/adapter.ts @@ -16,7 +16,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const jobRunID = validator.validated.id const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint) { + switch (endpoint.toLowerCase()) { case example.NAME: { return await example.execute(request, config) } diff --git a/example/src/config.ts b/example/src/config.ts index c7887a70de..87964b9cb0 100644 --- a/example/src/config.ts +++ b/example/src/config.ts @@ -1,10 +1,13 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' +export const NAME = 'EXAMPLE' // This should be filled in with a name corresponding to the data provider using UPPERCASE and _underscores_. + export const DEFAULT_ENDPOINT = 'example' +export const DEFAULT_BASE_URL = 'http://localhost:18081' export const makeConfig = (prefix?: string): Config => { const config = Requester.getDefaultConfig(prefix) - config.api.baseURL = config.api.baseURL || 'http://localhost:18081' + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL return config } diff --git a/example/src/endpoint/example.ts b/example/src/endpoint/example.ts index 52d3da607f..ca2b4af965 100644 --- a/example/src/endpoint/example.ts +++ b/example/src/endpoint/example.ts @@ -1,13 +1,15 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' -export const NAME = 'example' +export const NAME = 'example' // This should be filled in with a lowercase name corresponding to the API endpoint const customError = (data: any) => data.Response === 'Error' const customParams = { base: ['base', 'from', 'coin'], quote: ['quote', 'to', 'market'], + field: false, } export const execute: ExecuteWithConfig = async (request, config) => { @@ -15,22 +17,24 @@ export const execute: ExecuteWithConfig = async (request, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id - const base = validator.validated.data.base + const base = validator.overrideSymbol(AdapterName) const quote = validator.validated.data.quote + const field = validator.validated.data.field || 'price' const url = `price` const params = { base, quote, + api_key: config.apiKey, } - const reqConfig = { ...config.api, params, url } + const options = { ...config.api, params, url } - const response = await Requester.request(reqConfig, customError) - const result = Requester.validateResultNumber(response.data, ['price']) + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [field]) return Requester.success(jobRunID, { - data: { result }, + data: config.verbose ? { ...response.data, result } : { result }, result, status: 200, }) diff --git a/example/src/index.ts b/example/src/index.ts index 41f8a8e898..21ea85958f 100644 --- a/example/src/index.ts +++ b/example/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'EXAMPLE' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/example/test/adapter.test.ts b/example/test/example.test.ts similarity index 100% rename from example/test/adapter.test.ts rename to example/test/example.test.ts diff --git a/external-adapter/package.json b/external-adapter/package.json index 5bf3b8b079..7632045cba 100644 --- a/external-adapter/package.json +++ b/external-adapter/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/external-adapter", - "version": "0.2.8", + "version": "0.2.9", "description": "Helpers for creating Chainlink External Adapters", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -26,6 +26,7 @@ "dependencies": { "@google-cloud/logging-winston": "^4.0.1", "axios": "^0.21.1", + "lodash": "^4.17.21", "uuid": "^8.3.0", "winston": "^3.3.3" }, diff --git a/external-adapter/src/config.ts b/external-adapter/src/config.ts index b6dbfa67e2..0ae8e486a3 100644 --- a/external-adapter/src/config.ts +++ b/external-adapter/src/config.ts @@ -17,10 +17,13 @@ export const constants = { } /* eslint-disable @typescript-eslint/no-unused-vars */ -const cloneNoSecrets = (config: Config): Config => (({ apiKey, ...o }) => o)(config) +const cloneNoSecrets = (config: Config): Config => + (({ apiKey, api: { auth, headers, params, ...api }, ...o }) => ({ api, ...o }))(config) -export function getDefaultConfig(prefix = ''): Config { - const apiKey = util.getEnv(ENV_API_KEY, prefix) +export function getDefaultConfig(prefix = '', requireKey = false): Config { + const apiKey = requireKey + ? util.getRandomRequiredEnv(ENV_API_KEY, ',', prefix) + : util.getRandomEnv(ENV_API_KEY, ',', prefix) const timeout = util.getEnv(ENV_API_TIMEOUT, prefix) return { apiKey, @@ -43,5 +46,4 @@ export function getDefaultConfig(prefix = ''): Config { export function logConfig(config: Config): void { logger.debug('Adapter configuration:', { config: config && cloneNoSecrets(config) }) - if (!config.apiKey) logger.warn('API will be rate limited without an API key.') } diff --git a/external-adapter/src/overrides/presetSymbols.json b/external-adapter/src/overrides/presetSymbols.json new file mode 100644 index 0000000000..53fcb9e287 --- /dev/null +++ b/external-adapter/src/overrides/presetSymbols.json @@ -0,0 +1,56 @@ +{ + "coingecko": { + "COMP": "compound-governance-token", + "FNX": "finnexus", + "UNI": "uniswap", + "grt": "the-graph", + "LINA": "linear", + "FTT": "ftx-token", + "MDX": "mdex" + }, + "nomics": { + "FNX": "FNX2", + "AMP": "AMP2", + "WING": "WING2", + "FTT": "FTXTOKEN", + "MDX": "MDX2", + "RAI": "RAI3" + }, + "coinpaprika": { + "GRT": "grt-the-graph" + }, + "kaiko": { + "uni": "uniswap", + "RAI": "RAIR" + }, + "dxfeed": { + "N225": "NKY.IND:TEI", + "FTSE": "UKX.IND:TEI", + "TSLA": "TSLA:BFX", + "WTI": "USO/USD:AFX", + "NFLX": "NFLX:BFX", + "GOOGL": "GOOGL:BFX", + "AAPL": "AAPL:BFX", + "AMZN": "AMZN:BFX", + "FB": "FB:BFX" + }, + "dxfeed_secondary": { + "NFLX": "NFLX.US:TEI", + "AAPL": "AAPL.US:TEI", + "AMZN": "AMZN.US:TEI", + "N225": "NKY.IND:TEI", + "FTSE": "UKX.IND:TEI", + "TSLA": "TSLA.US:TEI", + "FB": "FB.US:TEI" + }, + "finage": { + "FTSE": "UK100", + "N225": "JAP225" + }, + "tradermade": { + "FTSE": "UK100", + "N225": "JPN225", + "WTI": "OIL", + "BRENT": "UKOIL" + } +} diff --git a/external-adapter/src/validator.ts b/external-adapter/src/validator.ts index 81ffb21d47..09c6ceb1a6 100644 --- a/external-adapter/src/validator.ts +++ b/external-adapter/src/validator.ts @@ -1,7 +1,10 @@ +import { AdapterErrorResponse, Override } from '@chainlink/types' +import { util } from '@chainlink/ea-bootstrap' +import { merge } from 'lodash' import { AdapterError } from './errors' import { Requester } from './requester' import { logger } from './logger' -import { AdapterErrorResponse } from '@chainlink/types' +import presetSymbols from './overrides/presetSymbols.json' export class Validator { input: any @@ -15,6 +18,7 @@ export class Validator { this.customParams = customParams this.validated = { data: {} } this.validateInput() + this.validateOverrides() } validateInput() { @@ -32,20 +36,74 @@ export class Validator { } } } catch (error) { - const message = 'Error validating input.' - if (error instanceof AdapterError) this.error = error - else - this.error = new AdapterError({ - jobRunID: this.validated.id, - statusCode: 400, - message, - cause: error, - }) - logger.error(message, { error: this.error }) - this.errored = Requester.errored(this.validated.id, this.error) + this.parseError(error) } } + validateOverrides() { + try { + if (!this.input.data?.overrides) { + this.validated.overrides = this.formatOverride(presetSymbols) + return + } + this.validated.overrides = this.formatOverride( + merge({ ...presetSymbols }, this.input.data.overrides), + ) + } catch (e) { + this.parseError(e) + } + } + + parseError(error: any) { + const message = 'Error validating input.' + if (error instanceof AdapterError) this.error = error + else + this.error = new AdapterError({ + jobRunID: this.validated.id, + statusCode: 400, + message, + cause: error, + }) + logger.error(message, { error: this.error }) + this.errored = Requester.errored(this.validated.id, this.error) + } + + overrideSymbol = (adapter: string, symbol?: string): string => { + const defaultSymbol = symbol || this.validated.data.base + if (!defaultSymbol) { + throw new AdapterError({ + jobRunID: this.validated.id, + statusCode: 400, + message: `Required parameter not supplied: base`, + }) + } + if (!this.validated.overrides) return defaultSymbol + return ( + this.validated.overrides.get(adapter.toLowerCase())?.get(defaultSymbol.toLowerCase()) || + defaultSymbol + ) + } + + formatOverride = (param: any): Override => { + const _throwInvalid = () => { + const message = `Parameter supplied with wrong format: "overrides"` + throw new AdapterError({ jobRunID: this.validated.id, statusCode: 400, message }) + } + if (!util.isObject(param)) _throwInvalid() + + const _isValid = Object.values(param).every(util.isObject) + if (!_isValid) _throwInvalid() + + const _keyToLowerCase = (entry: [string, any]): [string, any] => { + return [entry[0].toLowerCase(), entry[1]] + } + return new Map( + Object.entries(param) + .map(_keyToLowerCase) + .map(([key, value]) => [key, new Map(Object.entries(value).map(_keyToLowerCase))]), + ) + } + validateRequiredParam(param: any, key: string) { if (typeof param === 'undefined') { const message = `Required parameter not supplied: ${key}` diff --git a/external-adapter/test/validator.test.ts b/external-adapter/test/validator.test.ts index 3f62123a0c..affdcf3af2 100644 --- a/external-adapter/test/validator.test.ts +++ b/external-adapter/test/validator.test.ts @@ -122,4 +122,44 @@ describe('Validator', () => { assert.equal(validator.validated.id, input.id) assert.isUndefined(validator.error) }) + + it('default overrides input is loaded', () => { + const input = { + id: '1', + data: {}, + } + const validator = new Validator(input) + assert.isAbove(validator.validated.overrides?.size, 1) + assert.equal(validator.validated.overrides.get('coingecko').get('uni'), 'uniswap') + }) + + it('overrides input is formatted', () => { + const input = { + id: '1', + data: { + overrides: { + coingecko: { + uni: 'uniswap', + }, + }, + }, + } + const validator = new Validator(input) + assert.equal(validator.validated.overrides.get('coingecko').get('uni'), 'uniswap') + }) + + it('errors if overrides is not properly formatted', () => { + const input = { + id: '1', + data: { + overrides: { + uni: 'uniswap', + }, + }, + } + const validator = new Validator(input) + assert.exists(validator.error) + assert.equal(validator?.error?.statusCode, 400) + assert.equal(validator?.error?.status, 'errored') + }) }) diff --git a/external-adapter/tsconfig.json b/external-adapter/tsconfig.json index 3b4ccf41fa..2f0ab5c255 100644 --- a/external-adapter/tsconfig.json +++ b/external-adapter/tsconfig.json @@ -3,8 +3,19 @@ "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/**/*" + ], + "exclude": [ + "dist", + "**/*.spec.ts", + "**/*.test.ts" + ] } diff --git a/fcsapi/package.json b/fcsapi/package.json index f84ef3e06a..1b004975b9 100644 --- a/fcsapi/package.json +++ b/fcsapi/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/fcsapi-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -10,9 +10,9 @@ "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "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 3000 --grep @integration -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" diff --git a/fcsapi/src/adapter.ts b/fcsapi/src/adapter.ts index b77050e360..ae61063dce 100644 --- a/fcsapi/src/adapter.ts +++ b/fcsapi/src/adapter.ts @@ -34,7 +34,7 @@ export const execute: Execute = async (input) => { endpoint = commonKeys[symbol].endpoint symbol = commonKeys[symbol].id } - const url = `https://fcsapi.com/api-v2/${endpoint}` + const url = `https://fcsapi.com/api-v3/${endpoint}` const access_key = util.getRandomRequiredEnv('API_KEY') // eslint-disable-line camelcase const params = { @@ -48,6 +48,6 @@ export const execute: Execute = async (input) => { } const response = await Requester.request(config, customError) - response.data.result = Requester.validateResultNumber(response.data, ['response', 0, 'price']) + response.data.result = Requester.validateResultNumber(response.data, ['response', 0, 'c']) return Requester.success(jobRunID, response) } diff --git a/fcsapi/src/index.ts b/fcsapi/src/index.ts index 65e70a921a..38c3cada53 100644 --- a/fcsapi/src/index.ts +++ b/fcsapi/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'fcsapi' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/finage/README.md b/finage/README.md index 68cbaac81a..e25ea08e2b 100644 --- a/finage/README.md +++ b/finage/README.md @@ -3,6 +3,7 @@ ## Input Params - `base`, `from`, or `symbol`: The symbol of the asset to query +- `overrides`: (not required) If base provided is found in overrides, that will be used. [Format](../external-adapter/src/overrides/presetSymbols.json) ## Output diff --git a/finage/package.json b/finage/package.json index 471371fd4c..dd95864ee7 100644 --- a/finage/package.json +++ b/finage/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/finage-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink Finage adapter.", "keywords": [ "Chainlink", diff --git a/finage/src/adapter.ts b/finage/src/adapter.ts index 1615a12a1c..166dcd1de3 100644 --- a/finage/src/adapter.ts +++ b/finage/src/adapter.ts @@ -2,17 +2,14 @@ import { Execute } from '@chainlink/types' import { Requester, Validator } from '@chainlink/external-adapter' import { util } from '@chainlink/ea-bootstrap' +export const NAME = 'Finage' + const customParams = { - symbol: ['base', 'from', 'symbol'], + base: ['base', 'from', 'symbol'], to: false, endpoint: false, } -const commonKeys: Record = { - FTSE: 'UK100', - N225: 'JAP225', -} - export const execute: Execute = async (input) => { const validator = new Validator(input, customParams) if (validator.error) throw validator.error @@ -20,9 +17,9 @@ export const execute: Execute = async (input) => { const jobRunID = validator.validated.id const endpoint = validator.validated.data.endpoint || '' let url = `https://api.finage.co.uk/last/${endpoint}` - const symbol = validator.validated.data.symbol.toUpperCase() + const symbol = validator.overrideSymbol(NAME).toUpperCase() const to = (validator.validated.data.to || '').toUpperCase() - const currencies = (commonKeys[symbol] || symbol) + to + const currencies = symbol + to const apikey = util.getRandomRequiredEnv('API_KEY') let params let responsePath diff --git a/finage/src/index.ts b/finage/src/index.ts index ced6af6284..1411e108f7 100644 --- a/finage/src/index.ts +++ b/finage/src/index.ts @@ -1,6 +1,4 @@ -import { expose, util } from '@chainlink/ea-bootstrap' -import { execute } from './adapter' +import { expose } from '@chainlink/ea-bootstrap' +import { execute, NAME } from './adapter' -const NAME = 'Finage' - -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/finnhub/package.json b/finnhub/package.json index 9149afe1f2..7203022837 100644 --- a/finnhub/package.json +++ b/finnhub/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/finnhub-adapter", - "version": "0.0.3", + "version": "0.0.4", "license": "MIT", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/finnhub/src/index.ts b/finnhub/src/index.ts index 59d73c46f9..1bf385af9b 100644 --- a/finnhub/src/index.ts +++ b/finnhub/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'Finnhub' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/fixer/.eslintrc.js b/fixer/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/fixer/.eslintrc.js +++ b/fixer/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/fixer/README.md b/fixer/README.md index 7ceb40c9e7..033c16ce4b 100644 --- a/fixer/README.md +++ b/fixer/README.md @@ -2,34 +2,54 @@ This adapter is for [Fixer.io](https://fixer.io/) and supports the convert endpoint. -## Input Params +### Environment Variables -- `base` or `from`: The target currency to query (required) -- `quote` or `to`: The currency to convert to (required) -- `endpoint`: The endpoint to call (optional) -- `amount`: The amount to convert (optional) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | | | +| 🟡 | `amount` | The amount of `base` currency | | 1 | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ## Output ```json { - "jobRunID": "1", - "data": { - "success": true, - "query": { - "from": "GBP", - "to": "JPY", - "amount": 1 - }, - "info": { - "timestamp": 1519328414, - "rate": 148.972231 + "jobRunID": "1", + "data": { + "success": true, + "query": { + "from": "GBP", + "to": "JPY", + "amount": 1 + }, + "info": { + "timestamp": 1519328414, + "rate": 148.972231 + }, + "historical": "", + "date": "2018-02-22", + "result": 148.972231 }, - "historical": "", - "date": "2018-02-22", - "result": 148.972231 - }, - "result": 148.972231, - "statusCode": 200 + "result": 148.972231, + "statusCode": 200 } ``` diff --git a/fixer/adapter.js b/fixer/adapter.js deleted file mode 100644 index 7ef621b2c1..0000000000 --- a/fixer/adapter.js +++ /dev/null @@ -1,43 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const customParams = { - base: ['base', 'from'], - quote: ['quote', 'to'], - endpoint: false, - amount: false, -} - -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 endpoint = validator.validated.data.endpoint || 'convert' - const url = `https://data.fixer.io/api/${endpoint}` - const from = validator.validated.data.base.toUpperCase() - const to = validator.validated.data.quote.toUpperCase() - const amount = validator.validated.data.amount || 1 - const access_key = util.getRandomRequiredEnv('API_KEY') // eslint-disable-line camelcase - - const params = { - from, - to, - amount, - access_key, - } - - const config = { - url, - params, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['result']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/fixer/index.js b/fixer/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/fixer/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/fixer/package.json b/fixer/package.json index fb004bd1a6..3598298212 100644 --- a/fixer/package.json +++ b/fixer/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/fixer-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink fixer adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "fixer" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/fixer/src/adapter.ts b/fixer/src/adapter.ts new file mode 100644 index 0000000000..9a6c18769b --- /dev/null +++ b/fixer/src/adapter.ts @@ -0,0 +1,36 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { convert } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case 'price': + case convert.NAME: { + return await convert.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/fixer/src/config.ts b/fixer/src/config.ts new file mode 100644 index 0000000000..90fe68bd40 --- /dev/null +++ b/fixer/src/config.ts @@ -0,0 +1,19 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'FIXER' + +export const DEFAULT_ENDPOINT = 'convert' +export const DEFAULT_API_ENDPOINT = 'https://data.fixer.io' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + access_key: config.apiKey, + }, + } + return config +} diff --git a/fixer/src/endpoint/convert.ts b/fixer/src/endpoint/convert.ts new file mode 100644 index 0000000000..087380fb2b --- /dev/null +++ b/fixer/src/endpoint/convert.ts @@ -0,0 +1,46 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'convert' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + amount: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = `/api/convert` + const from = validator.overrideSymbol(AdapterName).toUpperCase() + const to = validator.validated.data.quote.toUpperCase() + const amount = validator.validated.data.amount || 1 + + const params = { + ...config.api.params, + from, + to, + amount, + } + + const options = { + ...config.api, + url, + params, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['result']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/fixer/src/endpoint/index.ts b/fixer/src/endpoint/index.ts new file mode 100644 index 0000000000..20381662bb --- /dev/null +++ b/fixer/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as convert from './convert' diff --git a/fixer/src/index.ts b/fixer/src/index.ts new file mode 100644 index 0000000000..21ea85958f --- /dev/null +++ b/fixer/src/index.ts @@ -0,0 +1,5 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/fixer/test/adapter_test.js b/fixer/test/adapter.test.ts similarity index 59% rename from fixer/test/adapter_test.js rename to fixer/test/adapter.test.ts index e0ef1611aa..f862f1bf49 100644 --- a/fixer/test/adapter_test.js +++ b/fixer/test/adapter.test.ts @@ -1,9 +1,13 @@ -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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -39,13 +43,11 @@ describe('execute', () => { ] requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(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) }) }) }) @@ -77,11 +79,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) + } }) }) }) @@ -111,11 +115,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/fixer/tsconfig.json b/fixer/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/fixer/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/fmpcloud/.eslintrc.js b/fmpcloud/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/fmpcloud/.eslintrc.js +++ b/fmpcloud/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/fmpcloud/README.md b/fmpcloud/README.md index 47ff610ed6..96d7f43dcc 100644 --- a/fmpcloud/README.md +++ b/fmpcloud/README.md @@ -1,41 +1,60 @@ # Chainlink External Adapter for Fmp Cloud -## Input Params +### Environment Variables -- `asset`, `base`, `from`: The target currency to query (required) -- `endpoint`: The endpoint to use (optional) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | -## Output Format +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `asset`, `from`, or `base` | The symbol of the currency to query | | | + +### Output Format ```json { - "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", - "data": [ - { - "symbol": "AAPL", - "name": "Apple Inc.", - "price": 245.52, - "changesPercentage": -0.55, - "change": -1.36, - "dayLow": 244.3, - "dayHigh": 258, - "yearHigh": 327.85, - "yearLow": 170.27, - "marketCap": 1074267815936, - "priceAvg50": 287.00342, - "priceAvg200": 269.64044, - "volume": 68684527, - "avgVolume": 47970853, - "exchange": "NASDAQ", - "open": 250.75, - "previousClose": 246.88, - "eps": 12.595, - "pe": 19.49345, - "earningsAnnouncement": "2020-01-28T21:30:00.000+0000", - "sharesOutstanding": 4375479808, - "timestamp": 1585227237 - } - ], - "statusCode": 200 + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": [ + { + "symbol": "AAPL", + "name": "Apple Inc.", + "price": 245.52, + "changesPercentage": -0.55, + "change": -1.36, + "dayLow": 244.3, + "dayHigh": 258, + "yearHigh": 327.85, + "yearLow": 170.27, + "marketCap": 1074267815936, + "priceAvg50": 287.00342, + "priceAvg200": 269.64044, + "volume": 68684527, + "avgVolume": 47970853, + "exchange": "NASDAQ", + "open": 250.75, + "previousClose": 246.88, + "eps": 12.595, + "pe": 19.49345, + "earningsAnnouncement": "2020-01-28T21:30:00.000+0000", + "sharesOutstanding": 4375479808, + "timestamp": 1585227237 + } + ], + "statusCode": 200 } ``` diff --git a/fmpcloud/adapter.js b/fmpcloud/adapter.js deleted file mode 100644 index 991af74c44..0000000000 --- a/fmpcloud/adapter.js +++ /dev/null @@ -1,54 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const { util } = require('@chainlink/ea-bootstrap') - -const commonKeys = { - N225: '^N225', - FTSE: '^FTSE', - AUD: 'AUDUSD', - CHF: 'CHFUSD', - EUR: 'EURUSD', - GBP: 'GBPUSD', - JPY: 'JPYUSD', -} - -const customError = (data) => { - return data.length === 0 -} - -const customParams = { - base: ['base', 'asset', 'from'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'quote' - let symbol = validator.validated.data.base.toUpperCase() - if (commonKeys[symbol]) { - symbol = commonKeys[symbol] - } - const url = `https://fmpcloud.io/api/v3/${endpoint}/${symbol}` - const apikey = util.getRandomRequiredEnv('API_KEY') - - const params = { - apikey, - } - - const config = { - url, - params, - } - - Requester.request(config, customError) - .then((response) => { - response.data = response.data[0] - response.data.result = Requester.validateResultNumber(response.data, ['price']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/fmpcloud/index.js b/fmpcloud/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/fmpcloud/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/fmpcloud/package.json b/fmpcloud/package.json index f1acc117a9..d04e762c2a 100644 --- a/fmpcloud/package.json +++ b/fmpcloud/package.json @@ -1,15 +1,46 @@ { "name": "@chainlink/fmpcloud-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink fmpcloud adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "fmpcloud" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/fmpcloud/src/adapter.ts b/fmpcloud/src/adapter.ts new file mode 100644 index 0000000000..548e873fa6 --- /dev/null +++ b/fmpcloud/src/adapter.ts @@ -0,0 +1,36 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory, Config } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { quote } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case 'price': + case quote.NAME: { + return await quote.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/fmpcloud/src/config.ts b/fmpcloud/src/config.ts new file mode 100644 index 0000000000..59d1c8ce5d --- /dev/null +++ b/fmpcloud/src/config.ts @@ -0,0 +1,17 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'quote' +export const DEFAULT_API_ENDPOINT = 'https://fmpcloud.io' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_API_ENDPOINT, + params: { + apikey: config.apiKey, + }, + } + return config +} diff --git a/fmpcloud/src/endpoint/index.ts b/fmpcloud/src/endpoint/index.ts new file mode 100644 index 0000000000..181bd379c8 --- /dev/null +++ b/fmpcloud/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as quote from './quote' diff --git a/fmpcloud/src/endpoint/quote.ts b/fmpcloud/src/endpoint/quote.ts new file mode 100644 index 0000000000..7dc20c0d46 --- /dev/null +++ b/fmpcloud/src/endpoint/quote.ts @@ -0,0 +1,46 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'quote' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + base: ['base', 'asset', 'from'], +} + +const commonKeys: { [key: string]: string } = { + N225: '^N225', + FTSE: '^FTSE', + AUD: 'AUDUSD', + CHF: 'CHFUSD', + EUR: 'EURUSD', + GBP: 'GBPUSD', + JPY: 'JPYUSD', +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + let symbol = validator.validated.data.base.toUpperCase() + if (commonKeys[symbol]) { + symbol = commonKeys[symbol] + } + const url = `/api/v3/quote/${symbol}` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, [0, 'price']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/fmpcloud/src/index.ts b/fmpcloud/src/index.ts new file mode 100644 index 0000000000..b17da51ccd --- /dev/null +++ b/fmpcloud/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'FMPCLOUD' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/fmpcloud/test/adapter.test.ts b/fmpcloud/test/adapter.test.ts new file mode 100644 index 0000000000..8de772f790 --- /dev/null +++ b/fmpcloud/test/adapter.test.ts @@ -0,0 +1,67 @@ +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() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' + + context('successful calls @integration', () => { + const requests = [ + { name: 'id not supplied', testData: { data: { base: '^FTSE' } } }, + { name: 'base', testData: { id: jobID, data: { base: 'N225' } } }, + { name: 'from', testData: { id: jobID, data: { from: 'N225' } } }, + { name: 'asset', testData: { id: jobID, data: { asset: 'N225' } } }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { base: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/fmpcloud/test/adapter_test.js b/fmpcloud/test/adapter_test.js deleted file mode 100644 index 4abd401f03..0000000000 --- a/fmpcloud/test/adapter_test.js +++ /dev/null @@ -1,61 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { name: 'id not supplied', testData: { data: { base: '^FTSE' } } }, - { name: 'base', testData: { id: jobID, data: { base: 'N225' } } }, - { name: 'from', testData: { id: jobID, data: { from: 'N225' } } }, - { name: 'asset', testData: { id: jobID, data: { asset: 'N225' } } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(data.data.result), 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/fmpcloud/tsconfig.json b/fmpcloud/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/fmpcloud/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/genesis-volatility/README.md b/genesis-volatility/README.md index 26539ef52f..c8dbae78dd 100644 --- a/genesis-volatility/README.md +++ b/genesis-volatility/README.md @@ -1,31 +1,61 @@ # Chainlink External Adapter for Genesis Volatility -## Input Params +### Environment Variables -- `base`, `from`, `coin` or `symbol`: The symbol of the currency to query -- `key`, `result`, or `period`: The key to get the result from +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | Your API key for Genesis Volatility | | | -## Output +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :----------------------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, or `symbol` | The symbol of the currency to query | | | +| ✅ | `days`, `key`, `result`, or `period` | The key to get the result from | | | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "symbol": "ETH", + "result": "oneDayIv" + } +} +``` + +```json +{ + "id": "1", + "data": { + "coin": "ETH", + "days": 1 + } +} +``` + +### Sample Output ```json { - "jobRunID":"1", - "data":{ - "data":{ - "ChainlinkIv":[ - { - "oneDayIv":65.07, - "twoDayIv":69.29, - "sevenDayIv":70.53, - "fourteenDayIv":73.1, - "twentyOneDayIv":74.93, - "twentyEightDayIv":76.32 - } - ] - }, - "result":76.32 - }, - "result":76.32, - "statusCode":200 + "jobRunID": "1", + "data": { + "data": { + "ChainlinkIv": [ + { + "oneDayIv": 65.07, + "twoDayIv": 69.29, + "sevenDayIv": 70.53, + "fourteenDayIv": 73.1, + "twentyOneDayIv": 74.93, + "twentyEightDayIv": 76.32 + } + ] + }, + "result": 65.07 + }, + "result": 65.07, + "statusCode": 200 } ``` diff --git a/genesis-volatility/package.json b/genesis-volatility/package.json index bcf8438cdc..3441d3ebf9 100644 --- a/genesis-volatility/package.json +++ b/genesis-volatility/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/genesis-volatility-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink Genesis Volatility adapter.", "keywords": [ "Chainlink", diff --git a/genesis-volatility/src/index.ts b/genesis-volatility/src/index.ts index b1d39a36f9..2d65973b75 100644 --- a/genesis-volatility/src/index.ts +++ b/genesis-volatility/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeConfig, makeExecute } from './adapter' const NAME = 'GenesisVolatility' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/geodb/.eslintrc.js b/geodb/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/geodb/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/geodb/README.md b/geodb/README.md new file mode 100644 index 0000000000..d55b728a07 --- /dev/null +++ b/geodb/README.md @@ -0,0 +1,51 @@ +# Chainlink GeoDB External Adapter + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [matches](#Matches-Endpoint) | `matches` | + +--- + +## Matches Endpoint + +Counts the number of matches within the circle specified by a radius and coordinates during the selected time period + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :------------------------------: | :-----: | :---------: | +| ✅ | `lat` | latitude coordinate | | | +| ✅ | `lng` | longitude coordinate | | | +| ✅ | `radius` | radius around coordinates (in m) | | | +| ✅ | `start` | start time (yyyy-mm-dd hh:mm:ss) | | | +| ✅ | `end` | end time (yyyy-mm-dd hh:mm:ss) | | | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "lat": "45.7905", + "lng": "11.9202", + "radius": "500000", + "start": "2021-01-01 20:00:00", + "end": "2021-02-21 20:30:00" + } +} +``` + +### Sample Output + +```json +{ + "jobRunID": "1", + "data": { + "result": 1496 + }, + "result": 1496, + "statusCode": 200 +} +``` diff --git a/geodb/package.json b/geodb/package.json new file mode 100644 index 0000000000..634736fc25 --- /dev/null +++ b/geodb/package.json @@ -0,0 +1,46 @@ +{ + "name": "@chainlink/geodb-adapter", + "version": "0.0.2", + "description": "Chainlink geodb adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "geodb" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": {} +} diff --git a/geodb/src/adapter.ts b/geodb/src/adapter.ts new file mode 100644 index 0000000000..6d5ec5eb63 --- /dev/null +++ b/geodb/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { matches } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case matches.NAME: { + return await matches.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/geodb/src/config.ts b/geodb/src/config.ts new file mode 100644 index 0000000000..06fff710ce --- /dev/null +++ b/geodb/src/config.ts @@ -0,0 +1,14 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'matches' +export const DEFAULT_BASE_URL = 'http://35.195.237.123:8000/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_BASE_URL, + } + return config +} diff --git a/geodb/src/endpoint/index.ts b/geodb/src/endpoint/index.ts new file mode 100644 index 0000000000..15d48aa33d --- /dev/null +++ b/geodb/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as matches from './matches' diff --git a/geodb/src/endpoint/matches.ts b/geodb/src/endpoint/matches.ts new file mode 100644 index 0000000000..861e8de28a --- /dev/null +++ b/geodb/src/endpoint/matches.ts @@ -0,0 +1,39 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'matches' + +const customParams = { + lat: true, + lng: true, + radius: true, + start: true, + end: true, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const lat = validator.validated.data.lat + const lng = validator.validated.data.lng + const radius = validator.validated.data.radius + const start = validator.validated.data.start + const end = validator.validated.data.end + const url = encodeURI(`/matches?start=${start}&end=${end}&lat=${lat}&lng=${lng}&radius=${radius}`) + + const reqConfig = { + ...config.api, + url, + } + + const response = await Requester.request(reqConfig) + const result = parseInt(response.data.matches) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/geodb/src/index.ts b/geodb/src/index.ts new file mode 100644 index 0000000000..f281de4fe7 --- /dev/null +++ b/geodb/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'GEODB' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/geodb/test/adapter.test.ts b/geodb/test/adapter.test.ts new file mode 100644 index 0000000000..8dbf928295 --- /dev/null +++ b/geodb/test/adapter.test.ts @@ -0,0 +1,221 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'matches', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + ] + + 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) + }).timeout(30000) + }) + }) + + context('validation error', () => { + const requests = [ + { name: 'empty body', testData: {} }, + { name: 'empty data', testData: { data: {} } }, + { + name: 'lat not supplied', + testData: { + id: jobID, + data: { + lng: '11.9202', + radius: '500000', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'lng not supplied', + testData: { + id: jobID, + data: { + lat: '45.7905', + radius: '500000', + start: '2021-01-01 20:00:00', + end: '2021-02-21 20:30:00', + }, + }, + }, + { + name: 'radius not supplied', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + start: '2021-01-01 20:00:00', + end: '2021-02-21 20:30:00', + }, + }, + }, + { + name: 'start not supplied', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + end: '2021-02-21 20:30:00', + }, + }, + }, + { + name: 'end not supplied', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + start: '2021-01-01 20:00:00', + }, + }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'invalid lat', + testData: { + id: jobID, + data: { + lat: '100', + lng: '11.9202', + radius: '500000', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'invalid lng', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '200', + radius: '500000', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'invalid radius', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '-100', + start: '2021-01-01 00:00:00', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'invalid start', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + start: 'invalid_time', + end: '2021-02-21 02:30:00', + }, + }, + }, + { + name: 'invalid end', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + start: '2021-01-01 00:00:00', + end: 'invalid_time', + }, + }, + }, + { + name: 'end < start', + testData: { + id: jobID, + data: { + lat: '45.7905', + lng: '11.9202', + radius: '500000', + end: '2021-01-01 00:00:00', + start: '2021-02-21 02:30:00', + }, + }, + }, + ] + + 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) + } + }).timeout(30000) + }) + }) +}) diff --git a/geodb/tsconfig.json b/geodb/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/geodb/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/google-finance/.eslintrc.js b/google-finance/.eslintrc.js deleted file mode 100644 index 7d045a59de..0000000000 --- a/google-finance/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../.eslintrc.js') diff --git a/google-finance/README.md b/google-finance/README.md deleted file mode 100644 index 37444f8a5a..0000000000 --- a/google-finance/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Chainlink Google Finance External Adapter - -This adapter is for Google Finance and supports the quote endpoint. - -## Input params - -- `base`, `from` or `asset`: The asset to query - -## Output - -```json -{ - "jobRunID": "1", - "data": { - "symbol": "NASDAQ:AAPL", - "companyName": "297.92 USD", - "ticker": 297.92, - "last": 297.92, - "open": 295.06, - "high": 298.07, - "low": 294.46, - "marketCap": "1.29T", - "peRatio": 23.36, - "yield": 1.1, - "prevClose": 293.16, - "high52week": 327.85, - "low52week": 170.27, - "result": 297.92 - }, - "result": 297.92, - "statusCode": 200 -} -``` - -## Build instructions - -When building this adapter for AWS Lambda, there are a few extra steps: - -After running `make zip adapter=google-finance`, 3 extra files will be created in the `google-finance/dist/` directory: - -- aws.tar.br -- chromium.br -- swiftshader.tar.br - -**These files needs to be moved inside the `google-finance-adapter.zip` file that was generated.** You can then upload -the updated zip and proceed with the usual steps. diff --git a/google-finance/adapter.js b/google-finance/adapter.js deleted file mode 100644 index ca901918e6..0000000000 --- a/google-finance/adapter.js +++ /dev/null @@ -1,37 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const google = require('boxhock_google-finance-data') - -const commonKeys = { - N225: 'INDEXNIKKEI:NI225', - FTSE: 'INDEXFTSE:UKX', -} - -const customParams = { - base: ['base', 'from', 'asset'], -} - -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 symbol = validator.validated.data.base.toUpperCase() - if (commonKeys[symbol]) { - symbol = commonKeys[symbol] - } - - google - .getSymbol(symbol) - .then((data) => { - const status = 200 - const response = { - data, - status, - } - response.data.result = Requester.validateResultNumber(response.data, ['ticker']) - callback(status, Requester.success(jobRunID, response)) - }) - .catch((err) => callback(500, Requester.errored(jobRunID, err.message))) -} - -module.exports.execute = execute diff --git a/google-finance/index.js b/google-finance/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/google-finance/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/google-finance/package.json b/google-finance/package.json deleted file mode 100644 index 856162484d..0000000000 --- a/google-finance/package.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "@chainlink/google-finance-adapter", - "version": "0.0.3", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "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" - }, - "dependencies": { - "boxhock_google-finance-data": "^0.2.0" - } -} diff --git a/google-finance/test/adapter_test.js b/google-finance/test/adapter_test.js deleted file mode 100644 index 1b6350514b..0000000000 --- a/google-finance/test/adapter_test.js +++ /dev/null @@ -1,98 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { base: 'NASDAQ:AAPL' } }, - }, - { - name: 'base', - testData: { - id: jobID, - data: { base: 'N225' }, - }, - }, - { - name: 'from', - testData: { - id: jobID, - data: { from: 'N225' }, - }, - }, - { - name: 'asset', - testData: { - id: jobID, - data: { asset: 'N225' }, - }, - }, - { - name: 'common key FTSE', - testData: { - id: jobID, - data: { asset: 'FTSE' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(Number(data.result), 0) - assert.isAbove(Number(data.data.result), 0) - done() - }) - }) - }) - }) - - context('validation calls', () => { - const requests = [ - { - name: 'empty body', - testData: {}, - }, - { - name: 'empty data', - testData: { data: {} }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { - id: jobID, - data: { base: 'not_real' }, - }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/hardhat.config.js b/hardhat.config.js new file mode 100644 index 0000000000..7576cf562b --- /dev/null +++ b/hardhat.config.js @@ -0,0 +1,29 @@ +// Hardhat configuration +// See (https://hardhat.org/config/) for more details. + +const { TESTING_PRIVATE_KEY } = require('./test-helpers/src/hardhat_config.json') + +module.exports = { + defaultNetwork: 'hardhat', + networks: { + hardhat: { + chainId: 31337, + accounts: [ + { + privateKey: TESTING_PRIVATE_KEY, + balance: '10000000000000000000000', + }, + ], + }, + }, + // NOTE: The compiler isn't currently used, but this suppresses a warning + solidity: { + version: '0.5.15', + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, +} diff --git a/harmony/package.json b/harmony/package.json index d3d757e7c3..f3ff311a92 100644 --- a/harmony/package.json +++ b/harmony/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/harmony-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink harmony adapter.", "keywords": [ "Chainlink", diff --git a/harmony/src/index.ts b/harmony/src/index.ts index 51891d31d1..c224160e53 100644 --- a/harmony/src/index.ts +++ b/harmony/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'harmony' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/helpers/reference-data-reader/package.json b/helpers/reference-data-reader/package.json index 605b361fb1..bf78ea41d6 100644 --- a/helpers/reference-data-reader/package.json +++ b/helpers/reference-data-reader/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/reference-data-reader", - "version": "0.0.3", + "version": "0.0.4", "description": "", "main": "dist/index.js", "types": "dist/index.d.ts", diff --git a/iex-cloud/README.md b/iex-cloud/README.md index b1ab9a0083..ed69749ece 100644 --- a/iex-cloud/README.md +++ b/iex-cloud/README.md @@ -5,6 +5,7 @@ - `base`, `from`, `coin`, `asset` or `symbol`: The symbol to query - `quote`, `to`, or `market`: The symbol to convert to (required when endpoint is "crypto") - `endpoint`: The endpoint to query, one of "stock" or "crypto". Default: stock. +- `overrides`: (not required) If base provided is found in overrides, that will be used. [Format](../external-adapter/src/overrides/presetSymbols.json) ## Output diff --git a/iex-cloud/package.json b/iex-cloud/package.json index fe4e866d76..f223592786 100644 --- a/iex-cloud/package.json +++ b/iex-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/iex-cloud-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink iex-cloud adapter.", "keywords": [ "Chainlink", diff --git a/iex-cloud/src/config.ts b/iex-cloud/src/config.ts index 809421a362..553a732057 100644 --- a/iex-cloud/src/config.ts +++ b/iex-cloud/src/config.ts @@ -1,6 +1,8 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' +export const NAME = 'IEXCloud' + export const DEFAULT_ENDPOINT = 'stock' export const makeConfig = (prefix?: string): Config => { diff --git a/iex-cloud/src/endpoint/crypto.ts b/iex-cloud/src/endpoint/crypto.ts index 3637fd3350..068c36d9a3 100644 --- a/iex-cloud/src/endpoint/crypto.ts +++ b/iex-cloud/src/endpoint/crypto.ts @@ -1,5 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' export const NAME = 'crypto' @@ -13,7 +14,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id - const base = validator.validated.data.base + const base = validator.overrideSymbol(AdapterName) const quote = validator.validated.data.quote const url = `crypto/${base.toUpperCase()}${quote.toUpperCase()}/quote` diff --git a/iex-cloud/src/endpoint/stock.ts b/iex-cloud/src/endpoint/stock.ts index 4f1135573e..6303fa5a20 100644 --- a/iex-cloud/src/endpoint/stock.ts +++ b/iex-cloud/src/endpoint/stock.ts @@ -1,5 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' export const NAME = 'stock' @@ -12,7 +13,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id - const base = validator.validated.data.base + const base = validator.overrideSymbol(AdapterName) const url = `stock/${base.toUpperCase()}/quote` const params = { diff --git a/iex-cloud/src/index.ts b/iex-cloud/src/index.ts index 372552c028..21ea85958f 100644 --- a/iex-cloud/src/index.ts +++ b/iex-cloud/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'IEX Cloud' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/json-rpc/package.json b/json-rpc/package.json index 8e2b7054df..9552ac8e72 100644 --- a/json-rpc/package.json +++ b/json-rpc/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/json-rpc-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "", "author": "Thomas Hodges (thomas@smartcontract.com)", "license": "MIT", diff --git a/json-rpc/src/index.ts b/json-rpc/src/index.ts index ff34a1db3b..f444ff67af 100644 --- a/json-rpc/src/index.ts +++ b/json-rpc/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'JSON-RPC' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/kaiko/README.md b/kaiko/README.md index 904eae7851..1805fa9750 100644 --- a/kaiko/README.md +++ b/kaiko/README.md @@ -1,11 +1,22 @@ # Chainlink Kaiko External Adapter -## Input Params +### Environment Variables -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://www.coinapi.io/pricing) | | | -## Output +--- + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { diff --git a/kaiko/package.json b/kaiko/package.json index 5aacde013c..5ebfc5eb85 100644 --- a/kaiko/package.json +++ b/kaiko/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/kaiko-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink kaiko adapter.", "keywords": [ "Chainlink", diff --git a/kaiko/src/adapter.ts b/kaiko/src/adapter.ts index 1b8f06faca..432863fae4 100644 --- a/kaiko/src/adapter.ts +++ b/kaiko/src/adapter.ts @@ -1,16 +1,19 @@ import { Requester, Validator } from '@chainlink/external-adapter' -import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' -import { DEFAULT_INTERVAL, DEFAULT_SORT, DEFAULT_MILLISECONDS, makeConfig } from './config' +import { Config, ExecuteWithConfig, ExecuteFactory, Override } from '@chainlink/types' +import { + DEFAULT_INTERVAL, + DEFAULT_SORT, + DEFAULT_MILLISECONDS, + makeConfig, + NAME as AdapterName, +} from './config' const customError = (data: any) => data.result === 'error' const customParams = { base: ['base', 'from', 'coin'], quote: ['quote', 'to', 'market'], -} - -const convertId: Record = { - uni: 'uniswap', + includes: false, } export const execute: ExecuteWithConfig = async (request, config) => { @@ -20,14 +23,18 @@ export const execute: ExecuteWithConfig = async (request, config) => { Requester.logConfig(config) const jobRunID = validator.validated.id - let base = validator.validated.data.base.toLowerCase() + const base = validator.overrideSymbol(AdapterName).toLowerCase() const quote = validator.validated.data.quote.toLowerCase() + const includes = validator.validated.data.includes || [] - // Correct common tickers that are misidentified - base = convertId[base] || base - + let inverse = false let url = `/spot_exchange_rate/${base}/${quote}` - if (quote === 'eth') { + if (includes.length > 0 && base === 'digg' && includes[0].toLowerCase() === 'wbtc') { + inverse = true + url = `/spot_direct_exchange_rate/wbtc/digg` + } else if (includes.length > 0 && includes[0].toLowerCase() === 'weth') { + url = `/spot_direct_exchange_rate/${base}/weth` + } else if (quote === 'eth') { url = `/spot_direct_exchange_rate/${base}/${quote}` } @@ -52,11 +59,15 @@ export const execute: ExecuteWithConfig = async (request, config) => { } const response = await Requester.request(requestConfig, customError) - const result = Requester.validateResultNumber( + let result = Requester.validateResultNumber( // sometimes, the most recent(fraction of a second) data contain null price response.data.data.filter((x: any) => x.price !== null), [0, 'price'], ) + if (inverse && result != 0) { + result = 1 / result + } + return Requester.success(jobRunID, { data: { ...response.data, diff --git a/kaiko/src/config.ts b/kaiko/src/config.ts index b87f25a111..87e4ac8792 100644 --- a/kaiko/src/config.ts +++ b/kaiko/src/config.ts @@ -1,13 +1,15 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' +export const NAME = 'KAIKO' + export const DEFAULT_INTERVAL = '1m' export const DEFAULT_SORT = 'desc' -export const DEFAULT_MILLISECONDS = 1000000 +export const DEFAULT_MILLISECONDS = 1800000 export const DEFAULT_API_ENDPOINT = 'https://us.market-api.kaiko.io/v2/data/trades.v1' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT config.api.headers['X-Api-Key'] = config.apiKey return config diff --git a/kaiko/src/index.ts b/kaiko/src/index.ts index 2377615e0c..21ea85958f 100644 --- a/kaiko/src/index.ts +++ b/kaiko/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'KAIKO' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/kaiko/test/adapter.test.ts b/kaiko/test/adapter.test.ts index be0bb428fe..65e3ae7d4b 100644 --- a/kaiko/test/adapter.test.ts +++ b/kaiko/test/adapter.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ diff --git a/lcx/package.json b/lcx/package.json index c0f6f51b3a..229710715c 100644 --- a/lcx/package.json +++ b/lcx/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/lcx-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink lcx adapter.", "keywords": [ "Chainlink", diff --git a/lcx/src/index.ts b/lcx/src/index.ts index 8fc9bb9ba5..9cef5d414b 100644 --- a/lcx/src/index.ts +++ b/lcx/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'lcx' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/linkpool/package.json b/linkpool/package.json index d1e592e854..debf1e6e94 100644 --- a/linkpool/package.json +++ b/linkpool/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/linkpool-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink linkpool adapter.", "keywords": [ "Chainlink", diff --git a/linkpool/src/index.ts b/linkpool/src/index.ts index f1b413a725..5198ad990a 100644 --- a/linkpool/src/index.ts +++ b/linkpool/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'LinkPool' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/lition/.eslintrc.js b/lition/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/lition/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/lition/README.md b/lition/README.md index 368a4e49b8..c80a49635d 100644 --- a/lition/README.md +++ b/lition/README.md @@ -1,12 +1,16 @@ # Chainlink External Adapter for Lition -## Price API +### Input Parameters -Returns the price in Euros per MWh +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [energy](#Energy-Endpoint) | energy | + +--- -### Endpoint +## Energy Endpoint -https://staking.lition.io/api/v1/energy/source/1/date/2020-10-07/hour/15/ +Returns the price in Euros per MWh ### Sources @@ -18,17 +22,22 @@ https://staking.lition.io/api/v1/energy/source/1/date/2020-10-07/hour/15/ ### Input Params -- `source`: The provider to retrieve price data from (required, e.g. "1", "2", or "3") -- `date`: The date to query formatted by `[YEAR]-[MONTH]-[DAY]` e.g. `2020-10-12`, if not provided defaults to the current UTC date (optional) -- `hour`: The hour to query (0-23), if not provided defaults to the current UTC hour (optional) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------------------------------------------------------------: | :--------: | :------------------: | +| ✅ | `source` | The provider to retrieve price data from (e.g. "1", "2", or "3") | | | +| | `date` | The date to query formatted by `[YEAR]-[MONTH]-[DAY]` (e.g. `2020-10-12`) | | The current UTC date | +| | `hour` | The hour to query | `0` - `23` | The current UTC hour | -### Example Usage +### Sample Input ```json -{ "source": "1" } +{ + "id": "1", + "data": { "source": "1" } +} ``` -#### Output +### Sample Output ```json { diff --git a/lition/adapter.js b/lition/adapter.js deleted file mode 100644 index a8e8df647b..0000000000 --- a/lition/adapter.js +++ /dev/null @@ -1,35 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customError = (data) => data.Response === 'Error' - -const customParams = { - source: ['source'], - date: false, - hour: false, -} - -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 source = validator.validated.data.source - const currentTime = new Date() - const date = validator.validated.data.date || `${currentTime.toISOString().slice(0, 10)}` // YYYY-MM-DD - const hour = validator.validated.data.hour || currentTime.getUTCHours() - - const url = `https://staking.lition.io/api/v1/energy/source/${source}/date/${date}/hour/${hour}/` - - const config = { - url, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['price']) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/lition/index.js b/lition/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/lition/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/lition/package.json b/lition/package.json index 16de2de06f..8caa187ea1 100644 --- a/lition/package.json +++ b/lition/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/lition-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Lition adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/lition/src/adapter.ts b/lition/src/adapter.ts new file mode 100644 index 0000000000..6e7227e236 --- /dev/null +++ b/lition/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { energy } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case energy.NAME: { + return await energy.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/lition/src/config.ts b/lition/src/config.ts new file mode 100644 index 0000000000..df91dafaa1 --- /dev/null +++ b/lition/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'energy' +export const DEFAULT_BASE_URL = 'https://staking.lition.io/api/v1' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/lition/src/endpoint/energy.ts b/lition/src/endpoint/energy.ts new file mode 100644 index 0000000000..32a27eeb31 --- /dev/null +++ b/lition/src/endpoint/energy.ts @@ -0,0 +1,37 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'energy' + +const customParams = { + source: true, + date: false, + hour: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const source = validator.validated.data.source + const currentTime = new Date() + const date = validator.validated.data.date || `${currentTime.toISOString().slice(0, 10)}` // YYYY-MM-DD + const hour = validator.validated.data.hour || currentTime.getUTCHours() + + const url = `energy/source/${source}/date/${date}/hour/${hour}/` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['price']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/lition/src/endpoint/index.ts b/lition/src/endpoint/index.ts new file mode 100644 index 0000000000..4df7621a3e --- /dev/null +++ b/lition/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as energy from './energy' diff --git a/lition/src/index.ts b/lition/src/index.ts new file mode 100644 index 0000000000..db405e8c0e --- /dev/null +++ b/lition/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'LITION' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/lition/test/adapter_test.js b/lition/test/adapter.test.ts similarity index 54% rename from lition/test/adapter_test.js rename to lition/test/adapter.test.ts index 761db0c456..461d27df7c 100644 --- a/lition/test/adapter_test.js +++ b/lition/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 = [ @@ -29,13 +32,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) }) }) }) @@ -47,11 +48,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) + } }) }) }) @@ -73,11 +76,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/lition/tsconfig.json b/lition/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/lition/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/marketstack/README.md b/marketstack/README.md index d9cc4ba66d..0600f10cd5 100644 --- a/marketstack/README.md +++ b/marketstack/README.md @@ -1,13 +1,32 @@ # Chainlink External Adapter for Marketstack -## Input Params +### Environment Variables -- `base`, `from`, or `coin`: The symbol of the currency to query -- `endpoint`: Optional endpoint param(default:eod) -- `interval`: The interval for the data (default: 1min) -- `limit`: The limit for number of results (default: 1) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :-------------------------: | :---------: | +| | endpoint | The endpoint to use | [eod](#End-Of-Day-Endpoint) | eod | + +--- + +## End Of Day Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| 🟡 | `interval` | The symbol of the currency to convert to | | 1min | +| 🟡 | `limit` | The limit for number of results | | 1 | + +### Output ```json { diff --git a/marketstack/package.json b/marketstack/package.json index de67289c4a..cf3b092248 100644 --- a/marketstack/package.json +++ b/marketstack/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/marketstack-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink marketstack adapter.", "keywords": [ "Chainlink", diff --git a/marketstack/src/adapter.ts b/marketstack/src/adapter.ts index ca5267081b..f9a9effcfd 100644 --- a/marketstack/src/adapter.ts +++ b/marketstack/src/adapter.ts @@ -17,7 +17,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint) { - case eod.Name: { + case eod.NAME: { return await eod.execute(config, request) } default: { diff --git a/marketstack/src/config.ts b/marketstack/src/config.ts index a598499209..6e628b5ba7 100644 --- a/marketstack/src/config.ts +++ b/marketstack/src/config.ts @@ -7,7 +7,7 @@ export const DEFAULT_ENDPOINT = 'eod' export const DEFAULT_API_ENDPOINT = 'http://api.marketstack.com/v1/' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT return config } diff --git a/marketstack/src/endpoint/eod.ts b/marketstack/src/endpoint/eod.ts index bdb7615588..d21329114d 100644 --- a/marketstack/src/endpoint/eod.ts +++ b/marketstack/src/endpoint/eod.ts @@ -1,9 +1,8 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' import { DEFAULT_INTERVAL, DEFAULT_LIMIT } from '../config' -import { util } from '@chainlink/ea-bootstrap' -export const Name = 'eod' +export const NAME = 'eod' const customError = (data: any) => data.Response === 'Error' diff --git a/marketstack/src/index.ts b/marketstack/src/index.ts index c69e93ebac..4781b9d99f 100644 --- a/marketstack/src/index.ts +++ b/marketstack/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'MARKETSTACK' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/marketstack/test/adapter.test.ts b/marketstack/test/adapter.test.ts index 430a7d3fc2..05926ecc50 100644 --- a/marketstack/test/adapter.test.ts +++ b/marketstack/test/adapter.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ diff --git a/messari/.eslintrc.js b/messari/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/messari/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/messari/README.md b/messari/README.md index 1aad214bb5..0c56110fc8 100644 --- a/messari/README.md +++ b/messari/README.md @@ -1,17 +1,32 @@ # Chainlink External Adapter for Messari -## Assets API +### Input Parameters -### Endpoint +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [assets](#Assets-Endpoint) | assets | -https://data.messari.io/api/v1/assets/ethereum/metrics +--- + +## Assets Endpoint ### Input Params -- `market`, `to`, or `quote`: The symbol of the currency to query -- `endpoint`: Optional endpoint param (defaults to "dominance", one of "dominance") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------------------------------------------------------------------------------------------------------: | :-----: | :-------------------------------------: | +| ✅ | `market`, `to`, `quote` | The symbol of the currency to query | | | +| | `field` | The object path to access the value that will be returned as the result. Deeply nested values can be accessed with a `.` delimiter. | | `marketcap.marketcap_dominance_percent` | + +### Sample Input + +```json +{ + "id": "1", + "data": { "coin": "BTC", "market": "ARS" } +} +``` -### Output +### Sample Output ```json { diff --git a/messari/adapter.js b/messari/adapter.js deleted file mode 100644 index 8d5d5aede7..0000000000 --- a/messari/adapter.js +++ /dev/null @@ -1,58 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const ENDPOINT_DOMINANCE = 'dominance' - -const DEFAULT_ENDPOINT = ENDPOINT_DOMINANCE - -const customError = (data) => { - if (data.Response === 'Error') return true - return false -} - -const assetsParams = { - base: ['market', 'to', 'quote'], -} - -const assets = (jobRunID, input, callback) => { - const validator = new Validator(input, assetsParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const base = validator.validated.data.base.toLowerCase() - const url = `https://data.messari.io/api/v1/assets/${base}/metrics` - - const config = { - url: url, - } - - Requester.request(config, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - 'marketcap', - 'marketcap_dominance_percent', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -const customParams = { - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - - switch (endpoint.toLowerCase()) { - case ENDPOINT_DOMINANCE: - return assets(jobRunID, input, callback) - default: - callback(500, Requester.errored(jobRunID, 'invalid endpoint provided')) - } -} - -module.exports.execute = execute diff --git a/messari/index.js b/messari/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/messari/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/messari/package.json b/messari/package.json index d7b9275fea..c2efe3ef16 100644 --- a/messari/package.json +++ b/messari/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/messari-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Messari adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/messari/src/adapter.ts b/messari/src/adapter.ts new file mode 100644 index 0000000000..069f56f3c9 --- /dev/null +++ b/messari/src/adapter.ts @@ -0,0 +1,36 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { assets } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case 'dominance': + case assets.NAME: { + return await assets.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/messari/src/config.ts b/messari/src/config.ts new file mode 100644 index 0000000000..e2ce6f36f4 --- /dev/null +++ b/messari/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'assets' +export const DEFAULT_BASE_URL = 'https://data.messari.io/api/v1/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/messari/src/endpoint/assets.ts b/messari/src/endpoint/assets.ts new file mode 100644 index 0000000000..4e863ccfb1 --- /dev/null +++ b/messari/src/endpoint/assets.ts @@ -0,0 +1,38 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'assets' + +const customError = (data: any) => { + if (data.Response === 'Error') return true + return false +} + +const customParams = { + base: ['base', 'market', 'to', 'quote'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const base = validator.validated.data.base.toLowerCase() + const field = validator.validated.data.field || 'marketcap.marketcap_dominance_percent' + const url = `assets/${base}/metrics` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['data', ...field.split('.')]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/messari/src/endpoint/index.ts b/messari/src/endpoint/index.ts new file mode 100644 index 0000000000..e48abd8585 --- /dev/null +++ b/messari/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as assets from './assets' diff --git a/messari/src/index.ts b/messari/src/index.ts new file mode 100644 index 0000000000..50a456b75c --- /dev/null +++ b/messari/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'MESSARI' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/messari/test/adapter_test.js b/messari/test/adapter_test.js deleted file mode 100644 index af2a20997e..0000000000 --- a/messari/test/adapter_test.js +++ /dev/null @@ -1,73 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { base: 'BTC' } }, - }, - { - name: 'market', - testData: { id: jobID, data: { market: 'BTC' } }, - }, - { - name: 'to', - testData: { id: jobID, data: { to: 'BTC' } }, - }, - { - name: 'quote', - testData: { id: jobID, data: { quote: 'ETH' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown market', - testData: { id: jobID, data: { market: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/messari/test/assets.test.ts b/messari/test/assets.test.ts new file mode 100644 index 0000000000..0eef9ab0f7 --- /dev/null +++ b/messari/test/assets.test.ts @@ -0,0 +1,78 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { base: 'BTC' } }, + }, + { + name: 'market', + testData: { id: jobID, data: { market: 'BTC' } }, + }, + { + name: 'to', + testData: { id: jobID, data: { to: 'BTC' } }, + }, + { + name: 'quote', + testData: { id: jobID, data: { quote: 'ETH' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown market', + testData: { id: jobID, data: { market: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/messari/tsconfig.json b/messari/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/messari/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/metalsapi/README.md b/metalsapi/README.md index d06d29f3d0..07dec11c9b 100644 --- a/metalsapi/README.md +++ b/metalsapi/README.md @@ -1,10 +1,31 @@ # Chainlink External Adapter for [MetalsAPI](https://metals-api.com/documentation#convertcurrency) -## Input Params +### Environment Variables -- `base` or `from`: The symbol of the currency to query -- `quote` or `to`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------: | :-----: | :---------: | +| ✅ | API_KEY | | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | | | +| 🟡 | `amount` | The amount fo the `base` currency | | 1 | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ## Output diff --git a/metalsapi/package.json b/metalsapi/package.json index 2c11e01b36..16e130b100 100644 --- a/metalsapi/package.json +++ b/metalsapi/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/metalsapi-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink metalsapi adapter.", "keywords": [ "Chainlink", diff --git a/metalsapi/src/adapter.ts b/metalsapi/src/adapter.ts index 56d0fecd15..f7da290b7a 100644 --- a/metalsapi/src/adapter.ts +++ b/metalsapi/src/adapter.ts @@ -17,6 +17,7 @@ export const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint) { + case 'price': case convert.Name: { const data = await convert.execute(config, request) return Requester.success(jobRunID, { diff --git a/metalsapi/src/config.ts b/metalsapi/src/config.ts index 6c24c8b82f..6681cc4b2e 100644 --- a/metalsapi/src/config.ts +++ b/metalsapi/src/config.ts @@ -1,12 +1,13 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' -export const DEFAULT_API_ENDPOINT = 'https://metals-api.com/api/' +export const NAME = 'METALSAPI' +export const DEFAULT_API_ENDPOINT = 'https://metals-api.com/api/' export const DEFAULT_ENDPOINT = 'convert' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.headers['x-api-key'] = config.apiKey config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT return config diff --git a/metalsapi/src/endpoint/convert.ts b/metalsapi/src/endpoint/convert.ts index 60ee1b7df8..3c2f4518a6 100644 --- a/metalsapi/src/endpoint/convert.ts +++ b/metalsapi/src/endpoint/convert.ts @@ -1,6 +1,6 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' -import { util } from '@chainlink/ea-bootstrap' +import { NAME as AdapterName } from '../config' export const Name = 'convert' @@ -9,21 +9,25 @@ const customError = (data: any) => data.Response === 'Error' const customParams = { base: ['base', 'from', 'coin'], quote: ['quote', 'to', 'market'], + amount: false, } export const execute = async (config: Config, request: AdapterRequest) => { const validator = new Validator(request, customParams) if (validator.error) throw validator.error - const from = validator.validated.data.base.toUpperCase() + const from = validator.overrideSymbol(AdapterName).toUpperCase() const to = validator.validated.data.quote.toUpperCase() + const amount = validator.validated.data.amount || 1 const url = `convert` + console.log('FROM', from) + const params = { access_key: config.apiKey, from, to, - amount: 1, + amount, } const reqConfig = { ...config.api, params, url } diff --git a/metalsapi/src/index.ts b/metalsapi/src/index.ts index 81d7ed6315..21ea85958f 100644 --- a/metalsapi/src/index.ts +++ b/metalsapi/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'METALSAPI' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/metalsapi/test/adapter.test.ts b/metalsapi/test/adapter.test.ts index 365dc2e578..2794c897bd 100644 --- a/metalsapi/test/adapter.test.ts +++ b/metalsapi/test/adapter.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ @@ -22,24 +23,6 @@ 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/nikkei/README.md b/nikkei/README.md index bd6bee5d6e..299f170686 100644 --- a/nikkei/README.md +++ b/nikkei/README.md @@ -1,11 +1,22 @@ # Chainlink External Adapter for Nikkei -## Input Params +### Input Parameters -- `base`, `from`, or `asset`: The symbol of the currency to query -- `endpoint`: Optional endpoint param +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | -## Output +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----------------------: | :------------------------------: | :---------------------------------------------------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the index to query | `N255`, [list](https://indexes.nikkei.co.jp/en/nkave/index) | | + +### Output ```json { diff --git a/nikkei/package.json b/nikkei/package.json index 7ba890e0be..88960773d9 100644 --- a/nikkei/package.json +++ b/nikkei/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/nikkei-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink nikkei adapter.", "keywords": [ "Chainlink", diff --git a/nikkei/src/adapter.ts b/nikkei/src/adapter.ts index a338af3e49..7303bc4c80 100644 --- a/nikkei/src/adapter.ts +++ b/nikkei/src/adapter.ts @@ -1,7 +1,7 @@ import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { realData } from './endpoint' +import { price } from './endpoint' const inputParams = { endpoint: false, @@ -17,8 +17,8 @@ export const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint) { - case realData.Name: { - return await realData.execute(config, request) + case price.NAME: { + return await price.execute(config, request) } default: { throw new AdapterError({ diff --git a/nikkei/src/config.ts b/nikkei/src/config.ts index 04ece8fac5..3c2d1f349b 100644 --- a/nikkei/src/config.ts +++ b/nikkei/src/config.ts @@ -1,7 +1,7 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' -export const DEFAULT_ENDPOINT = 'realData' +export const DEFAULT_ENDPOINT = 'price' export const DEFAULT_API_ENDPOINT = 'https://indexes.nikkei.co.jp/en/nkave/' diff --git a/nikkei/src/endpoint/index.ts b/nikkei/src/endpoint/index.ts index a0eaf490dc..7fca076fed 100644 --- a/nikkei/src/endpoint/index.ts +++ b/nikkei/src/endpoint/index.ts @@ -1 +1 @@ -export * as realData from './realData' +export * as price from './price' diff --git a/nikkei/src/endpoint/realData.ts b/nikkei/src/endpoint/price.ts similarity index 92% rename from nikkei/src/endpoint/realData.ts rename to nikkei/src/endpoint/price.ts index ba9820057c..c9b5decec0 100644 --- a/nikkei/src/endpoint/realData.ts +++ b/nikkei/src/endpoint/price.ts @@ -1,8 +1,7 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' -import { util } from '@chainlink/ea-bootstrap' -export const Name = 'realData' +export const NAME = 'price' const customError = (data: any) => data.Response === 'Error' diff --git a/nikkei/src/index.ts b/nikkei/src/index.ts index 7e55c284e7..36ce77feb5 100644 --- a/nikkei/src/index.ts +++ b/nikkei/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'NIKKEI' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/nomics/README.md b/nomics/README.md index 5f61932e57..334b1cf608 100644 --- a/nomics/README.md +++ b/nomics/README.md @@ -1,10 +1,30 @@ # Chainlink External Adapter for Nomics -## Input Params +### Environment Variables -- `base`, `from`, `coin`, or `ids`: The symbol of the currency to query -- `quote`, `to`, `market`, or `convert`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://p.nomics.com/pricing#free-plan) | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :---------------------------------------------------------------------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint), [globalmarketcap](#Global-Market-Capitalization-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------------------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin`, `ids` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`,`market`, `convert` | The symbol of the currency to convert to | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | ## Output @@ -76,3 +96,245 @@ "statusCode": 200 } ``` + +## Global Market Capitalization Endpoint + +## Output + +```json +{ + "jobRunID": "2", + "data": { + "num_currencies": "11996", + "num_currencies_active": "4814", + "num_currencies_inactive": "6118", + "num_currencies_dead": "1064", + "num_currencies_new": "735", + "market_cap": "1378511244903", + "transparent_market_cap": "1359550541086", + "1d": { + "market_cap_change": "-45151295457", + "market_cap_change_pct": "-0.0317", + "transparent_market_cap_change": "-42549876648", + "transparent_market_cap_change_pct": "-0.0303", + "volume": "258039036580.82", + "volume_change": "13784474286.90", + "volume_change_pct": "0.0564", + "transparent_volume": "55640357579.68", + "transparent_volume_change": "1918004148.04", + "transparent_volume_change_pct": "0.0357", + "volume_transparency": [ + { + "grade": "?", + "volume": "688460376.53", + "volume_change": "-32099929.51", + "volume_change_pct": "-0.0445" + }, + { + "grade": "A", + "volume": "55749207985.85", + "volume_change": "2013327125.70", + "volume_change_pct": "0.0375" + }, + { + "grade": "B", + "volume": "1213341177.30", + "volume_change": "114222543.51", + "volume_change_pct": "0.1039" + }, + { + "grade": "C", + "volume": "141344893303.36", + "volume_change": "13719226841.73", + "volume_change_pct": "0.1075" + }, + { + "grade": "D", + "volume": "59043133737.78", + "volume_change": "-2030202294.53", + "volume_change_pct": "-0.0332" + } + ] + }, + "7d": { + "market_cap_change": "224216447834", + "market_cap_change_pct": "0.1942", + "transparent_market_cap_change": "218401928553", + "transparent_market_cap_change_pct": "0.1914", + "volume": "1558702928385.47", + "volume_change": "359446794644.48", + "volume_change_pct": "0.2997", + "transparent_volume": "338670639090.49", + "transparent_volume_change": "94878735486.19", + "transparent_volume_change_pct": "0.3892", + "volume_transparency": [ + { + "grade": "?", + "volume": "4859417580.44", + "volume_change": "1136169291.31", + "volume_change_pct": "0.3052" + }, + { + "grade": "A", + "volume": "340755588081.80", + "volume_change": "96696599797.14", + "volume_change_pct": "0.3962" + }, + { + "grade": "B", + "volume": "7316235620.53", + "volume_change": "2549523100.38", + "volume_change_pct": "0.5349" + }, + { + "grade": "C", + "volume": "871093380740.69", + "volume_change": "160882329806.94", + "volume_change_pct": "0.2265" + }, + { + "grade": "D", + "volume": "334678306362.01", + "volume_change": "98182172648.71", + "volume_change_pct": "0.4152" + } + ] + }, + "30d": { + "market_cap_change": "416356723318", + "market_cap_change_pct": "0.4327", + "transparent_market_cap_change": "397674883386", + "transparent_market_cap_change_pct": "0.4134", + "volume": "5653908564464.01", + "volume_change": "1601131203412.30", + "volume_change_pct": "0.3951", + "transparent_volume": "1067111587952.80", + "transparent_volume_change": "404878968751.51", + "transparent_volume_change_pct": "0.6114", + "volume_transparency": [ + { + "grade": "?", + "volume": "16882677580.65", + "volume_change": "2205558964.40", + "volume_change_pct": "0.1503" + }, + { + "grade": "A", + "volume": "1070763889348.68", + "volume_change": "411903669832.29", + "volume_change_pct": "0.6252" + }, + { + "grade": "B", + "volume": "19913779679.12", + "volume_change": "9738875450.37", + "volume_change_pct": "0.9571" + }, + { + "grade": "C", + "volume": "3322269755276.39", + "volume_change": "951046406613.22", + "volume_change_pct": "0.4011" + }, + { + "grade": "D", + "volume": "1224078462579.18", + "volume_change": "226236692552.01", + "volume_change_pct": "0.2267" + } + ] + }, + "365d": { + "market_cap_change": "1081116974090", + "market_cap_change_pct": "3.6353", + "transparent_market_cap_change": "1068220627950", + "transparent_market_cap_change_pct": "3.6667", + "volume": "29883716656515.38", + "volume_change": "18282436180631.36", + "volume_change_pct": "1.5759", + "transparent_volume": "3713836896422.77", + "transparent_volume_change": "2278313444188.28", + "transparent_volume_change_pct": "1.5871", + "volume_transparency": [ + { + "grade": "?", + "volume": "328566253797.24", + "volume_change": "-281695023880.62", + "volume_change_pct": "-0.4616" + }, + { + "grade": "A", + "volume": "3718511931979.16", + "volume_change": "2275840182127.67", + "volume_change_pct": "1.5775" + }, + { + "grade": "B", + "volume": "49320831220.13", + "volume_change": "35847296193.89", + "volume_change_pct": "2.6606" + }, + { + "grade": "C", + "volume": "18797899813953.55", + "volume_change": "10871748480102.48", + "volume_change_pct": "1.3716" + }, + { + "grade": "D", + "volume": "6989417825565.30", + "volume_change": "5380695246087.94", + "volume_change_pct": "3.3447" + } + ] + }, + "ytd": { + "market_cap_change": "578426575593", + "market_cap_change_pct": "0.7230", + "transparent_market_cap_change": "578938892155", + "transparent_market_cap_change_pct": "0.7416", + "volume": "7656109632899.66", + "volume_change": "3899765551830.88", + "volume_change_pct": "1.0382", + "transparent_volume": "1412720078662.37", + "transparent_volume_change": "838480157007.22", + "transparent_volume_change_pct": "1.4602", + "volume_transparency": [ + { + "grade": "?", + "volume": "23712345285.45", + "volume_change": "7995749128.09", + "volume_change_pct": "0.5087" + }, + { + "grade": "A", + "volume": "1412955971658.93", + "volume_change": "838395923576.66", + "volume_change_pct": "1.4592" + }, + { + "grade": "B", + "volume": "25803043851.13", + "volume_change": "18258809154.80", + "volume_change_pct": "2.4202" + }, + { + "grade": "C", + "volume": "4430711040925.43", + "volume_change": "2026001737962.93", + "volume_change_pct": "0.8425" + }, + { + "grade": "D", + "volume": "1762927231178.73", + "volume_change": "1009113332008.41", + "volume_change_pct": "1.3387" + } + ] + }, + "result": 1378511244903 + }, + "result": 1378511244903, + "statusCode": 200 +} +``` diff --git a/nomics/package.json b/nomics/package.json index 773f448674..218a73d728 100644 --- a/nomics/package.json +++ b/nomics/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/nomics-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink nomics adapter.", "keywords": [ "Chainlink", diff --git a/nomics/src/adapter.ts b/nomics/src/adapter.ts index 2cfc50ba5c..afb42af000 100644 --- a/nomics/src/adapter.ts +++ b/nomics/src/adapter.ts @@ -16,11 +16,11 @@ export const execute: ExecuteWithConfig = async (request, config) => { const jobRunID = validator.validated.id const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - switch (endpoint) { - case globalmarketcap.Name: { + switch (endpoint.toLowerCase()) { + case globalmarketcap.NAME: { return await globalmarketcap.execute(config, request) } - case price.Name: { + case price.NAME: { return await price.execute(config, request) } default: { diff --git a/nomics/src/config.ts b/nomics/src/config.ts index 9198093a0a..3d511803dd 100644 --- a/nomics/src/config.ts +++ b/nomics/src/config.ts @@ -1,11 +1,13 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' +export const NAME = 'NOMICS' + export const DEFAULT_ENDPOINT = 'price' export const DEFAULT_API_ENDPOINT = 'https://api.nomics.com/v1' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT return config } diff --git a/nomics/src/endpoint/globalmarketcap.ts b/nomics/src/endpoint/globalmarketcap.ts index e93ca85dac..9789192f81 100644 --- a/nomics/src/endpoint/globalmarketcap.ts +++ b/nomics/src/endpoint/globalmarketcap.ts @@ -1,7 +1,7 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' -export const Name = 'globalmarketcap' +export const NAME = 'globalmarketcap' const customError = (data: any) => data.Response === 'Error' diff --git a/nomics/src/endpoint/price.ts b/nomics/src/endpoint/price.ts index 03f52cdc23..a24eef7c55 100644 --- a/nomics/src/endpoint/price.ts +++ b/nomics/src/endpoint/price.ts @@ -1,7 +1,8 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { AdapterRequest, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' -export const Name = 'price' +export const NAME = 'price' const customError = (data: any) => data.Response === 'Error' @@ -10,23 +11,17 @@ const customParams = { quote: ['quote', 'to', 'market', 'convert'], } -const convertId: Record = { - FNX: 'FNX2', -} - export const execute = async (config: Config, request: AdapterRequest) => { const validator = new Validator(request, customParams) if (validator.error) throw validator.error - let ids = validator.validated.data.base.toUpperCase() + const symbol = validator.overrideSymbol(AdapterName) const convert = validator.validated.data.quote.toUpperCase() const jobRunID = validator.validated.id const url = `/currencies/ticker` - // Correct common tickers that are misidentified - ids = convertId[ids] || ids const params = { - ids, + ids: symbol.toUpperCase(), convert, key: config.apiKey, } diff --git a/nomics/src/index.ts b/nomics/src/index.ts index 0c01fcf2f0..21ea85958f 100644 --- a/nomics/src/index.ts +++ b/nomics/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'NOMICS' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/nomics/test/adapter.test.ts b/nomics/test/adapter.test.ts index 1299ac7c44..b048170403 100644 --- a/nomics/test/adapter.test.ts +++ b/nomics/test/adapter.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ diff --git a/oilpriceapi/package.json b/oilpriceapi/package.json index 7cbb87586a..0747f3e666 100644 --- a/oilpriceapi/package.json +++ b/oilpriceapi/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/oilpriceapi-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink OilpriceAPI adapter.", "keywords": [ "Chainlink", diff --git a/oilpriceapi/src/index.ts b/oilpriceapi/src/index.ts index 2c794465c0..017169abdd 100644 --- a/oilpriceapi/src/index.ts +++ b/oilpriceapi/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeConfig, makeExecute } from './adapter' const NAME = 'OilpriceAPI' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/onchain/package.json b/onchain/package.json index 7b73b27b8b..160641ffbf 100644 --- a/onchain/package.json +++ b/onchain/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/onchain-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink onchain adapter.", "keywords": [ "Chainlink", diff --git a/onchain/src/index.ts b/onchain/src/index.ts index fbd04e75c3..8fba19ac11 100644 --- a/onchain/src/index.ts +++ b/onchain/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'ONCHAIN' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/openexchangerates/README.md b/openexchangerates/README.md index 1b6b1b2288..b81d397714 100644 --- a/openexchangerates/README.md +++ b/openexchangerates/README.md @@ -1,12 +1,31 @@ # Chainlink Open Exchange Rates External Adapter -## Input Params +### Environment Variables -- `base` or `from`: The currency symbol to convert from -- `quote` or `to`: The currency symbol to convert to -- `endpoint`: The endpoint to use (default: latest.json) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :--------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](hhttps://openexchangerates.org/signup) | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | | | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | +### Output ```json { diff --git a/openexchangerates/package.json b/openexchangerates/package.json index e05698ee8c..0146acaf90 100644 --- a/openexchangerates/package.json +++ b/openexchangerates/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/openexchangerates-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink openexchangerates adapter.", "keywords": [ "Chainlink", diff --git a/openexchangerates/src/adapter.ts b/openexchangerates/src/adapter.ts index 3bfa923c4e..e7c19bba45 100644 --- a/openexchangerates/src/adapter.ts +++ b/openexchangerates/src/adapter.ts @@ -1,10 +1,9 @@ -import { Requester, Validator } from '@chainlink/external-adapter' +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { price } from './endpoint' const inputParams = { - base: ['base', 'from'], - quote: ['quote', 'to'], endpoint: false, } @@ -16,26 +15,19 @@ export const execute: ExecuteWithConfig = async (request, config) => { const jobRunID = validator.validated.id const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT - const base = validator.validated.data.base - const to = validator.validated.data.quote - const params = { - base, - app_id: config.apiKey, + switch (endpoint) { + case price.NAME: { + return await price.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } } - - const reqConfig = { - ...config.api, - params, - url: endpoint, - } - const response = await Requester.request(reqConfig) - const result = Requester.validateResultNumber(response.data, ['rates', to]) - return Requester.success(jobRunID, { - data: { ...response.data, result }, - result, - status: 200, - }) } export const makeExecute: ExecuteFactory = (config) => { diff --git a/openexchangerates/src/config.ts b/openexchangerates/src/config.ts index b59d53ca65..1c7b8f19da 100644 --- a/openexchangerates/src/config.ts +++ b/openexchangerates/src/config.ts @@ -1,11 +1,13 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' -export const DEFAULT_ENDPOINT = 'latest.json' +export const NAME = 'OPEN-EXCHANGE-RATES' + +export const DEFAULT_ENDPOINT = 'price' export const DEFAULT_API_ENDPOINT = 'https://openexchangerates.org/api/' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT return config } diff --git a/openexchangerates/src/endpoint/index.ts b/openexchangerates/src/endpoint/index.ts new file mode 100644 index 0000000000..7fca076fed --- /dev/null +++ b/openexchangerates/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as price from './price' diff --git a/openexchangerates/src/endpoint/price.ts b/openexchangerates/src/endpoint/price.ts new file mode 100644 index 0000000000..951af69bb7 --- /dev/null +++ b/openexchangerates/src/endpoint/price.ts @@ -0,0 +1,40 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig } from '@chainlink/types' +import { NAME as AdapterName } from '../config' + +export const NAME = 'price' + +const inputParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const url = 'latest.json' + const base = validator.overrideSymbol(AdapterName) + const to = validator.validated.data.quote + + const params = { + base, + app_id: config.apiKey, + } + + const options = { + ...config.api, + params, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['rates', to]) + + return Requester.success(jobRunID, { + data: { ...response.data, result }, + result, + status: 200, + }) +} diff --git a/openexchangerates/src/index.ts b/openexchangerates/src/index.ts index 3f00035c4f..21ea85958f 100644 --- a/openexchangerates/src/index.ts +++ b/openexchangerates/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'OPEN-EXCHANGE-RATES' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/openexchangerates/test/adapter.test.ts b/openexchangerates/test/adapter.test.ts index de82a6a147..fbee51b85a 100644 --- a/openexchangerates/test/adapter.test.ts +++ b/openexchangerates/test/adapter.test.ts @@ -7,12 +7,13 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ { name: 'id not supplied', - testData: { data: { base: 'GBP', quote: 'USD' } }, + testData: { data: { base: 'BTC', quote: 'USD' } }, }, { name: 'base/quote', @@ -20,7 +21,7 @@ describe('execute', () => { }, { name: 'from/to', - testData: { id: jobID, data: { from: 'GBP', to: 'USD' } }, + testData: { id: jobID, data: { from: 'ETH', to: 'USD' } }, }, ] diff --git a/orchid-bandwidth/.eslintrc.js b/orchid-bandwidth/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/orchid-bandwidth/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/orchid-bandwidth/README.md b/orchid-bandwidth/README.md index d7722c97cc..12ff6f8f0f 100644 --- a/orchid-bandwidth/README.md +++ b/orchid-bandwidth/README.md @@ -2,17 +2,17 @@ ## Input Params -*None* +_None_ ## Output ```json { - "jobRunID":"1", - "data":{ - "result":0.059012542392806594 - }, - "result":0.059012542392806594, - "statusCode":200 + "jobRunID": "1", + "data": { + "result": 0.059012542392806594 + }, + "result": 0.059012542392806594, + "statusCode": 200 } ``` diff --git a/orchid-bandwidth/adapter.js b/orchid-bandwidth/adapter.js deleted file mode 100644 index fb56d8aa46..0000000000 --- a/orchid-bandwidth/adapter.js +++ /dev/null @@ -1,18 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const execute = (input, callback) => { - const validator = new Validator(input, {}) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - const url = 'https://chainlink.orchid.com/0' - - const config = { url } - - 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/orchid-bandwidth/index.js b/orchid-bandwidth/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/orchid-bandwidth/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/orchid-bandwidth/package.json b/orchid-bandwidth/package.json index ef3a6f963d..6f696248b1 100644 --- a/orchid-bandwidth/package.json +++ b/orchid-bandwidth/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/orchid-bandwidth-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink Orchid bandwidth adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/orchid-bandwidth/src/adapter.ts b/orchid-bandwidth/src/adapter.ts new file mode 100644 index 0000000000..0089d137d0 --- /dev/null +++ b/orchid-bandwidth/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { bandwidth } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case bandwidth.NAME: { + return await bandwidth.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/orchid-bandwidth/src/config.ts b/orchid-bandwidth/src/config.ts new file mode 100644 index 0000000000..5295b03ad0 --- /dev/null +++ b/orchid-bandwidth/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'bandwidth' +export const DEFAULT_BASE_URL = 'https://chainlink.orchid.com/0' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/orchid-bandwidth/src/endpoint/bandwidth.ts b/orchid-bandwidth/src/endpoint/bandwidth.ts new file mode 100644 index 0000000000..f16396ce5c --- /dev/null +++ b/orchid-bandwidth/src/endpoint/bandwidth.ts @@ -0,0 +1,28 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'bandwidth' + +const customParams = { + // No Params +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + + const options = { + ...config.api, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, []) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/orchid-bandwidth/src/endpoint/index.ts b/orchid-bandwidth/src/endpoint/index.ts new file mode 100644 index 0000000000..a6c75aa426 --- /dev/null +++ b/orchid-bandwidth/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as bandwidth from './bandwidth' diff --git a/orchid-bandwidth/src/index.ts b/orchid-bandwidth/src/index.ts new file mode 100644 index 0000000000..7b534974f6 --- /dev/null +++ b/orchid-bandwidth/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'ORCHID_BANDWIDTH' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/orchid-bandwidth/test/adapter_test.js b/orchid-bandwidth/test/adapter_test.js deleted file mode 100644 index 8969e01f4f..0000000000 --- a/orchid-bandwidth/test/adapter_test.js +++ /dev/null @@ -1,25 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { name: 'id not supplied', testData: { data: {} } }, - { name: 'id supplied', testData: { id: jobID, data: {} } }, - ] - - 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() - }) - }) - }) - }) -}) diff --git a/orchid-bandwidth/test/bandwidth.test.ts b/orchid-bandwidth/test/bandwidth.test.ts new file mode 100644 index 0000000000..7894e8ad87 --- /dev/null +++ b/orchid-bandwidth/test/bandwidth.test.ts @@ -0,0 +1,25 @@ +import { assert } from 'chai' +import { assertSuccess } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { name: 'id not supplied', testData: { data: {} } }, + { name: 'id supplied', testData: { id: jobID, data: {} } }, + ] + + 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) + }) + }) + }) +}) diff --git a/orchid-bandwidth/tsconfig.json b/orchid-bandwidth/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/orchid-bandwidth/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/package.json b/package.json index 7b0b199849..4be346c32e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/external-adapters-js", - "version": "0.2.0-rc.1", + "version": "0.2.0", "license": "MIT", "private": true, "workspaces": [ @@ -14,6 +14,7 @@ "covid-tracker", "messari", "coinlore", + "agoric", "trueusd", "cryptoid", "blockchair", @@ -24,7 +25,6 @@ "orchid-bandwidth", "genesis-volatility", "metalsapi", - "bravenewcoin-vwap", "dxfeed", "dxfeed-secondary", "tradingeconomics", @@ -56,20 +56,12 @@ "oilpriceapi", "openexchangerates", "polygon", - "google-finance", "etherchain", "poa-gasprice", "anyblock-gasprice", "anyblock-uniswap-vwap", "ethgasstation", "amberdata-gasprice", - "synth-index/coinapi", - "synth-index/coingecko", - "synth-index/coinmarketcap", - "synth-index/coinpaprika", - "synth-index/bravenewcoin", - "synth-index/cryptocompare", - "synth-index/amberdata", "wbtc-address-set", "renvm-address-set", "blockchain.com", @@ -95,7 +87,14 @@ "dydx-stark", "iex-cloud", "cfbenchmarks", - "harmony" + "conflux", + "harmony", + "tiingo", + "ethwrite", + "geodb", + "therundown", + "tradingeconomics-stream", + "blockstream" ], "scripts": { "lint": "yarn workspaces run lint", @@ -108,17 +107,16 @@ "test:example-start-server": "node ./helpers/server.js" }, "dependencies": { - "@chainlink/ea-bootstrap": "^0.1.6", - "@chainlink/ea-factories": "^0.0.1", - "@chainlink/external-adapter": "^0.2.8", + "@chainlink/ea-bootstrap": "^0.1.8", + "@chainlink/ea-factories": "^0.0.2", + "@chainlink/external-adapter": "^0.2.9", "decimal.js": "^10.2.0", "ethers": "^5.0.26", "express": "^4.17.0", - "synthetix": "^2.29.3", "web3": "^1.2.11" }, "devDependencies": { - "@chainlink/adapter-test-helpers": "0.0.1", + "@chainlink/adapter-test-helpers": "0.0.2", "@tsconfig/node12": "^1.0.7", "eslint": "^7.2.0", "eslint-config-prettier": "^6.11.0", @@ -129,6 +127,7 @@ "eslint-plugin-promise": "^4.2.1", "eslint-plugin-standard": "^4.0.1", "prettier": "^2.0.5", + "ts-node": "^8.10.2", "wsrun": "^5.2.4" }, "resolutions": { diff --git a/paxos/package.json b/paxos/package.json index d8836a7199..6132122ab5 100644 --- a/paxos/package.json +++ b/paxos/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/paxos-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink paxos adapter.", "keywords": [ "Chainlink", diff --git a/paxos/src/index.ts b/paxos/src/index.ts index d0fd89667b..61f916780e 100644 --- a/paxos/src/index.ts +++ b/paxos/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'PAXOS' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/poa-gasprice/README.md b/poa-gasprice/README.md index 95b55fb344..6b4e55ec67 100644 --- a/poa-gasprice/README.md +++ b/poa-gasprice/README.md @@ -1,12 +1,10 @@ # Chainlink External Adapter for POA Network gas price -## Input Params +### Input Params -- `speed`: The speed for gas price to get. Available choices: - - `slow` - - `standard` (default) - - `fast` - - `instant` +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :---------------------------------: | :--------------------------------: | :---------: | +| 🟡 | `speed` | The symbol of the currency to query | `slow`,`standard`,`fast`,`instant` | `standard` | ## Output Format diff --git a/poa-gasprice/package.json b/poa-gasprice/package.json index 4845d66d81..41a5f344e6 100644 --- a/poa-gasprice/package.json +++ b/poa-gasprice/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/poa-gasprice-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink poa-gasprice adapter.", "keywords": [ "Chainlink", diff --git a/poa-gasprice/src/index.ts b/poa-gasprice/src/index.ts index b0fcbd06b3..44d7028523 100644 --- a/poa-gasprice/src/index.ts +++ b/poa-gasprice/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'POA-GAS-PRICE' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/polygon/README.md b/polygon/README.md index 2512a1bd2e..edc6b7c16c 100644 --- a/polygon/README.md +++ b/polygon/README.md @@ -2,13 +2,37 @@ This adapter is for [Polygon.io](https://polygon.io/) and supports the conversion endpoint. -## Input params +### Environment Variables -- `base` or `from`: The asset to query -- `quote` or `to`: The currency to convert to -- `endpoint`: The endpoint to query (default: conversion) +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://polygon.io/dashboard/signup) | | | -## Output +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------: | :---------: | +| | endpoint | The endpoint to use | [price](#Price-Endpoint) | price | + +--- + +## Price Endpoint + +Get FOREX price conversions + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :------------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `coin` | The symbol of the currency to query | `BTC`, `ETH`, `USD` | | +| ✅ | `quote`, `to`, or `market` | The symbol of the currency to convert to | `BTC`, `ETH`, `USD` | | +| 🟡 | `amount` | The amount of the `base` to convert | | 1 | +| 🟡 | `precision` | The number of significant figures to include | | 4 | +| 🟡 | `overrides` | If base provided is found in overrides, that will be used | [Format](../external-adapter/src/overrides/presetSymbols.json)| | + +### Output ```json { diff --git a/polygon/package.json b/polygon/package.json index dd6c5a9f4f..3fbca4c9fa 100644 --- a/polygon/package.json +++ b/polygon/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/polygon-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink polygon adapter.", "keywords": [ "Chainlink", diff --git a/polygon/src/adapter.ts b/polygon/src/adapter.ts index 60e7dc62db..e7c19bba45 100644 --- a/polygon/src/adapter.ts +++ b/polygon/src/adapter.ts @@ -1,7 +1,7 @@ import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' import { makeConfig, DEFAULT_ENDPOINT } from './config' -import { conversion } from './endpoint' +import { price } from './endpoint' const inputParams = { endpoint: false, @@ -17,8 +17,8 @@ export const execute: ExecuteWithConfig = async (request, config) => { const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT switch (endpoint) { - case conversion.NAME: { - return await conversion.execute(request, config) + case price.NAME: { + return await price.execute(request, config) } default: { throw new AdapterError({ diff --git a/polygon/src/config.ts b/polygon/src/config.ts index f6e5a0fba9..88ea2ad282 100644 --- a/polygon/src/config.ts +++ b/polygon/src/config.ts @@ -1,11 +1,14 @@ import { Requester } from '@chainlink/external-adapter' import { Config } from '@chainlink/types' -export const DEFAULT_ENDPOINT = 'conversion' +export const NAME = 'POLYGON' + +export const DEFAULT_ENDPOINT = 'price' export const DEFAULT_API_ENDPOINT = 'https://api.polygon.io/v1/' export const makeConfig = (prefix = ''): Config => { - const config = Requester.getDefaultConfig(prefix) + const config = Requester.getDefaultConfig(prefix, true) config.api.baseURL = config.api.baseURL || DEFAULT_API_ENDPOINT + config.api.params = { apikey: config.apiKey } return config } diff --git a/polygon/src/endpoint/index.ts b/polygon/src/endpoint/index.ts index 17e1e8ffbd..7fca076fed 100644 --- a/polygon/src/endpoint/index.ts +++ b/polygon/src/endpoint/index.ts @@ -1 +1 @@ -export * as conversion from './conversion' +export * as price from './price' diff --git a/polygon/src/endpoint/conversion.ts b/polygon/src/endpoint/price.ts similarity index 74% rename from polygon/src/endpoint/conversion.ts rename to polygon/src/endpoint/price.ts index 332d88616d..968fb539bd 100644 --- a/polygon/src/endpoint/conversion.ts +++ b/polygon/src/endpoint/price.ts @@ -1,7 +1,8 @@ import { Requester, Validator } from '@chainlink/external-adapter' import { ExecuteWithConfig, Config } from '@chainlink/types' +import { NAME as AdapterName } from '../config' -export const NAME = 'conversion' +export const NAME = 'price' const customError = (data: any) => { return data.status === 'ERROR' @@ -19,23 +20,21 @@ export const execute: ExecuteWithConfig = async (request, config) => { if (validator.error) throw validator.error const jobRunID = validator.validated.id - const from = validator.validated.data.base.toUpperCase() + const from = validator.overrideSymbol(AdapterName).toUpperCase() const to = validator.validated.data.quote.toUpperCase() - const endpoint = `conversion` const amount = validator.validated.data.amount || 1 const precision = validator.validated.data.precision || 4 - const apikey = config.apiKey - const url = `${endpoint}/${from}/${to}` + const url = `conversion/${from}/${to}` const params = { + ...config.api.params, amount, precision, - apikey, } - const reqConfig = { ...config.api, params, url } + const options = { ...config.api, params, url } - const response = await Requester.request(reqConfig) + const response = await Requester.request(options, customError) const result = Requester.validateResultNumber(response.data, ['converted']) return Requester.success(jobRunID, { data: { ...response.data, result }, diff --git a/polygon/src/index.ts b/polygon/src/index.ts index e8113ddd20..21ea85958f 100644 --- a/polygon/src/index.ts +++ b/polygon/src/index.ts @@ -1,7 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' -import { makeConfig } from './config' +import { makeConfig, NAME } from './config' -const NAME = 'POLYGON' - -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/polygon/test/adapter.test.ts b/polygon/test/adapter.test.ts index de82a6a147..acd8a65058 100644 --- a/polygon/test/adapter.test.ts +++ b/polygon/test/adapter.test.ts @@ -7,6 +7,7 @@ import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' const execute = makeExecute() + process.env.API_KEY = process.env.API_KEY ?? 'test_api_key' context('successful calls @integration', () => { const requests = [ diff --git a/reduce/package.json b/reduce/package.json index b3412ebc26..f567716639 100644 --- a/reduce/package.json +++ b/reduce/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/reduce-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter to reduce input, resulting in single output value.", "keywords": [ "Chainlink", diff --git a/reduce/src/index.ts b/reduce/src/index.ts index ce46c76b87..58f703e363 100644 --- a/reduce/src/index.ts +++ b/reduce/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'REDUCE' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/renvm-address-set/package.json b/renvm-address-set/package.json index 876d550ab3..ccb31a4a44 100644 --- a/renvm-address-set/package.json +++ b/renvm-address-set/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/renvm-address-set-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter to query RenVM address set.", "keywords": [ "Chainlink", diff --git a/renvm-address-set/src/index.ts b/renvm-address-set/src/index.ts index bab9d78838..0ba8081694 100644 --- a/renvm-address-set/src/index.ts +++ b/renvm-address-set/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'RENVM' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/satoshitango/.eslintrc.js b/satoshitango/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/satoshitango/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/satoshitango/README.md b/satoshitango/README.md index 791d4c87d9..2dc5e534f3 100644 --- a/satoshitango/README.md +++ b/satoshitango/README.md @@ -1,88 +1,109 @@ # Chainlink External Adapter for SatoshiTango -## Input Params +### Input Parameters -- `base`, `from`, or `coin`: The symbol of the currency to query -- `quote`, `to`, or `market`: The symbol of the currency to convert to -- `endpoint`: Optional endpoint param (default: "ticker") +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------------: | :---------: | +| | endpoint | The endpoint to use | [ticker](#Ticker-Endpoint) | ticker | -## Output +--- + +## Ticker Endpoint + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :----------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `coin` | The symbol of the currency to query | | | +| ✅ | `quote`, `to`, `market` | The symbol of the currency to convert to | +| | `field` | The object path to access the value that will be returned as the result. | | `bid` | + +### Sample Input ```json { - "jobRunID":"1", - "data":{ - "data":{ - "ticker":{ - "BTC":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":1480212.48, - "ask":1560168.95, - "high":0, - "low":0, - "volume":0 - }, - "ETH":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":48425.72, - "ask":51062.97, - "high":0, - "low":0, - "volume":0 - }, - "LTC":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":6397.68, - "ask":6747.77, - "high":0, - "low":0, - "volume":0 - }, - "XRP":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":35.13, - "ask":37.07, - "high":0, - "low":0, - "volume":0 - }, - "BCH":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":30381.7, - "ask":32059.34, - "high":0, - "low":0, - "volume":0 - }, - "DAI":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":139, - "ask":147.68, - "high":0, - "low":0, - "volume":0 - }, - "USDC":{ - "date":"2020-10-06 10:46:03", - "timestamp":1601981163, - "bid":138.32, - "ask":145.55, - "high":0, - "low":0, - "volume":0 - } - }, - "code":"success" + "id": "1", + "data": { "base": "BTC", "quote": "ARS" } +} +``` + +### Sample Output + +```json +{ + "jobRunID": "1", + "data": { + "data": { + "ticker": { + "BTC": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 1480212.48, + "ask": 1560168.95, + "high": 0, + "low": 0, + "volume": 0 + }, + "ETH": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 48425.72, + "ask": 51062.97, + "high": 0, + "low": 0, + "volume": 0 + }, + "LTC": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 6397.68, + "ask": 6747.77, + "high": 0, + "low": 0, + "volume": 0 + }, + "XRP": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 35.13, + "ask": 37.07, + "high": 0, + "low": 0, + "volume": 0 + }, + "BCH": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 30381.7, + "ask": 32059.34, + "high": 0, + "low": 0, + "volume": 0 + }, + "DAI": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 139, + "ask": 147.68, + "high": 0, + "low": 0, + "volume": 0 }, - "result":1480212.48 + "USDC": { + "date": "2020-10-06 10:46:03", + "timestamp": 1601981163, + "bid": 138.32, + "ask": 145.55, + "high": 0, + "low": 0, + "volume": 0 + } + }, + "code": "success" }, - "result":1480212.48, - "statusCode":200 + "result": 1480212.48 + }, + "result": 1480212.48, + "statusCode": 200 } ``` diff --git a/satoshitango/adapter.js b/satoshitango/adapter.js deleted file mode 100644 index 0cab6c3ec7..0000000000 --- a/satoshitango/adapter.js +++ /dev/null @@ -1,36 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customParams = { - base: ['base', 'from', 'coin'], - quote: ['quote', 'to', 'market'], - endpoint: false, -} - -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 endpoint = validator.validated.data.endpoint || 'ticker' - const base = validator.validated.data.base.toUpperCase() - const quote = validator.validated.data.quote.toUpperCase() - const url = `https://api.satoshitango.com/v3/${endpoint}/${quote}` - - const config = { - url, - } - - Requester.request(config) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, [ - 'data', - 'ticker', - base, - 'bid', - ]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/satoshitango/index.js b/satoshitango/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/satoshitango/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/satoshitango/package.json b/satoshitango/package.json index 26ecb0e329..604073ac64 100644 --- a/satoshitango/package.json +++ b/satoshitango/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/satoshitango-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink SatoshiTango adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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/satoshitango/src/adapter.ts b/satoshitango/src/adapter.ts new file mode 100644 index 0000000000..6671088a13 --- /dev/null +++ b/satoshitango/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { ticker } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case ticker.NAME: { + return await ticker.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/satoshitango/src/config.ts b/satoshitango/src/config.ts new file mode 100644 index 0000000000..3c2e0bb101 --- /dev/null +++ b/satoshitango/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'ticker' +export const DEFAULT_BASE_URL = 'https://api.satoshitango.com/v3' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/satoshitango/src/endpoint/index.ts b/satoshitango/src/endpoint/index.ts new file mode 100644 index 0000000000..1f6d4dc303 --- /dev/null +++ b/satoshitango/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as ticker from './ticker' diff --git a/satoshitango/src/endpoint/ticker.ts b/satoshitango/src/endpoint/ticker.ts new file mode 100644 index 0000000000..1f61f9c807 --- /dev/null +++ b/satoshitango/src/endpoint/ticker.ts @@ -0,0 +1,35 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'ticker' + +const customParams = { + base: ['base', 'from', 'coin'], + quote: ['quote', 'to', 'market'], + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const base = validator.validated.data.base.toUpperCase() + const quote = validator.validated.data.quote.toUpperCase() + const url = `${NAME}/${quote}` + const field = validator.validated.data.field || 'bid' + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data, ['data', 'ticker', base, field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/satoshitango/src/index.ts b/satoshitango/src/index.ts new file mode 100644 index 0000000000..5f7ca5c428 --- /dev/null +++ b/satoshitango/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'SATOSHITANGO' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/satoshitango/test/adapter_test.js b/satoshitango/test/ticker.test.ts similarity index 52% rename from satoshitango/test/adapter_test.js rename to satoshitango/test/ticker.test.ts index 68602b3e75..f709ac5d24 100644 --- a/satoshitango/test/adapter_test.js +++ b/satoshitango/test/ticker.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 = [ @@ -14,13 +17,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) }) }) }) @@ -34,11 +35,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) + } }) }) }) @@ -50,11 +53,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/satoshitango/tsconfig.json b/satoshitango/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/satoshitango/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/sochain/package.json b/sochain/package.json index f4fc43e8cc..1c413cde8b 100644 --- a/sochain/package.json +++ b/sochain/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/sochain-adapter", - "version": "0.0.1", + "version": "0.0.2", "description": "Chainlink adapter to query BTC address balance from SoChain.", "keywords": [ "Chainlink", diff --git a/sochain/src/index.ts b/sochain/src/index.ts index fd8d452d81..3e69044c56 100644 --- a/sochain/src/index.ts +++ b/sochain/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'SOCHAIN' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/stasis/package.json b/stasis/package.json index 02bc256b5f..b21b73413b 100644 --- a/stasis/package.json +++ b/stasis/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/stasis-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink stasis adapter.", "keywords": [ "Chainlink", diff --git a/stasis/src/index.ts b/stasis/src/index.ts index b584a2b40c..7fa99e54ff 100644 --- a/stasis/src/index.ts +++ b/stasis/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'Stasis' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/synth-index/README.md b/synth-index/README.md deleted file mode 100644 index 99c6e66607..0000000000 --- a/synth-index/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Synthetix Index - -Get Synthetix index data - -## Docker - -To build a Docker container for a specific `$adapter`, use the following example, ran from the root of the repository: - -```bash -make docker-synth-index adapter=coinapi -``` - -The build argument `--build-arg` is required. This will be the directory of the adapter you wish to build. - -The naming convention for synthetix index Docker containers will be `synth-index-$adapter-adapter`. - -Then run it with: - -```bash -docker run -p 8080:8080 -e API_KEY='YOUR_API_KEY' -it synth-index-coinapi-adapter:latest -``` - -## Serverless - -Create the zip: - -```bash -make zip-synth-index adapter=coinapi -``` - -The zip will be created as `./synth-index/$adapter/dist/synth-index-$adapter-adapter.zip`. diff --git a/synth-index/adapter.js b/synth-index/adapter.js deleted file mode 100644 index 6d8c7fbf8c..0000000000 --- a/synth-index/adapter.js +++ /dev/null @@ -1,40 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const adapterExecute = require('./priceAdapter').execute -const adapterCalculateIndex = require('./priceAdapter').calculateIndex -const snx = require('synthetix') - -const DEFAULT_NETWORK = 'mainnet' - -const customParams = { - asset: ['asset', 'from'], - network: false, -} - -const execute = (input, callback) => synthIndexRequest(input, adapterExecute, callback) - -const synthIndexRequest = (input, adapter, callback) => { - const validator = new Validator(input, customParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - const asset = validator.validated.data.asset.toLowerCase() - const network = validator.validated.data.network || DEFAULT_NETWORK - - const synths = snx - .getSynths({ network: network.toLowerCase() }) - .filter(({ index, inverted }) => index && !inverted) - const synth = synths.find((d) => d.name.toLowerCase() === asset) - - adapter(jobRunID, synth) - .then((data) => { - data.result = adapterCalculateIndex(data.index) - const response = { - status: 200, - data, - } - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -exports.execute = execute diff --git a/synth-index/amberdata/.eslintrc.js b/synth-index/amberdata/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/amberdata/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/amberdata/README.md b/synth-index/amberdata/README.md deleted file mode 100644 index c9bc7ae143..0000000000 --- a/synth-index/amberdata/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Amberdata Synthetix Index - -Use of this adapter requires the following environment variable to be present: - -- `API_KEY`: The Amberdata API key diff --git a/synth-index/amberdata/index.js b/synth-index/amberdata/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/amberdata/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/synth-index/amberdata/package.json b/synth-index/amberdata/package.json deleted file mode 100644 index d614d7d870..0000000000 --- a/synth-index/amberdata/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-amberdata-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/amberdata/priceAdapter.js b/synth-index/amberdata/priceAdapter.js deleted file mode 100644 index fb2b66eef1..0000000000 --- a/synth-index/amberdata/priceAdapter.js +++ /dev/null @@ -1,44 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const getPriceData = async (synth) => { - const url = `https://web3api.io/api/v2/market/prices/${synth.toLowerCase()}/latest` - const headers = { - 'X-API-KEY': util.getRandomRequiredEnv('API_KEY'), - } - const config = { - url, - headers, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - value = value.plus( - new Decimal(i.units).times( - new Decimal(i.priceData.payload[`${i.asset.toLowerCase()}_usd`].price), - ), - ) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - await Promise.all( - data.index.map(async (synth) => { - synth.priceData = await getPriceData(synth.asset) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/bravenewcoin/.eslintrc.js b/synth-index/bravenewcoin/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/bravenewcoin/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/bravenewcoin/README.md b/synth-index/bravenewcoin/README.md deleted file mode 100644 index fb4705ae71..0000000000 --- a/synth-index/bravenewcoin/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# BraveNewCoin Synthetix Index - -Use of this adapter requires the following environment variable to be present: - -- `API_KEY`: The BraveNewCoin API key diff --git a/synth-index/bravenewcoin/index.js b/synth-index/bravenewcoin/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/bravenewcoin/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/synth-index/bravenewcoin/package.json b/synth-index/bravenewcoin/package.json deleted file mode 100644 index d1ae1ed3b9..0000000000 --- a/synth-index/bravenewcoin/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-bravenewcoin-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/bravenewcoin/priceAdapter.js b/synth-index/bravenewcoin/priceAdapter.js deleted file mode 100644 index 47f5027a4b..0000000000 --- a/synth-index/bravenewcoin/priceAdapter.js +++ /dev/null @@ -1,53 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const host = 'bravenewcoin-v1.p.rapidapi.com' -const url = `https://${host}/convert` - -const getPriceData = async (synth) => { - const headers = { - 'x-rapidapi-host': host, - 'x-rapidapi-key': util.getRandomRequiredEnv('API_KEY'), - } - const params = { - qty: 1, - to: 'USD', - from: synth.symbol, - } - const config = { - url, - params, - headers, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.to_quantity - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - await Promise.all( - data.index.map(async (synth) => { - synth.priceData = await getPriceData(synth) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/coinapi/.eslintrc.js b/synth-index/coinapi/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/coinapi/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/coinapi/README.md b/synth-index/coinapi/README.md deleted file mode 100644 index d9941134a8..0000000000 --- a/synth-index/coinapi/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# CoinAPI Synthetix Index - -Use of this adapter requires the following environment variable to be present: - -- `API_KEY`: The CoinAPI API key diff --git a/synth-index/coinapi/index.js b/synth-index/coinapi/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/coinapi/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/synth-index/coinapi/package.json b/synth-index/coinapi/package.json deleted file mode 100644 index 39bd9d3c62..0000000000 --- a/synth-index/coinapi/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-coinapi-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/coinapi/priceAdapter.js b/synth-index/coinapi/priceAdapter.js deleted file mode 100644 index cbfe6fa98d..0000000000 --- a/synth-index/coinapi/priceAdapter.js +++ /dev/null @@ -1,43 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const getPriceData = async (synth) => { - const url = `https://rest.coinapi.io/v1/exchangerate/${synth}/USD` - const config = { - url, - params: { - apikey: util.getRandomRequiredEnv('API_KEY'), - }, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.rate - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - await Promise.all( - data.index.map(async (synth) => { - synth.priceData = await getPriceData(synth.asset) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/coingecko/.eslintrc.js b/synth-index/coingecko/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/coingecko/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/coingecko/README.md b/synth-index/coingecko/README.md deleted file mode 100644 index 764ae2bdb6..0000000000 --- a/synth-index/coingecko/README.md +++ /dev/null @@ -1 +0,0 @@ -# Coingecko Synthetix Index diff --git a/synth-index/coingecko/index.js b/synth-index/coingecko/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/coingecko/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/synth-index/coingecko/package.json b/synth-index/coingecko/package.json deleted file mode 100644 index 5caac83771..0000000000 --- a/synth-index/coingecko/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-coingecko-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/coingecko/priceAdapter.js b/synth-index/coingecko/priceAdapter.js deleted file mode 100644 index 2ba7e603e6..0000000000 --- a/synth-index/coingecko/priceAdapter.js +++ /dev/null @@ -1,69 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') - -const getCoinList = async () => { - const url = 'https://api.coingecko.com/api/v3/coins/list' - const config = { - url, - } - const response = await Requester.request(config) - return response.data -} - -const getPriceData = async (id) => { - const url = 'https://api.coingecko.com/api/v3/simple/price' - const params = { - ids: id, - vs_currencies: 'usd', - } - const config = { - url, - params, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData[i.coinId].usd - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const coingeckoBlacklist = [ - 'leocoin', - 'farmatrust', - 'freetip', - 'compound-coin', - 'uni-coin', - 'unicorn-token', -] - -const execute = async (jobRunID, data) => { - const coinList = await getCoinList() - await Promise.all( - data.index.map(async (synth) => { - const coin = coinList.find( - (d) => - d.symbol.toLowerCase() === synth.asset.toLowerCase() && - !coingeckoBlacklist.includes(d.id.toLowerCase()), - ) - synth.coinId = coin.id - synth.priceData = await getPriceData(coin.id) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/coinmarketcap/.eslintrc.js b/synth-index/coinmarketcap/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/coinmarketcap/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/coinmarketcap/README.md b/synth-index/coinmarketcap/README.md deleted file mode 100644 index 8e79dda809..0000000000 --- a/synth-index/coinmarketcap/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# CoinMarketCap Synthetix Index - -Use of this adapter requires the following environment variable to be present: - -- `API_KEY`: The CoinMarketCap API key diff --git a/synth-index/coinmarketcap/index.js b/synth-index/coinmarketcap/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/coinmarketcap/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/synth-index/coinmarketcap/package.json b/synth-index/coinmarketcap/package.json deleted file mode 100644 index 589c16e7e6..0000000000 --- a/synth-index/coinmarketcap/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-coinmarketcap-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/coinmarketcap/priceAdapter.js b/synth-index/coinmarketcap/priceAdapter.js deleted file mode 100644 index a5c4e05312..0000000000 --- a/synth-index/coinmarketcap/priceAdapter.js +++ /dev/null @@ -1,90 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -// Defaults we use when there are multiple currencies with the same symbol -const presetSlugs = { - COMP: 'compound', - BNT: 'bancor', - RCN: 'ripio-credit-network', - UNI: 'uniswap', - CRV: 'curve-dao-token', - FNX: 'finnexus', - ETC: 'ethereum-classic', - BAT: 'basic-attention-token', - CRO: 'crypto-com-coin', - LEO: 'unus-sed-leo', - FTT: 'ftx-token', - HT: 'huobi-token', - OKB: 'okb', - KCS: 'kucoin-shares', -} - -const getPriceData = async (assets, convert) => { - const _getPriceData = async (params) => { - const url = 'https://pro-api.coinmarketcap.com/v1/cryptocurrency/quotes/latest' - const headers = { - 'X-CMC_PRO_API_KEY': util.getRandomRequiredEnv('API_KEY'), - } - const config = { - url, - headers, - params, - } - const response = await Requester.request(config) - return response.data - } - - let data = {} - if (!assets || assets.length === 0) return data - - // We map some symbols as slugs - const slugs = assets.map((s) => presetSlugs[s]).filter(Boolean) - const symbols = assets.filter((s) => !presetSlugs[s]) - - // We need to make two separate requests, one querying slugs - if (slugs.length > 0) { - const slugPrices = await _getPriceData({ slug: slugs.join(), convert }) - data = { ...data, ...slugPrices.data } - } - - // The other one querying symbols - if (symbols.length > 0) { - const symbolPrices = await _getPriceData({ symbol: symbols.join(), convert }) - data = { ...data, ...symbolPrices.data } - } - - return data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.quote.USD.price - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - const assets = data.index.map(({ asset }) => asset.toUpperCase()) - const pricesData = await getPriceData(assets, 'USD') - - for (let i = 0; i < data.index.length; i++) { - const { asset } = data.index[i] - const _iEqual = (s1, s2) => s1.toUpperCase() === s2.toUpperCase() - data.index[i].priceData = Object.values(pricesData).find((o) => _iEqual(o.symbol, asset)) - } - - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/coinpaprika/.eslintrc.js b/synth-index/coinpaprika/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/coinpaprika/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/coinpaprika/README.md b/synth-index/coinpaprika/README.md deleted file mode 100644 index 6f7f066505..0000000000 --- a/synth-index/coinpaprika/README.md +++ /dev/null @@ -1 +0,0 @@ -# Coinpaprika Synthetix Index diff --git a/synth-index/coinpaprika/index.js b/synth-index/coinpaprika/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/coinpaprika/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/synth-index/coinpaprika/package.json b/synth-index/coinpaprika/package.json deleted file mode 100644 index 3b4b078a37..0000000000 --- a/synth-index/coinpaprika/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-coinpaprika-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/coinpaprika/priceAdapter.js b/synth-index/coinpaprika/priceAdapter.js deleted file mode 100644 index 47e7381daf..0000000000 --- a/synth-index/coinpaprika/priceAdapter.js +++ /dev/null @@ -1,46 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') - -const getPriceData = async () => { - const url = 'https://api.coinpaprika.com/v1/tickers' - const params = { - quotes: 'USD', - } - const config = { - url, - params, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.quotes.USD.price - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (_, data) => { - const priceDatas = await getPriceData() - await Promise.all( - data.index.map(async (synth) => { - synth.priceData = priceDatas - .sort((a, b) => (a.rank > b.rank ? 1 : -1)) - .find((d) => d.symbol.toLowerCase() === synth.asset.toLowerCase()) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/cryptocompare/.eslintrc.js b/synth-index/cryptocompare/.eslintrc.js deleted file mode 100644 index c49b1ac4bc..0000000000 --- a/synth-index/cryptocompare/.eslintrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = require('../../.eslintrc.js') diff --git a/synth-index/cryptocompare/README.md b/synth-index/cryptocompare/README.md deleted file mode 100644 index b0b054e4e8..0000000000 --- a/synth-index/cryptocompare/README.md +++ /dev/null @@ -1 +0,0 @@ -# CryptoCompare Synthetix Index diff --git a/synth-index/cryptocompare/index.js b/synth-index/cryptocompare/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/cryptocompare/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/synth-index/cryptocompare/package.json b/synth-index/cryptocompare/package.json deleted file mode 100644 index 96c4ca6308..0000000000 --- a/synth-index/cryptocompare/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "@chainlink/synth-index-cryptocompare-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "lint": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx", - "lint:fix": "eslint --ignore-path ../../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "echo \"Error: no test specified\"", - "test:unit": "echo \"Error: no test specified\"", - "test:integration": "echo \"Error: no test specified\"" - }, - "dependencies": {} -} diff --git a/synth-index/cryptocompare/priceAdapter.js b/synth-index/cryptocompare/priceAdapter.js deleted file mode 100644 index 8e448a229c..0000000000 --- a/synth-index/cryptocompare/priceAdapter.js +++ /dev/null @@ -1,59 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const getPriceData = async (synths) => { - const url = 'https://min-api.cryptocompare.com/data/pricemulti' - const params = { - tsyms: 'USD', - fsyms: synths, - } - const config = { - url, - params, - } - if (process.env.API_KEY) { - config.headers = { - authorization: `Apikey ${util.getRandomRequiredEnv('API_KEY')}`, - } - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.USD - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - const synths = [] - data.index.forEach((synth) => { - synths.push(synth.asset.toUpperCase()) - }) - const prices = await getPriceData(synths.join()) - for (const symbol in prices) { - if (!Object.prototype.hasOwnProperty.call(prices, symbol)) continue - for (let i = 0; i < data.index.length; i++) { - if (symbol.toUpperCase() !== data.index[i].asset.toUpperCase()) continue - data.index[i].priceData = prices[symbol] - break - } - } - - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/kaiko/README.md b/synth-index/kaiko/README.md deleted file mode 100644 index 75eedaa188..0000000000 --- a/synth-index/kaiko/README.md +++ /dev/null @@ -1 +0,0 @@ -# Kaiko Synthetix Index diff --git a/synth-index/kaiko/index.js b/synth-index/kaiko/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/kaiko/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/synth-index/kaiko/package.json b/synth-index/kaiko/package.json deleted file mode 100644 index 4a28fce6d9..0000000000 --- a/synth-index/kaiko/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@chainlink/synth-index-kaiko-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "test": "yarn _mocha --timeout 0" - }, - "dependencies": {} -} diff --git a/synth-index/kaiko/priceAdapter.js b/synth-index/kaiko/priceAdapter.js deleted file mode 100644 index e1735024bf..0000000000 --- a/synth-index/kaiko/priceAdapter.js +++ /dev/null @@ -1,51 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const getPriceData = async (synth) => { - const url = `https://us.market-api.kaiko.io/v2/data/trades.v1/spot_exchange_rate/${synth.asset.toLowerCase()}/usd` - const params = { - sort: 'desc', - } - const headers = { - 'X-Api-Key': util.getRandomRequiredEnv("API_KEY"), - 'User-Agent': 'Chainlink', - } - const timeout = 5000 - const config = { - url, - params, - headers, - timeout, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.data[0].price - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - await Promise.all( - data.index.map(async (synth) => { - synth.priceData = await getPriceData(synth) - }), - ) - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/synth-index/nomics/README.md b/synth-index/nomics/README.md deleted file mode 100644 index 3d03d27112..0000000000 --- a/synth-index/nomics/README.md +++ /dev/null @@ -1 +0,0 @@ -# Nomics Synthetix Index diff --git a/synth-index/nomics/index.js b/synth-index/nomics/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/synth-index/nomics/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/synth-index/nomics/package.json b/synth-index/nomics/package.json deleted file mode 100644 index 68ca3e88c6..0000000000 --- a/synth-index/nomics/package.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "@chainlink/synth-index-nomics-adapter", - "version": "0.0.1", - "license": "MIT", - "main": "index.js", - "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", - "test": "yarn _mocha --timeout 0" - }, - "dependencies": {} -} diff --git a/synth-index/nomics/priceAdapter.js b/synth-index/nomics/priceAdapter.js deleted file mode 100644 index bb5cc53cba..0000000000 --- a/synth-index/nomics/priceAdapter.js +++ /dev/null @@ -1,64 +0,0 @@ -const { Requester } = require('@chainlink/external-adapter') -const Decimal = require('decimal.js') -const { util } = require('@chainlink/ea-bootstrap') - -const nomicsIds = { - FTT: 'FTXTOKEN', -} - -const getPriceData = async (synths) => { - const url = 'https://api.nomics.com/v1/currencies/ticker' - const params = { - ids: synths, - convert: 'USD', - key: util.getRandomRequiredEnv("API_KEY"), - } - const config = { - url, - params, - } - const response = await Requester.request(config) - return response.data -} - -const calculateIndex = (indexes) => { - let value = new Decimal(0) - try { - indexes.forEach((i) => { - const price = i.priceData.price - if (price <= 0) { - throw Error('invalid price') - } - value = value.plus(new Decimal(i.units).times(new Decimal(price))) - }) - } catch (error) { - throw error.message - } - return value.toNumber() -} - -const execute = async (jobRunID, data) => { - const synths = [] - data.index.forEach((synth) => { - let symbol = synth.asset.toUpperCase() - if (symbol in nomicsIds) { - symbol = nomicsIds[symbol] - } - synths.push(symbol) - }) - const prices = await getPriceData(synths.join()) - prices.forEach((price) => { - for (let i = 0; i < data.index.length; i++) { - if (data.index[i].asset.toUpperCase() !== price.symbol) { - continue - } - data.index[i].priceData = price - break - } - }) - - return data -} - -module.exports.execute = execute -module.exports.calculateIndex = calculateIndex diff --git a/taapi/package.json b/taapi/package.json index a6c7029917..cf27500aad 100644 --- a/taapi/package.json +++ b/taapi/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/taapi-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink taapi adapter.", "keywords": [ "Chainlink", diff --git a/taapi/src/index.ts b/taapi/src/index.ts index a566680d64..c8a8cf9608 100644 --- a/taapi/src/index.ts +++ b/taapi/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { execute } from './adapter' const NAME = 'taapi' -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, execute, ...expose(execute) } diff --git a/test-helpers/package.json b/test-helpers/package.json index ebd2d83091..e8e8851606 100644 --- a/test-helpers/package.json +++ b/test-helpers/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/adapter-test-helpers", - "version": "0.0.1", + "version": "0.0.2", "description": "Helpers for testing Chainlink External Adapters.", "keywords": [ "Chainlink", @@ -35,6 +35,7 @@ "@typescript-eslint/eslint-plugin": "^3.9.0", "@typescript-eslint/parser": "^3.9.0", "chai": "^4.2.0", + "hardhat": "^2.0.8", "mocha": "^7.1.2", "sinon": "^9.0.3", "ts-node": "^8.10.2", diff --git a/test-helpers/src/behaviors/balance.ts b/test-helpers/src/behaviors/balance.ts index a0a98fd2a7..ce35ec4552 100644 --- a/test-helpers/src/behaviors/balance.ts +++ b/test-helpers/src/behaviors/balance.ts @@ -134,7 +134,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -211,7 +211,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -248,7 +248,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -285,7 +285,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -322,7 +322,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -359,7 +359,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -396,7 +396,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -433,7 +433,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -470,7 +470,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -507,7 +507,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -544,7 +544,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -581,7 +581,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -618,7 +618,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -655,7 +655,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -692,7 +692,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -729,7 +729,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -766,7 +766,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -803,7 +803,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -840,7 +840,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -877,7 +877,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) @@ -914,7 +914,7 @@ const extensions: { [network: string]: (execute: Execute) => void } = { const jobID = '1' const assertions = (request: any, response: any) => { - const numAddr = request?.testData?.data?.addresses.length + const numAddr = request?.testData?.data?.result.length assert.isAbove(Number(response.data.result.length), 0) assert.isAbove(Number(response.result.length), 0) assert.equal(Number(response.data.result.length), numAddr) diff --git a/test-helpers/src/hardhat.ts b/test-helpers/src/hardhat.ts new file mode 100644 index 0000000000..5dfa75db2c --- /dev/null +++ b/test-helpers/src/hardhat.ts @@ -0,0 +1,28 @@ +import { JsonRpcServer } from 'hardhat/internal/hardhat-network/jsonrpc/server' +import { TASK_NODE_GET_PROVIDER, TASK_NODE_CREATE_SERVER } from 'hardhat/builtin-tasks/task-names' +import { run } from 'hardhat' + +export async function startChain(port = 7545): Promise { + console.log('Starting hardhat') + const hostname = 'localhost' + const provider = await run(TASK_NODE_GET_PROVIDER) + + // Disable logging + // if (Boolean(config.VERBOSE) !== true) + await provider.request({ + method: 'hardhat_setLoggingEnabled', + params: [false], + }) + + // Start Hardhat network + const server: JsonRpcServer = await run(TASK_NODE_CREATE_SERVER, { + hostname, + port, + provider, + }) + + // Wait until server is ready for requests + await server.listen() + console.log(`Hardhat listening on localhost:${port}`) + return server +} diff --git a/test-helpers/src/hardhat_config.json b/test-helpers/src/hardhat_config.json new file mode 100644 index 0000000000..293cc901a3 --- /dev/null +++ b/test-helpers/src/hardhat_config.json @@ -0,0 +1,4 @@ +{ + "TESTING_PRIVATE_KEY" : + "0x90125e49d93a24cc8409d1e00cc69c88919c6826d8bbabb6f2e1dc8213809f4c" +} \ No newline at end of file diff --git a/test-helpers/src/index.ts b/test-helpers/src/index.ts index ba94a34bd8..a863464308 100644 --- a/test-helpers/src/index.ts +++ b/test-helpers/src/index.ts @@ -1,4 +1,6 @@ import * as helpers from './helpers' import * as behaviors from './behaviors' +import * as hardhat from './hardhat' +import * as hardhat_config from './hardhat_config.json' -export = { ...helpers, ...behaviors } +export = { ...helpers, ...behaviors, ...hardhat, ...hardhat_config } diff --git a/test-helpers/tsconfig.json b/test-helpers/tsconfig.json index 3b4ccf41fa..4944357de8 100644 --- a/test-helpers/tsconfig.json +++ b/test-helpers/tsconfig.json @@ -3,7 +3,8 @@ "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"] diff --git a/therundown/.eslintrc.js b/therundown/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/therundown/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/therundown/README.md b/therundown/README.md new file mode 100644 index 0000000000..e96b325661 --- /dev/null +++ b/therundown/README.md @@ -0,0 +1,220 @@ +# Chainlink External Adapter for TheRundown + +### Environment Variables + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-----------------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://rapidapi.com/therundown/api/therundown) | | | + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :----------------------------------: | :---------: | +| | endpoint | The endpoint to use | [total-score](#Total-Score-Endpoint) | total-score | + +--- + +## Total Score Endpoint + +Returns the sum of both teams' scores for a match (match status must be final) + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-------: | :--------------------------: | :-----: | :---------: | +| ✅ | `matchId` | The ID of the match to query | | | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "matchId": "5527455bb80a5e9884153786aeb5f2b2" + } +} +``` + +### Sample Output + +```json +{ + "jobRunID": "278c97ffadb54a5bbb93cfec5f7b5503", + "data": { + "event_id": "5527455bb80a5e9884153786aeb5f2b2", + "event_uuid": "11ea-fb62-b7f6e800-88a2-1258f92d5a70", + "sport_id": 2, + "event_date": "2020-09-20T17:00:00Z", + "rotation_number_away": 277, + "rotation_number_home": 278, + "score": { + "event_id": "5527455bb80a5e9884153786aeb5f2b2", + "event_status": "STATUS_FINAL", + "score_away": 13, + "score_home": 17, + "winner_away": 0, + "winner_home": 1, + "score_away_by_period": [0, 0, 3, 10], + "score_home_by_period": [10, 7, 0, 0], + "venue_name": "Soldier Field", + "venue_location": "Chicago, IL", + "game_clock": 0, + "display_clock": "0.00", + "game_period": 4, + "broadcast": "CBS", + "event_status_detail": "Final" + }, + "teams": [ + { + "team_id": 18620, + "team_normalized_id": 78, + "name": "New York Giants", + "is_away": true, + "is_home": false + }, + { + "team_id": 18611, + "team_normalized_id": 81, + "name": "Chicago Bears", + "is_away": false, + "is_home": true + } + ], + "teams_normalized": [ + { + "team_id": 78, + "name": "New York", + "mascot": "Giants", + "abbreviation": "NYG", + "ranking": 0, + "record": "0-4", + "is_away": true, + "is_home": false + }, + { + "team_id": 81, + "name": "Chicago", + "mascot": "Bears", + "abbreviation": "CHI", + "ranking": 0, + "record": "3-1", + "is_away": false, + "is_home": true + } + ], + "schedule": { + "season_type": "Regular Season", + "season_year": 2020, + "week": 2, + "week_name": "Week 2", + "week_detail": "Sep 16-22", + "event_name": "New York at Chicago - 2020-09-20", + "attendance": "0" + }, + "lines": { + "1": { + "line_id": 10806455, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "2": { + "line_id": 10806465, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "3": { + "line_id": 10978226, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "4": { + "line_id": 10972470, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "6": { + "line_id": 10972478, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "7": { + "line_id": 10971009, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "9": { + "line_id": 10970983, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "10": { + "line_id": 10970996, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "11": { + "line_id": 10972462, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "12": { + "line_id": 10806566, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "15": { + "line_id": 10972458, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "16": { + "line_id": 10978258, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "17": { + "line_id": 10978096, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + }, + "18": { + "line_id": 10977770, + "moneyline": [Object], + "spread": [Object], + "total": [Object], + "affiliate": [Object] + } + }, + "line_periods": null, + "result": 30 + }, + "result": 30, + "statusCode": 200 +} +``` diff --git a/therundown/package.json b/therundown/package.json new file mode 100644 index 0000000000..d59739b7b7 --- /dev/null +++ b/therundown/package.json @@ -0,0 +1,46 @@ +{ + "name": "@chainlink/therundown-adapter", + "version": "0.0.2", + "description": "Chainlink therundown adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "therundown" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": {} +} diff --git a/therundown/src/adapter.ts b/therundown/src/adapter.ts new file mode 100644 index 0000000000..8709b5302d --- /dev/null +++ b/therundown/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { totalScore } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case totalScore.NAME: { + return await totalScore.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/therundown/src/config.ts b/therundown/src/config.ts new file mode 100644 index 0000000000..41e0f616de --- /dev/null +++ b/therundown/src/config.ts @@ -0,0 +1,18 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'total-score' +export const DEFAULT_BASE_URL = 'https://therundown-therundown-v1.p.rapidapi.com/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseURL || DEFAULT_BASE_URL, + headers: { + ...config.api.headers, + 'x-rapidapi-host': 'therundown-therundown-v1.p.rapidapi.com', + }, + } + return config +} diff --git a/therundown/src/endpoint/index.ts b/therundown/src/endpoint/index.ts new file mode 100644 index 0000000000..9380c185cc --- /dev/null +++ b/therundown/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as totalScore from './totalScore' diff --git a/therundown/src/endpoint/totalScore.ts b/therundown/src/endpoint/totalScore.ts new file mode 100644 index 0000000000..90a7d8c9bb --- /dev/null +++ b/therundown/src/endpoint/totalScore.ts @@ -0,0 +1,43 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'total-score' + +const customParams = { + matchId: true, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const matchId = validator.validated.data.matchId + const url = `events/${matchId}` + + const reqConfig = { + ...config.api, + headers: { + ...config.api.headers, + 'x-rapidapi-key': config.apiKey, + }, + params: { + include: 'scores', + }, + url, + } + + const response = await Requester.request(reqConfig) + + if (response.data.score.event_status !== 'STATUS_FINAL') { + return Requester.errored(jobRunID, 'Match status is not final') + } + + const result = parseInt(response.data.score.score_away) + parseInt(response.data.score.score_home) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/therundown/src/index.ts b/therundown/src/index.ts new file mode 100644 index 0000000000..a78e1d5bda --- /dev/null +++ b/therundown/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'THERUNDOWN' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/therundown/test/adapter.test.ts b/therundown/test/adapter.test.ts new file mode 100644 index 0000000000..307afb5f37 --- /dev/null +++ b/therundown/test/adapter.test.ts @@ -0,0 +1,71 @@ +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', () => { + process.env.API_KEY = process.env.API_KEY ?? 'test_API_key' + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { matchId: '5527455bb80a5e9884153786aeb5f2b2' } }, + }, + { + name: 'matchId', + testData: { id: jobID, data: { matchId: '5527455bb80a5e9884153786aeb5f2b2' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown matchId', + testData: { id: jobID, data: { matchId: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/therundown/tsconfig.json b/therundown/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/therundown/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/tiingo/.eslintrc.js b/tiingo/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/tiingo/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/tiingo/README.md b/tiingo/README.md new file mode 100644 index 0000000000..31abd1cae9 --- /dev/null +++ b/tiingo/README.md @@ -0,0 +1,53 @@ +# Chainlink Tiingo External Adapter + +### Environment Variables + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :------------------------------------------------------------------------------------------------: | :-----: | :---------: | +| ✅ | API_KEY | An API key that can be obtained from [here](https://api.tiingo.com/documentation/general/overview) | | | + +--- + +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :------------------: | :---------: | +| | endpoint | The endpoint to use | [eod](#EOD-Endpoint) | `eod` | + +--- + +## EOD Endpoint + +https://api.tiingo.com/documentation/end-of-day + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------------: | :-----: | :---------: | +| ✅ | `ticker` | The stock ticker to query | | | +| | `field` | The value to return | | `close` | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "ticker": "aapl", + "field": "close" + } +} +``` + +### Sample Output + +```json +{ + "jobRunID": "1", + "data": { + "result": 130.27 + }, + "result": 130.27, + "statusCode": 200 +} +``` diff --git a/tiingo/package.json b/tiingo/package.json new file mode 100644 index 0000000000..98503de0cc --- /dev/null +++ b/tiingo/package.json @@ -0,0 +1,46 @@ +{ + "name": "@chainlink/tiingo-adapter", + "version": "0.0.2", + "description": "Chainlink tiingo adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "tiingo" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": {} +} diff --git a/tiingo/src/adapter.ts b/tiingo/src/adapter.ts new file mode 100644 index 0000000000..471f4212c9 --- /dev/null +++ b/tiingo/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { eod } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case eod.NAME: { + return await eod.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/tiingo/src/config.ts b/tiingo/src/config.ts new file mode 100644 index 0000000000..546fe1c572 --- /dev/null +++ b/tiingo/src/config.ts @@ -0,0 +1,14 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'eod' +export const DEFAULT_BASE_URL = 'https://api.tiingo.com/tiingo/' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix, true) + config.api = { + ...config.api, + baseURL: config.api.baseUrl || DEFAULT_BASE_URL, + } + return config +} diff --git a/tiingo/src/endpoint/eod.ts b/tiingo/src/endpoint/eod.ts new file mode 100644 index 0000000000..49e75adf4f --- /dev/null +++ b/tiingo/src/endpoint/eod.ts @@ -0,0 +1,36 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'eod' + +const customParams = { + ticker: true, + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const ticker = validator.validated.data.ticker + const field = validator.validated.data.field || 'close' + const url = `daily/${ticker.toLowerCase()}/prices` + + const reqConfig = { + ...config.api, + params: { + token: config.apiKey, + }, + url, + } + + const response = await Requester.request(reqConfig) + const result = Requester.validateResultNumber(response.data, [0, field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/tiingo/src/endpoint/index.ts b/tiingo/src/endpoint/index.ts new file mode 100644 index 0000000000..d505edccdc --- /dev/null +++ b/tiingo/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as eod from './eod' diff --git a/tiingo/src/index.ts b/tiingo/src/index.ts new file mode 100644 index 0000000000..d36cae596d --- /dev/null +++ b/tiingo/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'TIINGO' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/tiingo/test/adapter.test.ts b/tiingo/test/adapter.test.ts new file mode 100644 index 0000000000..59244e8f4b --- /dev/null +++ b/tiingo/test/adapter.test.ts @@ -0,0 +1,83 @@ +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', () => { + process.env.API_KEY = process.env.API_KEY ?? 'test_API_key' + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: { ticker: 'aapl' } }, + }, + { + name: 'ticker', + testData: { id: jobID, data: { ticker: 'aapl' } }, + }, + { + name: 'ticker & field', + testData: { id: jobID, data: { ticker: 'aapl', field: 'open' } }, + }, + ] + + 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: {} } }, + { + name: 'ticker not supplied', + testData: { id: jobID, data: { field: 'open' } }, + }, + ] + + 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown ticker', + testData: { id: jobID, data: { ticker: 'not_real' } }, + }, + { + name: 'unknown field', + testData: { id: jobID, data: { ticker: 'aapl', field: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/tiingo/tsconfig.json b/tiingo/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/tiingo/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/tradermade/README.md b/tradermade/README.md index 98ccd881eb..e134b5bf89 100644 --- a/tradermade/README.md +++ b/tradermade/README.md @@ -4,6 +4,7 @@ - `base`, `from`, or `coin`: The symbol of the currency to query - `to`: The symbol of the currency to convert to (for FX) +- `overrides`: (not required) If base provided is found in overrides, that will be used. [Format](../external-adapter/src/overrides/presetSymbols.json) ## Output diff --git a/tradermade/package.json b/tradermade/package.json index 50825f2929..034d0ec08c 100644 --- a/tradermade/package.json +++ b/tradermade/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/tradermade-adapter", - "version": "0.0.3", + "version": "0.0.5", "description": "Chainlink tradermade adapter.", "keywords": [ "Chainlink", diff --git a/tradermade/src/adapter.ts b/tradermade/src/adapter.ts index 92cb92a6fb..48589b0a93 100644 --- a/tradermade/src/adapter.ts +++ b/tradermade/src/adapter.ts @@ -1,38 +1,35 @@ -import { Execute } from '@chainlink/types' +import { Config, ExecuteFactory, ExecuteWithConfig } from '@chainlink/types' import { Requester, Validator } from '@chainlink/external-adapter' -import { util } from '@chainlink/ea-bootstrap' +import { makeConfig, NAME } from './config' const customParams = { - symbol: ['base', 'from', 'symbol'], + base: ['base', 'from', 'symbol', 'market'], to: false, } -const commonKeys: Record = { - FTSE: 'UK100', - N225: 'JPN225', -} - -export const execute: Execute = async (input) => { +export const execute: ExecuteWithConfig = async (input, config) => { const validator = new Validator(input, customParams) if (validator.error) throw validator.error + Requester.logConfig(config) + const jobRunID = validator.validated.id - const url = 'https://marketdata.tradermade.com/api/v1/live' - const symbol = validator.validated.data.symbol.toUpperCase() + const symbol = validator.overrideSymbol(NAME).toUpperCase() const to = (validator.validated.data.to || '').toUpperCase() - const currency = (commonKeys[symbol] || symbol) + to + const currency = symbol + to const params = { + ...config.api.params, currency, - api_key: util.getRandomRequiredEnv('API_KEY'), } - const config = { - url, - params, - } + const options = { ...config.api, params } - const response = await Requester.request(config) + const response = await Requester.request(options) response.data.result = Requester.validateResultNumber(response.data, ['quotes', 0, 'mid']) return Requester.success(jobRunID, response) } + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/tradermade/src/config.ts b/tradermade/src/config.ts new file mode 100644 index 0000000000..2dfc0622ae --- /dev/null +++ b/tradermade/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const NAME = 'TRADERMADE' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || 'https://marketdata.tradermade.com/api/v1/live' + config.api.params = { api_key: config.apiKey } + return config +} diff --git a/tradermade/src/index.ts b/tradermade/src/index.ts index f6c80c64b9..21ea85958f 100644 --- a/tradermade/src/index.ts +++ b/tradermade/src/index.ts @@ -1,6 +1,5 @@ -import { expose, util } from '@chainlink/ea-bootstrap' -import { execute } from './adapter' +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig, NAME } from './config' -const NAME = 'Tradermade' - -export = { NAME, execute, ...expose(util.wrapExecute(execute)) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/tradermade/test/adapter.test.ts b/tradermade/test/adapter.test.ts index 5b1244148d..b6eeee7c1d 100644 --- a/tradermade/test/adapter.test.ts +++ b/tradermade/test/adapter.test.ts @@ -2,7 +2,7 @@ import { assert } from 'chai' import { Requester } from '@chainlink/external-adapter' import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' import { AdapterRequest } from '@chainlink/types' -import { execute } from '../src/adapter' +import { makeExecute } from '../src/adapter' describe('execute', () => { const jobID = '1' @@ -25,7 +25,7 @@ describe('execute', () => { requests.forEach((req) => { it(`${req.name}`, async () => { - const data = await execute(req.testData as AdapterRequest) + const data = await makeExecute()(req.testData as AdapterRequest) assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) assert.isAbove(data.result, 0) assert.isAbove(data.data.result, 0) @@ -46,7 +46,7 @@ describe('execute', () => { requests.forEach((req) => { it(`${req.name}`, async () => { try { - await execute(req.testData as AdapterRequest) + await makeExecute()(req.testData as AdapterRequest) } catch (error) { const errorResp = Requester.errored(jobID, error) assertError({ expected: 400, actual: errorResp.statusCode }, errorResp, jobID) @@ -70,7 +70,7 @@ describe('execute', () => { requests.forEach((req) => { it(`${req.name}`, async () => { try { - await execute(req.testData as AdapterRequest) + await makeExecute()(req.testData as AdapterRequest) } catch (error) { const errorResp = Requester.errored(jobID, error) assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) diff --git a/tradingeconomics-stream/.eslintrc.js b/tradingeconomics-stream/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/tradingeconomics-stream/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/tradingeconomics-stream/README.md b/tradingeconomics-stream/README.md new file mode 100644 index 0000000000..98c067ee0c --- /dev/null +++ b/tradingeconomics-stream/README.md @@ -0,0 +1,43 @@ +# Chainlink External Adapter for Tradingeconomics Stream + +This adapter uses the Tradingeconomics WS stream + +### Environment variables + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| ❌ | `API_URL` | The URL of the WS endpoint | | `ws://stream.tradingeconomics.com/` | +| ✅ | `API_CLIENT_KEY` | Your API client key | | | +| ✅ | `API_CLIENT_SECRET` | Your API client secret | | | +| ✅ | `SYMBOLS` | A comma delimited list of symbols to fetch prices for. E.g: "FTSE,N225" | | | +| ❌ | `RECONNECT_TIMEOUT` | Time to wait in ms before reconnecting the WS connection | | 3000 | + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------------------------: | :--------------------------------------: | :-----------------: | :---------: | +| ✅ | `base`, `from`, or `asset` | The symbol of the asset to query | one of `SYMBOLS` | | + +### Sample Input + +```json +{ + "id": "1", + "data": { + "base": "FTSE" + } +} +``` + +### Sample Output + +```json +{ + "jobRunID": "1", + "data": { + "result": 6663.73 + }, + "result": 6663.73, + "statusCode": 200 +} +``` diff --git a/tradingeconomics-stream/package.json b/tradingeconomics-stream/package.json new file mode 100644 index 0000000000..38742ee0c7 --- /dev/null +++ b/tradingeconomics-stream/package.json @@ -0,0 +1,50 @@ +{ + "name": "@chainlink/tradingeconomics-stream-adapter", + "version": "0.0.4", + "description": "Chainlink tradingeconomics-stream adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle", + "tradingeconomics-stream" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, + "author": "Jonas Hals ", + "license": "MIT", + "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 --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" + }, + "dependencies": { + "express": "^4.17.1", + "tradingeconomics-stream": "^0.2.0" + } +} diff --git a/tradingeconomics-stream/src/adapter.ts b/tradingeconomics-stream/src/adapter.ts new file mode 100644 index 0000000000..72aa7bd088 --- /dev/null +++ b/tradingeconomics-stream/src/adapter.ts @@ -0,0 +1,83 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { TEClient } from 'tradingeconomics-stream' +import { AdapterRequest } from '@chainlink/types' +import { Config, makeConfig } from './config' + +const prices: { [symbol: string]: number } = {} + +const subscribe = (asset: string, config: Config) => { + const client = new TEClient({ + ...config, + reconnect: true, + }) + + client.subscribe(asset) + + client.on('message', (msg: any) => { + console.log(`Got price for asset ${asset}:`, msg.price) + prices[asset] = msg.price + }) +} + +export const startService = (config: Config): void => { + const symbols = config.symbols.split(',') + for (let i = 0; i < symbols.length; i++) { + const symbol = commonSymbols[symbols[i]] + subscribe(symbol, config) + } +} + +const customParams = { + base: ['base', 'from', 'asset'], +} + +const commonSymbols: { [symbol: string]: string } = { + N225: 'NKY:IND', + FTSE: 'UKX:IND', +} + +export const execute = async (input: AdapterRequest, config: Config) => { + const validator = new Validator(input, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + let symbol = validator.validated.data.base.toUpperCase() + if (symbol in commonSymbols) { + symbol = commonSymbols[symbol] + } + + const price = Number(prices[symbol]) + if (price > 0) { + const response = { + data: { + result: price, + }, + result: price, + status: 200, + } + return Requester.success(jobRunID, response) + } + + // Fall back to getting the data from HTTP endpoint + const url = `https://api.tradingeconomics.com/markets/symbol/${symbol}` + + const params = { + c: `${config.key}:${config.secret}`, + } + + const response = await Requester.request({ url, params }) + if (!response.data || response.data.length < 1) { + throw new Error('no result for query') + } + // Replace array by the first object in array + // to avoid unexpected behavior when returning arrays. + response.data = response.data[0] + + response.data.result = Requester.validateResultNumber(response.data, ['Last']) + prices[symbol] = response.data.result + return Requester.success(jobRunID, response) +} + +export const makeExecute = (config?: Config) => { + return async (request: AdapterRequest) => execute(request, config || makeConfig()) +} diff --git a/tradingeconomics-stream/src/config.ts b/tradingeconomics-stream/src/config.ts new file mode 100644 index 0000000000..0d4fce6c1a --- /dev/null +++ b/tradingeconomics-stream/src/config.ts @@ -0,0 +1,19 @@ +import { util } from '@chainlink/ea-bootstrap' + +export type Config = { + url: string + key: string + secret: string + symbols: string + reconnectTimeout: number +} + +export const makeConfig = (prefix?: string): Config => { + return { + url: util.getEnv('API_URL', prefix) || 'ws://stream.tradingeconomics.com/', + key: util.getRequiredEnv('API_CLIENT_KEY', prefix), + secret: util.getRequiredEnv('API_CLIENT_SECRET', prefix), + symbols: util.getRequiredEnv('SYMBOLS', prefix), + reconnectTimeout: Number(util.getEnv('RECONNECT_TIMEOUT', prefix) || 3000), + } +} diff --git a/tradingeconomics-stream/src/index.ts b/tradingeconomics-stream/src/index.ts new file mode 100644 index 0000000000..bf072e6f4e --- /dev/null +++ b/tradingeconomics-stream/src/index.ts @@ -0,0 +1,19 @@ +import * as bootstrap from '@chainlink/ea-bootstrap' +import { makeExecute, startService } from './adapter' +import { AdapterRequest, Execute, ExecuteSync } from '@chainlink/types' +import { Requester } from '@chainlink/external-adapter' +import { makeConfig } from './config' + +// Execution helper async => sync +const executeSync = (execute: Execute): ExecuteSync => { + return (data: AdapterRequest, callback: any) => { + return execute(data) + .then((result) => callback(result.statusCode, result)) + .catch((error) => callback(error.statusCode || 500, Requester.errored(data.id, error))) + } +} + +export const server = (): void => { + startService(makeConfig()) + bootstrap.server.initHandler(executeSync(makeExecute()))() +} diff --git a/tradingeconomics-stream/test/adapter.test.ts b/tradingeconomics-stream/test/adapter.test.ts new file mode 100644 index 0000000000..2afb87054b --- /dev/null +++ b/tradingeconomics-stream/test/adapter.test.ts @@ -0,0 +1,31 @@ +import { Requester } from '@chainlink/external-adapter' +import { assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute({ key: '', reconnectTimeout: 0, secret: '', symbols: '', url: '' }) + + context('validation error', () => { + const requests = [ + { name: 'empty body', testData: {} }, + { name: 'empty data', testData: { data: {} } }, + { + name: 'base not supplied', + testData: { id: jobID, data: { quote: 'USD' } }, + }, + ] + + 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) + } + }) + }) + }) +}) diff --git a/tradingeconomics-stream/tsconfig.json b/tradingeconomics-stream/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/tradingeconomics-stream/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/tradingeconomics/.eslintrc.js b/tradingeconomics/.eslintrc.js index 7d045a59de..11f16f9a15 100644 --- a/tradingeconomics/.eslintrc.js +++ b/tradingeconomics/.eslintrc.js @@ -1 +1,3 @@ -module.exports = require('../.eslintrc.js') +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/tradingeconomics/README.md b/tradingeconomics/README.md index 42f23b9edb..28ad3bef70 100644 --- a/tradingeconomics/README.md +++ b/tradingeconomics/README.md @@ -1,41 +1,113 @@ # Chainlink External Adapter for Trading Economics -## Configuration +### Environment Variables -The adapter takes the following environment variables: +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------: | :-----------------------------------------------------: | :-----: | :---------: | +| ✅ | API_CLIENT_KEY | | | | +| ✅ | API_CLIENT_SECRET | | | | +| | WS_TIMEOUT | The timeout for waiting for a response in the WS stream | | `5000` | -- `API_CLIENT_KEY`: Your API client key -- `API_CLIENT_SECRET`: Your API client secret -- `WS_TIMEOUT`: The timeout for waiting for a response in the WS stream (default: 5000) +--- -## Input Params +### Input Parameters -- `base`, `from`, or `asset`: The symbol of the asset to query +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [markets](#Markets-Endpoint) | markets | -## Output +--- + +## Markets Endpoint + +Returns the price of a currency. + +First attempts to request over a Websocket connecting and then falls back to HTTP + +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :---------------------: | :---------------------------------: | :-----: | :---------: | +| ✅ | `base`, `from`, `asset` | The symbol of the currency to query | | | + +### Sample Input ```json { - "jobRunID":"1", - "data":{ - "s":"UKX:IND", - "i":"UKX", - "pch":-0.28, - "nch":-17.37, - "bid":6106.32, - "ask":6106.32, - "price":6106.32, - "dt":1593080344361, - "state":"open", - "type":"index", - "dhigh":6320.12, - "dlow":6068.87, - "o":6320.12, - "prev":6123.69, - "topic":"UKX", - "result":6106.32 - }, - "result":6106.32, - "statusCode":200 + "id": "1", + "data": { "base": "FTSE" } } ``` + +### Sample Output + +WS + +```json +{ + "jobRunID": "1", + "data": { + "s": "UKX:IND", + "i": "UKX", + "pch": -0.28, + "nch": -17.37, + "bid": 6106.32, + "ask": 6106.32, + "price": 6106.32, + "dt": 1593080344361, + "state": "open", + "type": "index", + "dhigh": 6320.12, + "dlow": 6068.87, + "o": 6320.12, + "prev": 6123.69, + "topic": "UKX", + "result": 6106.32 + }, + "result": 6106.32, + "statusCode": 200 +} +``` + +HTTP + +```json +[ + { + "Symbol": "AAPL:US", + "Ticker": "AAPL", + "Name": "Apple", + "Country": "United States", + "Date": "2021-03-03T21:00:00", + "Type": "stocks", + "decimals": 2.0, + "state": "CLOSED ", + "Last": 122.825, + "Close": 122.825, + "CloseDate": "2021-03-03T00:00:00", + "MarketCap": 1303031131050.0, + "URL": "/aapl:us", + "Importance": 500, + "DailyChange": -2.295, + "DailyPercentualChange": -1.8342, + "WeeklyChange": -2.525, + "WeeklyPercentualChange": -2.0144, + "MonthlyChange": -14.565, + "MonthlyPercentualChange": -10.6012, + "YearlyChange": 47.14, + "YearlyPercentualChange": 62.2845, + "YTDChange": -9.865, + "YTDPercentualChange": -7.4346, + "day_high": 125.71, + "day_low": 121.84, + "yesterday": 125.12, + "lastWeek": 125.35, + "lastMonth": 137.39, + "lastYear": 75.685, + "startYear": 132.69, + "ISIN": "US0378331005 ", + "frequency": "Daily, Intraday, Live Stream", + "LastUpdate": "2021-03-03T21:01:00" + } +] +``` diff --git a/tradingeconomics/adapter.js b/tradingeconomics/adapter.js deleted file mode 100644 index 2c2f08e9b3..0000000000 --- a/tradingeconomics/adapter.js +++ /dev/null @@ -1,99 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') -const TeClient = require('tradingeconomics-stream') -const API_CLIENT_KEY = process.env.API_CLIENT_KEY -const API_CLIENT_SECRET = process.env.API_CLIENT_SECRET -const DEFAULT_WS_TIMEOUT = 5000 -const wsTimeout = process.env.WS_TIMEOUT || DEFAULT_WS_TIMEOUT - -const customParams = { - base: ['base', 'from', 'asset'], -} - -const commonSymbols = { - N225: 'NKY:IND', - FTSE: 'UKX:IND', -} - -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 symbol = validator.validated.data.base.toUpperCase() - if (symbol in commonSymbols) { - symbol = commonSymbols[symbol] - } - - const client = new TeClient({ - url: 'ws://stream.tradingeconomics.com/', - key: API_CLIENT_KEY, - secret: API_CLIENT_SECRET, - reconnect: false, - }) - - client.subscribe(symbol) - - let completed = false - - client.on('message', (msg) => { - completed = true - const response = { - data: msg, - status: 200, - } - response.data.result = Requester.validateResultNumber(response.data, ['price']) - callback(response.status, Requester.success(jobRunID, response)) - client.ws.close() - }) - - const _getDataFromUrl = () => { - const url = `https://api.tradingeconomics.com/markets/symbol/${symbol}` - - const params = { - c: `${API_CLIENT_KEY}:${API_CLIENT_SECRET}`, - } - - const config = { - url, - params, - } - - const _handleResponse = (response) => { - if (!response.data || response.data.length < 1) { - return callback(500, Requester.errored(jobRunID, 'no result for query')) - } - // Replace array by the first object in array - // to avoid unexpected behavior when returning arrays. - response.data = response.data[0] - - response.data.result = Requester.validateResultNumber(response.data, ['Last']) - callback(response.status, Requester.success(jobRunID, response)) - } - - const _handleError = (error) => callback(500, Requester.errored(jobRunID, error)) - - Requester.request(config).then(_handleResponse).catch(_handleError) - } - - // In case we don't get a response from the WS stream - // within the WS_TIMEOUT, we do a check on the (possibly) - // delayed HTTP(s) endpoint. - const timeout = 1000 - const maxTries = wsTimeout / timeout - let tries = 0 - const _checkTimeout = () => { - if (completed) return - - if (tries++ > maxTries) { - client.ws.close() - _getDataFromUrl() - return - } - - setTimeout(_checkTimeout, timeout) - } - - setTimeout(_checkTimeout, timeout) -} - -module.exports.execute = execute diff --git a/tradingeconomics/index.js b/tradingeconomics/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/tradingeconomics/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/tradingeconomics/package.json b/tradingeconomics/package.json index 6e511bff8f..05ef4004b1 100644 --- a/tradingeconomics/package.json +++ b/tradingeconomics/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/tradingeconomics-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink TradingEconomics adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 8000 -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 8000 --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": { "tradingeconomics-stream": "^0.1.2" diff --git a/tradingeconomics/src/adapter.ts b/tradingeconomics/src/adapter.ts new file mode 100644 index 0000000000..2ad9d7931b --- /dev/null +++ b/tradingeconomics/src/adapter.ts @@ -0,0 +1,36 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { CustomConfig, makeConfig, DEFAULT_ENDPOINT } from './config' +import { markets } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint.toLowerCase()) { + case 'price': + case markets.NAME: { + return await markets.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/tradingeconomics/src/config.ts b/tradingeconomics/src/config.ts new file mode 100644 index 0000000000..41b4808f04 --- /dev/null +++ b/tradingeconomics/src/config.ts @@ -0,0 +1,31 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' +import { util } from '@chainlink/ea-bootstrap' + +export const DEFAULT_ENDPOINT = 'markets' +export const DEFAULT_BASE_URL = 'https://api.tradingeconomics.com' + +const DEFAULT_WS_TIMEOUT = 5000 + +export type CustomConfig = Config & { + wsTimeout: number + apiClientKey: string +} + +export const makeConfig = (prefix?: string): CustomConfig => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + + config.wsTimeout = util.getEnv('WS_TIMEOUT') || DEFAULT_WS_TIMEOUT + + const CLIENT_KEY = util.getRequiredEnv('API_CLIENT_KEY') + config.clientKey = CLIENT_KEY + const CLIENT_SECRET = util.getRequiredEnv('API_CLIENT_SECRET') + config.apiKey = CLIENT_SECRET + + config.api.params = { + c: `${CLIENT_KEY}:${CLIENT_SECRET}`, + } + + return config +} diff --git a/tradingeconomics/src/endpoint/index.ts b/tradingeconomics/src/endpoint/index.ts new file mode 100644 index 0000000000..816b036aed --- /dev/null +++ b/tradingeconomics/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as markets from './markets' diff --git a/tradingeconomics/src/endpoint/markets.ts b/tradingeconomics/src/endpoint/markets.ts new file mode 100644 index 0000000000..31efe6200f --- /dev/null +++ b/tradingeconomics/src/endpoint/markets.ts @@ -0,0 +1,101 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig } from '@chainlink/types' +import { CustomConfig } from '../config' +import TeClient from 'tradingeconomics-stream' + +export const NAME = 'markets' + +const customParams = { + base: ['base', 'from', 'asset'], +} + +const commonSymbols: Record = { + N225: 'NKY:IND', + FTSE: 'UKX:IND', +} + +const _executeHTTP = async (jobRunID: any, symbol: string, config: CustomConfig) => { + const url = `markets/symbol/${symbol}` + + const options = { + ...config.api, + url, + } + + const response = await Requester.request(options) + const result = Requester.validateResultNumber(response.data[0], ['Last']) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} + +const _executeWS = async (jobRunID: any, symbol: string, config: CustomConfig) => + new Promise((resolve, reject) => { + const client = new TeClient({ + url: 'ws://stream.tradingeconomics.com/', + key: config.apiClientKey, + secret: config.apiKey, + reconnect: false, + }) + + client.subscribe(symbol) + + let completed = false + + client.on('message', (msg: any) => { + client.ws.close() + completed = true + const response = { + data: msg, + status: 200, + } + const result = Requester.validateResultNumber(response.data, ['price']) + resolve( + Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }), + ) + }) + + // In case we don't get a response from the WS stream + // within the WS_TIMEOUT, we do a check on the (possibly) + // delayed HTTP(s) endpoint. + const timeout = 1000 + const maxTries = config.wsTimeout / timeout + let tries = 0 + const _checkTimeout = () => { + if (completed) return + + if (tries++ > maxTries) { + client.ws.close() + reject('Websocket Timed Out') + } + + setTimeout(_checkTimeout, timeout) + } + + setTimeout(_checkTimeout, timeout) + }) + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + let symbol = validator.validated.data.base.toUpperCase() + if (symbol in commonSymbols) { + symbol = commonSymbols[symbol] + } + + try { + const ws = await _executeWS(jobRunID, symbol, config) + if (ws) return ws + } catch { + return await _executeHTTP(jobRunID, symbol, config) + } +} diff --git a/tradingeconomics/src/index.ts b/tradingeconomics/src/index.ts new file mode 100644 index 0000000000..3527f7b8af --- /dev/null +++ b/tradingeconomics/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'TRADINGECONOMICS' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/tradingeconomics/test/adapter.test.ts b/tradingeconomics/test/adapter.test.ts new file mode 100644 index 0000000000..7f2ba3630f --- /dev/null +++ b/tradingeconomics/test/adapter.test.ts @@ -0,0 +1,68 @@ +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() + if (!process.env.API_CLIENT_KEY) process.env.API_CLIENT_KEY = 'test_client_key' + if (!process.env.API_CLIENT_SECRET) process.env.API_CLIENT_SECRET = 'test_client_secret' + + context('successful calls @integration', () => { + const requests = [ + { name: 'id not supplied', testData: { data: { base: 'FTSE' } } }, + { name: 'base', testData: { id: jobID, data: { base: 'FTSE' } } }, + { name: 'from/to', testData: { id: jobID, data: { from: 'FTSE' } } }, + ] + + 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: {} } }, + { name: 'base not supplied', testData: { id: jobID, 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) + } + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown base', + testData: { id: jobID, data: { base: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/tradingeconomics/test/adapter_test.js b/tradingeconomics/test/adapter_test.js deleted file mode 100644 index 85ade99bc3..0000000000 --- a/tradingeconomics/test/adapter_test.js +++ /dev/null @@ -1,61 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { name: 'id not supplied', testData: { data: { base: 'FTSE' } } }, - { name: 'base', testData: { id: jobID, data: { base: 'FTSE' } } }, - { name: 'from/to', testData: { id: jobID, data: { from: 'FTSE' } } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('validation error', () => { - const requests = [ - { name: 'empty body', testData: {} }, - { name: 'empty data', testData: { data: {} } }, - { name: 'base not supplied', testData: { id: jobID, data: {} } }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 400, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown base', - testData: { id: jobID, data: { base: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/tradingeconomics/tsconfig.json b/tradingeconomics/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/tradingeconomics/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/tradingeconomics/typings/tradingeconomics/index.d.ts b/tradingeconomics/typings/tradingeconomics/index.d.ts new file mode 100644 index 0000000000..024398ff63 --- /dev/null +++ b/tradingeconomics/typings/tradingeconomics/index.d.ts @@ -0,0 +1 @@ +declare module 'tradingeconomics-stream' diff --git a/trueusd/.eslintrc.js b/trueusd/.eslintrc.js new file mode 100644 index 0000000000..11f16f9a15 --- /dev/null +++ b/trueusd/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + ...require('../.eslintrc.ts.js'), +} diff --git a/trueusd/README.md b/trueusd/README.md index 0bc7bd7d2e..5ab5f1f6a2 100644 --- a/trueusd/README.md +++ b/trueusd/README.md @@ -1,14 +1,35 @@ # Chainlink External Adapter for TrueUSD -## Total Supply API +### Input Parameters + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :------: | :-----------------: | :--------------------------: | :---------: | +| | endpoint | The endpoint to use | [trueusd](#TrueUSD-Endpoint) | `trueusd` | + +--- + +## TrueUSD Endpoint https://core-api.real-time-attest.trustexplorer.io/trusttoken/TrueUSD -## Input Params +### Input Params + +| Required? | Name | Description | Options | Defaults to | +| :-------: | :-----: | :-------------------------------------------------: | :------------------------: | :----------: | +| | `field` | The data point to return from the API response data | `totalTrust`, `totalToken` | `totalTrust` | + +### Sample Input -- `field`: The data field to return. (defaults to `totalTrust`, one of `totalToken`, `totalTrust`) +```json +{ + "id": "1", + "data": { + "field": "totalToken" + } +} +``` -## Output +### Output ```json { diff --git a/trueusd/adapter.js b/trueusd/adapter.js deleted file mode 100644 index 68aa0642f6..0000000000 --- a/trueusd/adapter.js +++ /dev/null @@ -1,28 +0,0 @@ -const { Requester, Validator } = require('@chainlink/external-adapter') - -const customError = (data) => { - if (data.Response === 'Error') return true - return false -} - -const supplyParams = { - field: false, -} - -const execute = (input, callback) => { - const validator = new Validator(input, supplyParams) - if (validator.error) return callback(validator.error.statusCode, validator.errored) - - const jobRunID = validator.validated.id - const url = 'https://core-api.real-time-attest.trustexplorer.io/trusttoken/TrueUSD' - const field = validator.validated.data.field || 'totalTrust' - - Requester.request(url, customError) - .then((response) => { - response.data.result = Requester.validateResultNumber(response.data, ['responseData', field]) - callback(response.status, Requester.success(jobRunID, response)) - }) - .catch((error) => callback(500, Requester.errored(jobRunID, error))) -} - -module.exports.execute = execute diff --git a/trueusd/index.js b/trueusd/index.js deleted file mode 100644 index d5dfd92b9f..0000000000 --- a/trueusd/index.js +++ /dev/null @@ -1,4 +0,0 @@ -const { expose } = require('@chainlink/ea-bootstrap') -const { execute } = require('./adapter') - -module.exports = expose(execute) diff --git a/trueusd/package.json b/trueusd/package.json index ab92392004..664fe0529b 100644 --- a/trueusd/package.json +++ b/trueusd/package.json @@ -1,15 +1,45 @@ { "name": "@chainlink/trueusd-adapter", - "version": "0.0.3", + "version": "0.0.4", + "description": "Chainlink TrueUSD adapter.", + "keywords": [ + "Chainlink", + "LINK", + "blockchain", + "oracle" + ], + "main": "dist/index.js", + "types": "dist/index.d.ts", + "files": [ + "dist" + ], + "repository": { + "url": "https://github.com/smartcontractkit/external-adapters-js", + "type": "git" + }, "license": "MIT", - "main": "index.js", "scripts": { - "server": "node -e 'require(\"./index.js\").server()'", + "prepublishOnly": "yarn build && yarn test:unit", + "setup": "yarn build", + "build": "tsc -b", "lint": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx", "lint:fix": "eslint --ignore-path ../.eslintignore . --ext .js,.jsx,.ts,.tsx --fix", - "test": "yarn _mocha --timeout 0", - "test:unit": "yarn _mocha --grep @integration --invert --timeout 0", - "test:integration": "yarn _mocha --grep @integration --timeout 0" + "test": "mocha --exit --timeout 3000 -r ts-node/register 'test/**/*.test.ts'", + "test:unit": "mocha --exit --grep @integration --invert -r ts-node/register 'test/**/*.test.ts'", + "test:integration": "mocha --exit --timeout 3000 --grep @integration -r ts-node/register 'test/**/*.test.ts'", + "server": "node -e 'require(\"./index.js\").server()'", + "server:dist": "node -e 'require(\"./dist/index.js\").server()'", + "start": "yarn server:dist" + }, + "devDependencies": { + "@types/chai": "^4.2.11", + "@types/express": "^4.17.6", + "@types/mocha": "^7.0.2", + "@types/node": "^14.0.13", + "@typescript-eslint/eslint-plugin": "^3.9.0", + "@typescript-eslint/parser": "^3.9.0", + "ts-node": "^8.10.2", + "typescript": "^3.9.7" }, "dependencies": {} } diff --git a/trueusd/src/adapter.ts b/trueusd/src/adapter.ts new file mode 100644 index 0000000000..5c2e05b305 --- /dev/null +++ b/trueusd/src/adapter.ts @@ -0,0 +1,35 @@ +import { Requester, Validator, AdapterError } from '@chainlink/external-adapter' +import { Config, ExecuteWithConfig, ExecuteFactory } from '@chainlink/types' +import { makeConfig, DEFAULT_ENDPOINT } from './config' +import { trueusd } from './endpoint' + +const inputParams = { + endpoint: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, inputParams) + if (validator.error) throw validator.error + + Requester.logConfig(config) + + const jobRunID = validator.validated.id + const endpoint = validator.validated.data.endpoint || DEFAULT_ENDPOINT + + switch (endpoint) { + case trueusd.NAME: { + return await trueusd.execute(request, config) + } + default: { + throw new AdapterError({ + jobRunID, + message: `Endpoint ${endpoint} not supported.`, + statusCode: 400, + }) + } + } +} + +export const makeExecute: ExecuteFactory = (config) => { + return async (request) => execute(request, config || makeConfig()) +} diff --git a/trueusd/src/config.ts b/trueusd/src/config.ts new file mode 100644 index 0000000000..022c293c2c --- /dev/null +++ b/trueusd/src/config.ts @@ -0,0 +1,11 @@ +import { Requester } from '@chainlink/external-adapter' +import { Config } from '@chainlink/types' + +export const DEFAULT_ENDPOINT = 'trueusd' +export const DEFAULT_BASE_URL = 'https://core-api.real-time-attest.trustexplorer.io' + +export const makeConfig = (prefix?: string): Config => { + const config = Requester.getDefaultConfig(prefix) + config.api.baseURL = config.api.baseURL || DEFAULT_BASE_URL + return config +} diff --git a/trueusd/src/endpoint/index.ts b/trueusd/src/endpoint/index.ts new file mode 100644 index 0000000000..a0f6f6754a --- /dev/null +++ b/trueusd/src/endpoint/index.ts @@ -0,0 +1 @@ +export * as trueusd from './trueusd' diff --git a/trueusd/src/endpoint/trueusd.ts b/trueusd/src/endpoint/trueusd.ts new file mode 100644 index 0000000000..68a23d4d9a --- /dev/null +++ b/trueusd/src/endpoint/trueusd.ts @@ -0,0 +1,30 @@ +import { Requester, Validator } from '@chainlink/external-adapter' +import { ExecuteWithConfig, Config } from '@chainlink/types' + +export const NAME = 'trueusd' + +const customError = (data: any) => data.Response === 'Error' + +const customParams = { + field: false, +} + +export const execute: ExecuteWithConfig = async (request, config) => { + const validator = new Validator(request, customParams) + if (validator.error) throw validator.error + + const jobRunID = validator.validated.id + const field = validator.validated.data.field || 'totalTrust' + const url = '/trusttoken/TrueUSD' + + const options = { ...config.api, url } + + const response = await Requester.request(options, customError) + const result = Requester.validateResultNumber(response.data, ['responseData', field]) + + return Requester.success(jobRunID, { + data: config.verbose ? { ...response.data, result } : { result }, + result, + status: 200, + }) +} diff --git a/trueusd/src/index.ts b/trueusd/src/index.ts new file mode 100644 index 0000000000..f0291472f0 --- /dev/null +++ b/trueusd/src/index.ts @@ -0,0 +1,7 @@ +import { expose } from '@chainlink/ea-bootstrap' +import { makeExecute } from './adapter' +import { makeConfig } from './config' + +const NAME = 'TRUEUSD' + +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/trueusd/test/adapter_test.js b/trueusd/test/adapter_test.js deleted file mode 100644 index ca05ce4055..0000000000 --- a/trueusd/test/adapter_test.js +++ /dev/null @@ -1,53 +0,0 @@ -const { assert } = require('chai') -const { assertSuccess, assertError } = require('@chainlink/adapter-test-helpers') -const { execute } = require('../adapter') - -describe('execute', () => { - const jobID = '1' - - context('successful calls @integration', () => { - const requests = [ - { - name: 'id not supplied', - testData: { data: { field: 'totalTrust' } }, - }, - { - name: 'id is supplied', - testData: { id: jobID, data: { field: 'totalTrust' } }, - }, - { - name: 'trust supply', - testData: { id: jobID, data: { field: 'totalToken' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertSuccess({ expected: 200, actual: statusCode }, data, jobID) - assert.isAbove(data.result, 0) - assert.isAbove(data.data.result, 0) - done() - }) - }) - }) - }) - - context('error calls @integration', () => { - const requests = [ - { - name: 'unknown field', - testData: { id: jobID, data: { field: 'not_real' } }, - }, - ] - - requests.forEach((req) => { - it(`${req.name}`, (done) => { - execute(req.testData, (statusCode, data) => { - assertError({ expected: 500, actual: statusCode }, data, jobID) - done() - }) - }) - }) - }) -}) diff --git a/trueusd/test/trueusd.test.ts b/trueusd/test/trueusd.test.ts new file mode 100644 index 0000000000..ca02f2f3c6 --- /dev/null +++ b/trueusd/test/trueusd.test.ts @@ -0,0 +1,56 @@ +import { assert } from 'chai' +import { Requester } from '@chainlink/external-adapter' +import { assertSuccess, assertError } from '@chainlink/adapter-test-helpers' +import { AdapterRequest } from '@chainlink/types' +import { makeExecute } from '../src/adapter' + +describe('execute', () => { + const jobID = '1' + const execute = makeExecute() + + context('successful calls @integration', () => { + const requests = [ + { + name: 'id not supplied', + testData: { data: {} }, + }, + { + name: 'id is supplied', + testData: { id: jobID, data: { field: 'totalTrust' } }, + }, + { + name: 'trust supply', + testData: { id: jobID, data: { field: 'totalToken' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + const data = await execute(req.testData as AdapterRequest) + assertSuccess({ expected: 200, actual: data.statusCode }, data, jobID) + assert.isAbove(data.result, 0) + assert.isAbove(data.data.result, 0) + }) + }) + }) + + context('error calls @integration', () => { + const requests = [ + { + name: 'unknown field', + testData: { id: jobID, data: { field: 'not_real' } }, + }, + ] + + requests.forEach((req) => { + it(`${req.name}`, async () => { + try { + await execute(req.testData as AdapterRequest) + } catch (error) { + const errorResp = Requester.errored(jobID, error) + assertError({ expected: 500, actual: errorResp.statusCode }, errorResp, jobID) + } + }) + }) + }) +}) diff --git a/trueusd/tsconfig.json b/trueusd/tsconfig.json new file mode 100644 index 0000000000..3b4ccf41fa --- /dev/null +++ b/trueusd/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "src", + "typeRoots": ["../node_modules/@types", "../typings", "./typings"] + }, + "include": ["src/**/*"], + "exclude": ["dist", "**/*.spec.ts", "**/*.test.ts"] +} diff --git a/typings/@chainlink/index.d.ts b/typings/@chainlink/index.d.ts index 2afa6b78d7..78e1876ee4 100644 --- a/typings/@chainlink/index.d.ts +++ b/typings/@chainlink/index.d.ts @@ -65,13 +65,6 @@ declare module '@chainlink/types' { } /* BOOTSTRAP */ - // TODO: clean this ASAP - export type WrappedAdapterResponse = { - statusCode: number - data: AdapterResponse | AdapterErrorResponse - } - export type ExecuteWrappedResponse = (input: AdapterRequest) => Promise - export type ExecuteSync = (input: AdapterRequest, callback: Callback) => void export type Execute = (input: AdapterRequest) => Promise @@ -102,7 +95,13 @@ declare module '@chainlink/types' { chain?: string warning?: string } + + /* INPUT TYPE VALIDATIONS */ + export type Override = Map> + } declare module '@chainlink/ea-bootstrap' declare module '@chainlink/external-adapter' +declare module 'synthetix' declare module 'object-path' +declare module 'lodash' diff --git a/wbtc-address-set/package.json b/wbtc-address-set/package.json index ac01facf91..8d5d4d51ce 100644 --- a/wbtc-address-set/package.json +++ b/wbtc-address-set/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/wbtc-address-set-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink adapter to query wBTC address set.", "keywords": [ "Chainlink", diff --git a/wbtc-address-set/src/index.ts b/wbtc-address-set/src/index.ts index 7d4690864d..f30da744b3 100644 --- a/wbtc-address-set/src/index.ts +++ b/wbtc-address-set/src/index.ts @@ -1,7 +1,7 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeExecute } from './adapter' import { makeConfig } from './config' const NAME = 'WBTC' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/xbto/package.json b/xbto/package.json index d5c2b27e6d..68f6654bea 100644 --- a/xbto/package.json +++ b/xbto/package.json @@ -1,6 +1,6 @@ { "name": "@chainlink/xbto-adapter", - "version": "0.0.3", + "version": "0.0.4", "description": "Chainlink xbto adapter.", "keywords": [ "Chainlink", diff --git a/xbto/src/index.ts b/xbto/src/index.ts index be3e72eac4..1207dc9dc9 100644 --- a/xbto/src/index.ts +++ b/xbto/src/index.ts @@ -1,6 +1,6 @@ -import { expose, util } from '@chainlink/ea-bootstrap' +import { expose } from '@chainlink/ea-bootstrap' import { makeConfig, makeExecute } from './adapter' const NAME = 'XBTO' -export = { NAME, makeExecute, makeConfig, ...expose(util.wrapExecute(makeExecute())) } +export = { NAME, makeExecute, makeConfig, ...expose(makeExecute()) } diff --git a/yarn.lock b/yarn.lock index 2ed19518ec..baeb906225 100644 --- a/yarn.lock +++ b/yarn.lock @@ -24,31 +24,38 @@ resolved "https://registry.yarnpkg.com/@authereum/starkware-types/-/starkware-types-1.2.0-beta.1.tgz#d8e710865bd1f1be1a838da30041c3afd2124844" integrity sha512-7TOskv42zRtKItSTMnKdgNdXWxP0JyydjwFiibGeuDDFJ3bGB4iNGEpwsCx7DThKm95zge7Bap7BRsuwL/yjhw== -"@babel/code-frame@^7.0.0": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.10.4.tgz#168da1a36e90da68ae8d49c0f1b48c7c6249213a" - integrity sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg== +"@babel/code-frame@7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f" + integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw== dependencies: "@babel/highlight" "^7.10.4" -"@babel/helper-validator-identifier@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz#a78c7a7251e01f616512d31b10adcf52ada5e0d2" - integrity sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw== +"@babel/helper-validator-identifier@^7.12.11": + version "7.12.11" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" + integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== "@babel/highlight@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.10.4.tgz#7d1bdfd65753538fabe6c38596cdb76d9ac60143" - integrity sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA== + version "7.12.13" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.12.13.tgz#8ab538393e00370b26271b01fa08f7f27f2e795c" + integrity sha512-kocDQvIbgMKlWxXe9fof3TQ+gkIPOUSEYhJjqUjvKMez3krV7vbzYCDq39Oj11UAVK7JqPVGQPlgE85dPNlQww== dependencies: - "@babel/helper-validator-identifier" "^7.10.4" + "@babel/helper-validator-identifier" "^7.12.11" chalk "^2.0.0" js-tokens "^4.0.0" "@babel/runtime@^7.3.1": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" - integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== + version "7.12.17" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.17.tgz#9821f8de0f8d518624198b7382b74bd71b17f75c" + integrity sha512-nEvWif7aHm4O0lTT2p4kYCfYwsGSBiWA8XmAqovusBDKugpUy3BVggAjJL4iFWIwrFJktay2VLtAQl1/l8Xsow== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.8.4": + version "7.13.9" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.9.tgz#97dbe2116e2630c489f22e0656decd60aaa1fcee" + integrity sha512-aY2kU+xgJ3dJ1eU6FMB9EH8dIe8dmusF1xEku52joLvw6eAFN0AI+WxCLDnpev2LEejWBAy2sBvBOBAjI3zmvA== dependencies: regenerator-runtime "^0.13.4" @@ -77,10 +84,10 @@ enabled "2.0.x" kuler "^2.0.0" -"@eslint/eslintrc@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.2.1.tgz#f72069c330461a06684d119384435e12a5d76e3c" - integrity sha512-XRUeBZ5zBWLYgSANMpThFddrZZkEbGHgUdt5UJjZfnlN9BGCiUBrf+nvbRupSjMvqzwnQN0qwCmOxITt1cfywA== +"@eslint/eslintrc@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-0.3.0.tgz#d736d6963d7003b6514e6324bec9c602ac340318" + integrity sha512-1JTKgrOKAHVivSvOYw+sJOunkBjUOvjqWk1DPja7ZFhIS2mX/4EgTT8M7eTK9jrKhL/FvXXEbQwIs3pg1xp3dg== dependencies: ajv "^6.12.4" debug "^4.1.1" @@ -89,7 +96,7 @@ ignore "^4.0.6" import-fresh "^3.2.1" js-yaml "^3.13.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" strip-json-comments "^3.1.1" @@ -100,6 +107,13 @@ dependencies: ethers "5.0.0" +"@eth-optimism/watcher@0.0.1-alpha.8": + version "0.0.1-alpha.8" + resolved "https://registry.yarnpkg.com/@eth-optimism/watcher/-/watcher-0.0.1-alpha.8.tgz#db8179cf9dd38ad20ffe7aac799353e6e2312221" + integrity sha512-UICItZmRtQ4U2Ae8D56to0MZskFVdKOej3srcDg7PgAi/HqDetoyt5eutBPVgABiiIMTWvkoh6EsbgRJRPjWNg== + dependencies: + ethers "5.0.0" + "@ethersproject/abi@5.0.0-beta.153": version "5.0.0-beta.153" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.0-beta.153.tgz#43a37172b33794e4562999f6e2d555b7599a8eee" @@ -115,7 +129,7 @@ "@ethersproject/properties" ">=5.0.0-beta.131" "@ethersproject/strings" ">=5.0.0-beta.130" -"@ethersproject/abi@5.0.10", "@ethersproject/abi@^5.0.10": +"@ethersproject/abi@5.0.10": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.10.tgz#c32baa919ad0e5cddabb2f3a8aed0eaeeed7fa4a" integrity sha512-cfC3lGgotfxX3SMri4+CisOPwignoj/QGHW9J29spC4R4Qqcnk/SYuVkPFBMdLbvBp3f/pGiVqPNwont0TSXhg== @@ -130,10 +144,25 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/abi@^5.0.0", "@ethersproject/abi@^5.0.5": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.9.tgz#738c1c557e56d8f395a5a27caef9b0449bc85a10" - integrity sha512-ily2OufA2DTrxkiHQw5GqbkMSnNKuwZBqKsajtT0ERhZy1r9w2CpW1bmtRMIGzaqQxCdn/GEoFogexk72cBBZQ== +"@ethersproject/abi@5.0.12", "@ethersproject/abi@^5.0.0", "@ethersproject/abi@^5.0.10": + version "5.0.12" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.12.tgz#9aebe6aedc05ce45bb6c41b06d80bd195b7de77c" + integrity sha512-Ujr/3bwyYYjXLDQfebeiiTuvOw9XtUKM8av6YkoBeMXyGQM9GkjrQlwJMNwGTmqjATH/ZNbRgCh98GjOLiIB1Q== + dependencies: + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/hash" "^5.0.10" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/strings" "^5.0.8" + +"@ethersproject/abi@5.0.7": + version "5.0.7" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.0.7.tgz#79e52452bd3ca2956d0e1c964207a58ad1a0ee7b" + integrity sha512-Cqktk+hSIckwP/W8O47Eef60VwmoSC/L3lY0+dIBhQPCNn9E4V7rwmm2aFrNRRDJfFlGuZ1khkQUOc3oBX+niw== dependencies: "@ethersproject/address" "^5.0.4" "@ethersproject/bignumber" "^5.0.7" @@ -145,7 +174,7 @@ "@ethersproject/properties" "^5.0.3" "@ethersproject/strings" "^5.0.4" -"@ethersproject/abstract-provider@5.0.8", "@ethersproject/abstract-provider@^5.0.8": +"@ethersproject/abstract-provider@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.8.tgz#880793c29bfed33dff4c2b2be7ecb9ba966d52c0" integrity sha512-fqJXkewcGdi8LogKMgRyzc/Ls2js07yor7+g9KfPs09uPOcQLg7cc34JN+lk34HH9gg2HU0DIA5797ZR8znkfw== @@ -158,20 +187,20 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/web" "^5.0.12" -"@ethersproject/abstract-provider@^5.0.0", "@ethersproject/abstract-provider@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.7.tgz#04ee3bfe43323384e7fecf6c774975b8dec4bdc9" - integrity sha512-NF16JGn6M0zZP5ZS8KtDL2Rh7yHxZbUjBIHLNHMm/0X0BephhjUWy8jqs/Zks6kDJRzNthgmPVy41Ec0RYWPYA== +"@ethersproject/abstract-provider@5.0.9", "@ethersproject/abstract-provider@^5.0.0", "@ethersproject/abstract-provider@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.0.9.tgz#a55410b73e3994842884eb82b1f43e3a9f653eea" + integrity sha512-X9fMkqpeu9ayC3JyBkeeZhn35P4xQkpGX/l+FrxDtEW9tybf/UWXSMi8bGThpPtfJ6q6U2LDetXSpSwK4TfYQQ== dependencies: - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/networks" "^5.0.3" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/transactions" "^5.0.5" - "@ethersproject/web" "^5.0.6" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/networks" "^5.0.7" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/transactions" "^5.0.9" + "@ethersproject/web" "^5.0.12" -"@ethersproject/abstract-signer@5.0.11", "@ethersproject/abstract-signer@^5.0.10": +"@ethersproject/abstract-signer@5.0.11": version "5.0.11" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.11.tgz#675da9ec168905c60ee79a6da95f7157ca956f46" integrity sha512-RKOgPSEYafknA62SrD3OCK42AllHE4YBfKYXyQeM+sBP7Nq3X5FpzeoY4uzC43P4wIhmNoTHCKQuwnX7fBqb6Q== @@ -182,18 +211,29 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" -"@ethersproject/abstract-signer@^5.0.0", "@ethersproject/abstract-signer@^5.0.4", "@ethersproject/abstract-signer@^5.0.6": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.9.tgz#238ddc06031aeb9dfceee2add965292d7dd1acbf" - integrity sha512-CM5UNmXQaA03MyYARFDDRjHWBxujO41tVle7glf5kHcQsDDULgqSVpkliLJMtPzZjOKFeCVZBHybTZDEZg5zzg== +"@ethersproject/abstract-signer@5.0.13", "@ethersproject/abstract-signer@^5.0.0", "@ethersproject/abstract-signer@^5.0.10": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.0.13.tgz#59b4d0367d6327ec53bc269c6730c44a4a3b043c" + integrity sha512-VBIZEI5OK0TURoCYyw0t3w+TEO4kdwnI9wvt4kqUwyxSn3YCRpXYVl0Xoe7XBR/e5+nYOi2MyFGJ3tsFwONecQ== dependencies: - "@ethersproject/abstract-provider" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" + "@ethersproject/abstract-provider" "^5.0.8" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" -"@ethersproject/address@5.0.9", "@ethersproject/address@^5.0.9": +"@ethersproject/address@5.0.10", "@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.0", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.9": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.10.tgz#2bc69fdff4408e0570471cd19dee577ab06a10d0" + integrity sha512-70vqESmW5Srua1kMDIN6uVfdneZMaMyRYH4qPvkAXGkbicrCOsA9m01vIloA4wYiiF+HLEfL1ENKdn5jb9xiAw== + dependencies: + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/rlp" "^5.0.7" + +"@ethersproject/address@5.0.9": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.9.tgz#347ef30dc8243c682574a3f23ff63f73c8f8cbf1" integrity sha512-gKkmbZDMyGbVjr8nA5P0md1GgESqSGH7ILIrDidPdNXBl4adqbuA3OAuZx/O2oGpL6PtJ9BDa0kHheZ1ToHU3w== @@ -204,32 +244,21 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/rlp" "^5.0.7" -"@ethersproject/address@>=5.0.0-beta.128", "@ethersproject/address@^5.0.0", "@ethersproject/address@^5.0.4", "@ethersproject/address@^5.0.5": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.0.8.tgz#0c551659144a5a7643c6bea337149d410825298f" - integrity sha512-V87DHiZMZR6hmFYmoGaHex0D53UEbZpW75uj8AqPbjYUmi65RB4N2LPRcJXuWuN2R0Y2CxkvW6ArijWychr5FA== - dependencies: - "@ethersproject/bignumber" "^5.0.10" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/rlp" "^5.0.3" - -"@ethersproject/base64@5.0.7", "@ethersproject/base64@^5.0.7": +"@ethersproject/base64@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.7.tgz#d5da73699b4a33dc92bd8e5056ad1880b262057d" integrity sha512-S5oh5DVfCo06xwJXT8fQC68mvJfgScTl2AXvbYMsHNfIBTDb084Wx4iA9MNlEReOv6HulkS+gyrUM/j3514rSw== dependencies: "@ethersproject/bytes" "^5.0.9" -"@ethersproject/base64@^5.0.0", "@ethersproject/base64@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.6.tgz#26311ebf29ea3d0b9c300ccf3e1fdc44b7481516" - integrity sha512-HwrGn8YMiUf7bcdVvB4NJ+eWT0BtEFpDtrYxVXEbR7p/XBSJjwiR7DEggIiRvxbualMKg+EZijQWJ3az2li0uw== +"@ethersproject/base64@5.0.8", "@ethersproject/base64@^5.0.0", "@ethersproject/base64@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.0.8.tgz#1bc4b4b8c59c1debf972c7164b96c0b8964a20a1" + integrity sha512-PNbpHOMgZpZ1skvQl119pV2YkCPXmZTxw+T92qX0z7zaMFPypXWTZBzim+hUceb//zx4DFjeGT4aSjZRTOYThg== dependencies: - "@ethersproject/bytes" "^5.0.4" + "@ethersproject/bytes" "^5.0.9" -"@ethersproject/basex@5.0.7", "@ethersproject/basex@^5.0.7": +"@ethersproject/basex@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.7.tgz#2f7026b12c9dee6cdc7b7bf1805461836e635495" integrity sha512-OsXnRsujGmYD9LYyJlX+cVe5KfwgLUbUJrJMWdzRWogrygXd5HvGd7ygX1AYjlu1z8W/+t2FoQnczDR/H2iBjA== @@ -237,15 +266,15 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/properties" "^5.0.7" -"@ethersproject/basex@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.6.tgz#ab95c32e48288a3d868726463506641cb1e9fb6b" - integrity sha512-Y/8dowRxBF3bsKkqEp7XN4kcFFQ0o5xxP1YyopfqkXejaOEGiD7ToQdQ0pIZpAJ5GreW56oFOTDDSO6ZcUCNYg== +"@ethersproject/basex@5.0.8", "@ethersproject/basex@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.0.8.tgz#6867fad20047aa29fbd4b880f27894ed04cc7bb8" + integrity sha512-PCVKZIShBQUqAXjJSvaCidThPvL0jaaQZcewJc0sf8Xx05BizaOS8r3jdPdpNdY+/qZtRDqwHTSKjvR/xssyLQ== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/properties" "^5.0.3" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/properties" "^5.0.7" -"@ethersproject/bignumber@5.0.13", "@ethersproject/bignumber@^5.0.13": +"@ethersproject/bignumber@5.0.13": version "5.0.13" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.13.tgz#a5466412b3b80104097b9c694f6ae827df4353fe" integrity sha512-b89bX5li6aK492yuPP5mPgRVgIxxBP7ksaBtKX5QQBsrZTpNOjf/MR4CjcUrAw8g+RQuD6kap9lPjFgY4U1/5A== @@ -254,42 +283,57 @@ "@ethersproject/logger" "^5.0.8" bn.js "^4.4.0" -"@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.0", "@ethersproject/bignumber@^5.0.10", "@ethersproject/bignumber@^5.0.7", "@ethersproject/bignumber@^5.0.8": - version "5.0.12" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.12.tgz#fe4a78667d7cb01790f75131147e82d6ea7e7cba" - integrity sha512-mbFZjwthx6vFlHG9owXP/C5QkNvsA+xHpDCkPPPdG2n1dS9AmZAL5DI0InNLid60rQWL3MXpEl19tFmtL7Q9jw== +"@ethersproject/bignumber@5.0.14", "@ethersproject/bignumber@>=5.0.0-beta.130", "@ethersproject/bignumber@^5.0.0", "@ethersproject/bignumber@^5.0.13", "@ethersproject/bignumber@^5.0.7": + version "5.0.14" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.0.14.tgz#605bc61dcbd4a8c6df8b5a7a77c0210273f3de8a" + integrity sha512-Q4TjMq9Gg3Xzj0aeJWqJgI3tdEiPiET7Y5OtNtjTAODZ2kp4y9jMNg97zVcvPedFvGROdpGDyCI77JDFodUzOw== dependencies: - "@ethersproject/bytes" "^5.0.8" - "@ethersproject/logger" "^5.0.5" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" bn.js "^4.4.0" -"@ethersproject/bytes@5.0.9", "@ethersproject/bytes@^5.0.9": +"@ethersproject/bytes@5.0.10", "@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.9": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.10.tgz#aa49afe7491ba24ff76fa33d98677351263f9ba4" + integrity sha512-vpu0v1LZ1j1s9kERQIMnVU69MyHEzUff7nqK9XuCU4vx+AM8n9lU2gj7jtJIvGSt9HzatK/6I6bWusI5nyuaTA== + dependencies: + "@ethersproject/logger" "^5.0.8" + +"@ethersproject/bytes@5.0.9": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.9.tgz#2748247402ad20df69f3a3e935dc7b58c0d75c08" integrity sha512-k+17ZViDtAugC0s7HM6rdsTWEdIYII4RPCDkPEuxKc6i40Bs+m6tjRAtCECX06wKZnrEoR9pjOJRXHJ/VLoOcA== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/bytes@>=5.0.0-beta.129", "@ethersproject/bytes@^5.0.0", "@ethersproject/bytes@^5.0.4", "@ethersproject/bytes@^5.0.8": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.0.8.tgz#cf1246a6a386086e590063a4602b1ffb6cc43db1" - integrity sha512-O+sJNVGzzuy51g+EMK8BegomqNIg+C2RO6vOt0XP6ac4o4saiq69FnjlsrNslaiMFVO7qcEHBsWJ9hx1tj1lMw== - dependencies: - "@ethersproject/logger" "^5.0.5" - -"@ethersproject/constants@5.0.8", "@ethersproject/constants@^5.0.8": +"@ethersproject/constants@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.8.tgz#50f2e23f48c0d1d0de3759ea79b68ec3e06435a1" integrity sha512-sCc73pFBsl59eDfoQR5OCEZCRv5b0iywadunti6MQIr5lt3XpwxK1Iuzd8XSFO02N9jUifvuZRrt0cY0+NBgTg== dependencies: "@ethersproject/bignumber" "^5.0.13" -"@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.0", "@ethersproject/constants@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.7.tgz#44ff979e5781b17c8c6901266896c3ee745f4e7e" - integrity sha512-cbQK1UpE4hamB52Eg6DLhJoXeQ1plSzekh5Ujir1xdREdwdsZPPXKczkrWqBBR0KyywJZHN/o/hj0w8j7scSGg== +"@ethersproject/constants@5.0.9", "@ethersproject/constants@>=5.0.0-beta.128", "@ethersproject/constants@^5.0.0", "@ethersproject/constants@^5.0.4", "@ethersproject/constants@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.0.9.tgz#81ac44c3bf612de63eb1c490b314ea1b932cda9f" + integrity sha512-2uAKH89UcaJP/Sc+54u92BtJtZ4cPgcS1p0YbB1L3tlkavwNvth+kNCUplIB1Becqs7BOZr0B/3dMNjhJDy4Dg== dependencies: - "@ethersproject/bignumber" "^5.0.7" + "@ethersproject/bignumber" "^5.0.13" + +"@ethersproject/contracts@5.0.11", "@ethersproject/contracts@^5.0.0": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.11.tgz#e6cc57698a05be2329cb2ca3d7e87686f95e438a" + integrity sha512-FTUUd/6x00dYL2VufE2VowZ7h3mAyBfCQMGwI3tKDIWka+C0CunllFiKrlYCdiHFuVeMotR65dIcnzbLn72MCw== + dependencies: + "@ethersproject/abi" "^5.0.10" + "@ethersproject/abstract-provider" "^5.0.8" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" "@ethersproject/contracts@5.0.9": version "5.0.9" @@ -306,22 +350,7 @@ "@ethersproject/logger" "^5.0.8" "@ethersproject/properties" "^5.0.7" -"@ethersproject/contracts@^5.0.0": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.0.8.tgz#71d3ba16853a1555be2e161a6741df186f81c73b" - integrity sha512-PecBL4vnsrpuks2lzzkRsOts8csJy338HNDKDIivbFmx92BVzh3ohOOv3XsoYPSXIHQvobF959W+aSk3RCZL/g== - dependencies: - "@ethersproject/abi" "^5.0.5" - "@ethersproject/abstract-provider" "^5.0.4" - "@ethersproject/abstract-signer" "^5.0.4" - "@ethersproject/address" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - -"@ethersproject/hash@5.0.10", "@ethersproject/hash@^5.0.10": +"@ethersproject/hash@5.0.10": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.10.tgz#41bf37428e8ddbc229ffd81c47af667174cb491a" integrity sha512-Tf0bvs6YFhw28LuHnhlDWyr0xfcDxSXdwM4TcskeBbmXVSKLv3bJQEEEBFUcRX0fJuslR3gCVySEaSh7vuMx5w== @@ -335,21 +364,21 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.0", "@ethersproject/hash@^5.0.4": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.8.tgz#368a60addc3c5cd18e28f78c80dc94e1bacb82d8" - integrity sha512-Qay01tcFyFreYjSMt82rOQGMfQDmLm1sj3iNNO1BhrVf840xgBZuJ7gBATERzAjTuTCHUHw9BuGwxErJUS95yg== +"@ethersproject/hash@5.0.11", "@ethersproject/hash@>=5.0.0-beta.128", "@ethersproject/hash@^5.0.0", "@ethersproject/hash@^5.0.10", "@ethersproject/hash@^5.0.4": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.0.11.tgz#da89517438bbbf8a39df56fff09f0a71669ae7a7" + integrity sha512-H3KJ9fk33XWJ2djAW03IL7fg3DsDMYjO1XijiUb1hJ85vYfhvxu0OmsU7d3tg2Uv1H1kFSo8ghr3WFQ8c+NL3g== dependencies: - "@ethersproject/abstract-signer" "^5.0.6" - "@ethersproject/address" "^5.0.5" - "@ethersproject/bignumber" "^5.0.8" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.4" - "@ethersproject/strings" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/strings" "^5.0.8" -"@ethersproject/hdnode@5.0.8", "@ethersproject/hdnode@^5.0.8": +"@ethersproject/hdnode@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.8.tgz#2b52ede921cfbce8de49da774ec5a74025fc2bb1" integrity sha512-Mscpjd7BBjxYSWghaNMwV0xrBBkOoCq6YEPRm9MgE24CiBlzzbfEB5DGq6hiZqhQaxPkdCUtKKqZi3nt9hx43g== @@ -367,25 +396,25 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/hdnode@^5.0.0", "@ethersproject/hdnode@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.7.tgz#c7bce94a337ea65e37c46bab09a83e1c1a555d99" - integrity sha512-89tphqlji4y/LNE1cSaMQ3hrBtJ4lO1qWGi2hn54LiHym85DTw+zAKbA8QgmdSdJDLGR/kc9VHaIPQ+vZQ2LkQ== +"@ethersproject/hdnode@5.0.9", "@ethersproject/hdnode@^5.0.0", "@ethersproject/hdnode@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.0.9.tgz#ce65b430d3d3f0cd3c8f9dfaaf376b55881d9dba" + integrity sha512-S5UMmIC6XfFtqhUK4uTjD8GPNzSbE+sZ/0VMqFnA3zAJ+cEFZuEyhZDYnl2ItGJzjT4jsy+uEy1SIl3baYK1PQ== dependencies: - "@ethersproject/abstract-signer" "^5.0.4" - "@ethersproject/basex" "^5.0.3" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/pbkdf2" "^5.0.3" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/sha2" "^5.0.3" - "@ethersproject/signing-key" "^5.0.4" - "@ethersproject/strings" "^5.0.4" - "@ethersproject/transactions" "^5.0.5" - "@ethersproject/wordlists" "^5.0.4" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/basex" "^5.0.7" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/pbkdf2" "^5.0.7" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/sha2" "^5.0.7" + "@ethersproject/signing-key" "^5.0.8" + "@ethersproject/strings" "^5.0.8" + "@ethersproject/transactions" "^5.0.9" + "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/json-wallets@5.0.10", "@ethersproject/json-wallets@^5.0.10": +"@ethersproject/json-wallets@5.0.10": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.10.tgz#cdc9c27cb486762a3313e25f6f2fef0eb890dbaf" integrity sha512-Ux36u+d7Dm0M5AQ+mWuHdvfGPMN8K1aaLQgwzrsD4ELTWlwRuHuQbmn7/GqeOpbfaV6POLwdYcBk2TXjlGp/IQ== @@ -404,26 +433,26 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/json-wallets@^5.0.0", "@ethersproject/json-wallets@^5.0.6": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.9.tgz#2e1708c2854c4ab764e35920bd1f44c948b95434" - integrity sha512-EWuFvJd8nu90dkmJwmJddxOYCvFvMkKBsZi8rxTme2XEZsHKOFnybVkoL23u7ZtApuEfTKmVcR2PTwgZwqDsKw== +"@ethersproject/json-wallets@5.0.11", "@ethersproject/json-wallets@^5.0.0", "@ethersproject/json-wallets@^5.0.10": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.0.11.tgz#86fdc41b7762acb443d6a896f6c61231ab2aee5d" + integrity sha512-0GhWScWUlXXb4qJNp0wmkU95QS3YdN9UMOfMSEl76CRANWWrmyzxcBVSXSBu5iQ0/W8wO+xGlJJ3tpA6v3mbIw== dependencies: - "@ethersproject/abstract-signer" "^5.0.4" - "@ethersproject/address" "^5.0.4" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/hdnode" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/pbkdf2" "^5.0.3" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/random" "^5.0.3" - "@ethersproject/strings" "^5.0.4" - "@ethersproject/transactions" "^5.0.5" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/hdnode" "^5.0.8" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/pbkdf2" "^5.0.7" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/random" "^5.0.7" + "@ethersproject/strings" "^5.0.8" + "@ethersproject/transactions" "^5.0.9" aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.0.7", "@ethersproject/keccak256@^5.0.7": +"@ethersproject/keccak256@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.7.tgz#2eedb5e4c160fcdf0079660f8ae362d7855ea943" integrity sha512-zpUBmofWvx9PGfc7IICobgFQSgNmTOGTGLUxSYqZzY/T+b4y/2o5eqf/GGmD7qnTGzKQ42YlLNo+LeDP2qe55g== @@ -431,34 +460,39 @@ "@ethersproject/bytes" "^5.0.9" js-sha3 "0.5.7" -"@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0", "@ethersproject/keccak256@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.6.tgz#5b5ba715ef1be86efde5c271f896fa0daf0e1efe" - integrity sha512-eJ4Id/i2rwrf5JXEA7a12bG1phuxjj47mPZgDUbttuNBodhSuZF2nEO5QdpaRjmlphQ8Kt9PNqY/z7lhtJptZg== +"@ethersproject/keccak256@5.0.8", "@ethersproject/keccak256@>=5.0.0-beta.127", "@ethersproject/keccak256@^5.0.0", "@ethersproject/keccak256@^5.0.3", "@ethersproject/keccak256@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.0.8.tgz#13aaf69e1c8bd15fc59a2ebd055c0878f2a059c8" + integrity sha512-zoGbwXcWWs9MX4NOAZ7N0hhgIRl4Q/IO/u9c/RHRY4WqDy3Ywm0OLamEV53QDwhjwn3YiiVwU1Ve5j7yJ0a/KQ== dependencies: - "@ethersproject/bytes" "^5.0.4" + "@ethersproject/bytes" "^5.0.9" js-sha3 "0.5.7" -"@ethersproject/logger@5.0.8", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.0.8": +"@ethersproject/logger@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.8.tgz#135c1903d35c878265f3cbf2b287042c4c20d5d4" integrity sha512-SkJCTaVTnaZ3/ieLF5pVftxGEFX56pTH+f2Slrpv7cU0TNpUZNib84QQdukd++sWUp/S7j5t5NW+WegbXd4U/A== -"@ethersproject/networks@5.0.7", "@ethersproject/networks@^5.0.7": +"@ethersproject/logger@5.0.9", "@ethersproject/logger@>=5.0.0-beta.129", "@ethersproject/logger@^5.0.0", "@ethersproject/logger@^5.0.5", "@ethersproject/logger@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.0.9.tgz#0e6a0b3ecc938713016954daf4ac7967467aa763" + integrity sha512-kV3Uamv3XOH99Xf3kpIG3ZkS7mBNYcLDM00JSDtNgNB4BihuyxpQzIZPRIDmRi+95Z/R1Bb0X2kUNHa/kJoVrw== + +"@ethersproject/networks@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.7.tgz#8d06e41197b27c2404d89a29ca21f741a01acbfc" integrity sha512-dI14QATndIcUgcCBL1c5vUr/YsI5cCHLN81rF7PU+yS7Xgp2/Rzbr9+YqpC6NBXHFUASjh6GpKqsVMpufAL0BQ== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/networks@^5.0.0", "@ethersproject/networks@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.6.tgz#4d6586bbebfde1c027504ebf6dfb783b29c3803a" - integrity sha512-2Cg1N5109zzFOBfkyuPj+FfF7ioqAsRffmybJ2lrsiB5skphIAE72XNSCs4fqktlf+rwSh/5o/UXRjXxvSktZw== +"@ethersproject/networks@5.0.8", "@ethersproject/networks@^5.0.0", "@ethersproject/networks@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.0.8.tgz#37e6f8c058f2d540373ea5939056cd3de069132e" + integrity sha512-PYpptlO2Tu5f/JEBI5hdlMds5k1DY1QwVbh3LKPb3un9dQA2bC51vd2/gRWAgSBpF3kkmZOj4FhD7ATLX4H+DA== dependencies: - "@ethersproject/logger" "^5.0.5" + "@ethersproject/logger" "^5.0.8" -"@ethersproject/pbkdf2@5.0.7", "@ethersproject/pbkdf2@^5.0.7": +"@ethersproject/pbkdf2@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.7.tgz#a36fdb7327760ec0096857053e01c923a63417da" integrity sha512-0SNLNixPMqnosH6pyc4yPiUu/C9/Jbu+f6I8GJW9U2qNpMBddmRJviwseoha5Zw1V+Aw0Z/yvYyzIIE8yPXqLA== @@ -466,27 +500,27 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/sha2" "^5.0.7" -"@ethersproject/pbkdf2@^5.0.0", "@ethersproject/pbkdf2@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.6.tgz#105dbfb08cd5fcf33869b42bfdc35a3ebd978cbd" - integrity sha512-CUYciSxR/AaCoKMJk3WUW+BDhR41G3C+O9lOeZ4bR1wDhLKL2Z8p0ciF5XDEiVbmI4CToW6boVKybeVMdngRrg== +"@ethersproject/pbkdf2@5.0.8", "@ethersproject/pbkdf2@^5.0.0", "@ethersproject/pbkdf2@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.0.8.tgz#06a086b1ac04c75e6846afd6cf6170a49a634411" + integrity sha512-UlmAMGbIPaS2xXsI38FbePVTfJMuU9jnwcqVn3p88HxPF4kD897ha+l3TNsBqJqf32UbQL5GImnf1oJkSKq4vQ== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/sha2" "^5.0.3" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/sha2" "^5.0.7" -"@ethersproject/properties@5.0.7", "@ethersproject/properties@^5.0.7": +"@ethersproject/properties@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.7.tgz#951d11ba592ff90bbe8ec34c5a03a5157e3b3360" integrity sha512-812H1Rus2vjw0zbasfDI1GLNPDsoyX1pYqiCgaR1BuyKxUTbwcH1B+214l6VGe1v+F6iEVb7WjIwMjKhb4EUsg== dependencies: "@ethersproject/logger" "^5.0.8" -"@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.4": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.6.tgz#44d82aaa294816fd63333e7def42426cf0e87b3b" - integrity sha512-a9DUMizYhJ0TbtuDkO9iYlb2CDlpSKqGPDr+amvlZhRspQ6jbl5Eq8jfu4SCcGlcfaTbguJmqGnyOGn1EFt6xA== +"@ethersproject/properties@5.0.8", "@ethersproject/properties@>=5.0.0-beta.131", "@ethersproject/properties@^5.0.0", "@ethersproject/properties@^5.0.3", "@ethersproject/properties@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.0.8.tgz#e45d28d25402c73394873dbf058f856c966cae01" + integrity sha512-zEnLMze2Eu2VDPj/05QwCwMKHh506gpT9PP9KPVd4dDB+5d6AcROUYVLoIIQgBYK7X/Gw0UJmG3oVtnxOQafAw== dependencies: - "@ethersproject/logger" "^5.0.5" + "@ethersproject/logger" "^5.0.8" "@ethersproject/providers@5.0.19": version "5.0.19" @@ -513,32 +547,32 @@ bech32 "1.1.4" ws "7.2.3" -"@ethersproject/providers@^5.0.0": - version "5.0.17" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.17.tgz#f380e7831149e24e7a1c6c9b5fb1d6dfc729d024" - integrity sha512-bJnvs5X7ttU5x2ekGJYG7R3Z+spZawLFfR0IDsbaMDLiCwZOyrgk+VTBU7amSFLT0WUhWFv8WwSUB+AryCQG1Q== +"@ethersproject/providers@5.0.23", "@ethersproject/providers@^5.0.0": + version "5.0.23" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.0.23.tgz#1e26512303d60bbd557242532fdb5fa3c5d5fb73" + integrity sha512-eJ94z2tgPaUgUmxwd3BVkIzkgkbNIkY6wRPVas04LVaBTycObQbgj794aaUu2bfk7+Bn2B/gjUZtJW1ybxh9/A== dependencies: - "@ethersproject/abstract-provider" "^5.0.4" - "@ethersproject/abstract-signer" "^5.0.4" - "@ethersproject/address" "^5.0.4" - "@ethersproject/basex" "^5.0.3" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/hash" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/networks" "^5.0.3" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/random" "^5.0.3" - "@ethersproject/rlp" "^5.0.3" - "@ethersproject/sha2" "^5.0.3" - "@ethersproject/strings" "^5.0.4" - "@ethersproject/transactions" "^5.0.5" - "@ethersproject/web" "^5.0.6" + "@ethersproject/abstract-provider" "^5.0.8" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/basex" "^5.0.7" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/hash" "^5.0.10" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/networks" "^5.0.7" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/random" "^5.0.7" + "@ethersproject/rlp" "^5.0.7" + "@ethersproject/sha2" "^5.0.7" + "@ethersproject/strings" "^5.0.8" + "@ethersproject/transactions" "^5.0.9" + "@ethersproject/web" "^5.0.12" bech32 "1.1.4" ws "7.2.3" -"@ethersproject/random@5.0.7", "@ethersproject/random@^5.0.7": +"@ethersproject/random@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.7.tgz#e364268ce68bf6d300c36d654e622fd9d26b3a86" integrity sha512-PxSRWwN3s+FH9AWMZU6AcWJsNQ9KzqKV6NgdeKPtxahdDjCuXxTAuzTZNXNRK+qj+Il351UnweAGd+VuZcOAlQ== @@ -546,15 +580,15 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" -"@ethersproject/random@^5.0.0", "@ethersproject/random@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.6.tgz#9be80a1065f2b8e6f321dccb3ebeb4886cac9ea4" - integrity sha512-8nsVNaZvZ9OD5NXfzE4mmz8IH/1DYJbAR95xpRxZkIuNmfn6QlMp49ccJYZWGhs6m0Zj2+FXjx3pzXfYlo9/dA== +"@ethersproject/random@5.0.8", "@ethersproject/random@^5.0.0", "@ethersproject/random@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.0.8.tgz#8d3726be48e95467abce9b23c93adbb1de009dda" + integrity sha512-4rHtotmd9NjklW0eDvByicEkL+qareIyFSbG1ShC8tPJJSAC0g55oQWzw+3nfdRCgBHRuEE7S8EcPcTVPvZ9cA== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" -"@ethersproject/rlp@5.0.7", "@ethersproject/rlp@^5.0.7": +"@ethersproject/rlp@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.7.tgz#cfa4fa6415960a435b7814e1a29bdfea657e2b6e" integrity sha512-ulUTVEuV7PT4jJTPpfhRHK57tkLEDEY9XSYJtrSNHOqdwMvH0z7BM2AKIMq4LVDlnu4YZASdKrkFGEIO712V9w== @@ -562,15 +596,15 @@ "@ethersproject/bytes" "^5.0.9" "@ethersproject/logger" "^5.0.8" -"@ethersproject/rlp@^5.0.0", "@ethersproject/rlp@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.6.tgz#29f9097348a3c330811997433b7df89ab51cd644" - integrity sha512-M223MTaydfmQSsvqAl0FJZDYFlSqt6cgbhnssLDwqCKYegAHE16vrFyo+eiOapYlt32XAIJm0BXlqSunULzZuQ== +"@ethersproject/rlp@5.0.8", "@ethersproject/rlp@^5.0.0", "@ethersproject/rlp@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.0.8.tgz#ff54e206d0ae28640dd054f2bcc7070f06f9dfbe" + integrity sha512-E4wdFs8xRNJfzNHmnkC8w5fPeT4Wd1U2cust3YeT16/46iSkLT8nn8ilidC6KhR7hfuSZE4UqSPzyk76p7cdZg== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" -"@ethersproject/sha2@5.0.7", "@ethersproject/sha2@^5.0.7": +"@ethersproject/sha2@5.0.7": version "5.0.7" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.7.tgz#ef9f18770c9f90a6cfd73840b0c3400910219099" integrity sha512-MbUqz68hhp5RsaZdqi1eg1rrtiqt5wmhRYqdA7MX8swBkzW2KiLgK+Oh25UcWhUhdi1ImU9qrV6if5j0cC7Bxg== @@ -579,16 +613,26 @@ "@ethersproject/logger" "^5.0.8" hash.js "1.1.3" -"@ethersproject/sha2@^5.0.0", "@ethersproject/sha2@^5.0.3": - version "5.0.6" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.6.tgz#175116dc10b866a0a381f6316d094bcc510bee3c" - integrity sha512-30gypDLkfkP5gE3llqi0jEuRV8m4/nvzeqmqMxiihZ7veFQHqDaGpyFeHzFim+qGeH9fq0lgYjavLvwW69+Fkw== +"@ethersproject/sha2@5.0.8", "@ethersproject/sha2@^5.0.0", "@ethersproject/sha2@^5.0.7": + version "5.0.8" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.0.8.tgz#9903c67e562739d8b312820b0a265b9c9bf35fc3" + integrity sha512-ILP1ZgyvDj4rrdE+AXrTv9V88m7x87uga2VZ/FeULKPumOEw/4bGnJz/oQ8zDnDvVYRCJ+48VaQBS2CFLbk1ww== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" hash.js "1.1.3" -"@ethersproject/signing-key@5.0.8", "@ethersproject/signing-key@^5.0.8": +"@ethersproject/signing-key@5.0.10", "@ethersproject/signing-key@^5.0.0", "@ethersproject/signing-key@^5.0.8": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.10.tgz#05e26e04f0aa5360dc78674d7331bacea8fea5c1" + integrity sha512-w5it3GbFOvN6e0mTd5gDNj+bwSe6L9jqqYjU+uaYS8/hAEp4qYLk5p8ZjbJJkNn7u1p0iwocp8X9oH/OdK8apA== + dependencies: + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + elliptic "6.5.4" + +"@ethersproject/signing-key@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.8.tgz#156522e542916b9aa9135527b40c5b6f9235af02" integrity sha512-YKxQM45eDa6WAD+s3QZPdm1uW1MutzVuyoepdRRVmMJ8qkk7iOiIhUkZwqKLNxKzEJijt/82ycuOREc9WBNAKg== @@ -598,16 +642,6 @@ "@ethersproject/properties" "^5.0.7" elliptic "6.5.3" -"@ethersproject/signing-key@^5.0.0", "@ethersproject/signing-key@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.0.7.tgz#d03bfc5f565efb962bafebf8e6965e70d1c46d31" - integrity sha512-JYndnhFPKH0daPcIjyhi+GMcw3srIHkQ40hGRe6DA0CdGrpMfgyfSYDQ2D8HL2lgR+Xm4SHfEB0qba6+sCyrvg== - dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - elliptic "6.5.3" - "@ethersproject/solidity@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.8.tgz#a260116a794bc97558d89e98f59831ce8d25c733" @@ -619,18 +653,18 @@ "@ethersproject/sha2" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/solidity@^5.0.0": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.7.tgz#72a3455f47a454db2dcf363992d42e9045dc7fce" - integrity sha512-dUevKUZ06p/VMLP/+cz4QUV+lA17NixucDJfm0ioWF0B3R0Lf+6wqwPchcqiAXlxkNFGIax7WNLgGMh4CkQ8iw== +"@ethersproject/solidity@5.0.9", "@ethersproject/solidity@^5.0.0": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.0.9.tgz#49100fbe9f364ac56f7ff7c726f4f3d151901134" + integrity sha512-LIxSAYEQgLRXE3mRPCq39ou61kqP8fDrGqEeNcaNJS3aLbmAOS8MZp56uK++WsdI9hj8sNsFh78hrAa6zR9Jag== dependencies: - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/sha2" "^5.0.3" - "@ethersproject/strings" "^5.0.4" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/sha2" "^5.0.7" + "@ethersproject/strings" "^5.0.8" -"@ethersproject/strings@5.0.8", "@ethersproject/strings@^5.0.8": +"@ethersproject/strings@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.8.tgz#11a1b0ed1e8417408693789839f0b5f4e323c0c9" integrity sha512-5IsdXf8tMY8QuHl8vTLnk9ehXDDm6x9FB9S9Og5IA1GYhLe5ZewydXSjlJlsqU2t9HRbfv97OJZV/pX8DVA/Hw== @@ -639,16 +673,31 @@ "@ethersproject/constants" "^5.0.8" "@ethersproject/logger" "^5.0.8" -"@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.0", "@ethersproject/strings@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.7.tgz#8dc68f794c9e2901f3b75e53b2afbcb6b6c15037" - integrity sha512-a+6T80LvmXGMOOWQTZHtGGQEg1z4v8rm8oX70KNs55YtPXI/5J3LBbVf5pyqCKSlmiBw5IaepPvs5XGalRUSZQ== +"@ethersproject/strings@5.0.9", "@ethersproject/strings@>=5.0.0-beta.130", "@ethersproject/strings@^5.0.0", "@ethersproject/strings@^5.0.4", "@ethersproject/strings@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.0.9.tgz#8e2eb2918b140231e1d1b883d77e43213a8ac280" + integrity sha512-ogxBpcUpdO524CYs841MoJHgHxEPUy0bJFDS4Ezg8My+WYVMfVAOlZSLss0Rurbeeam8CpUVDzM4zUn09SU66Q== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/logger" "^5.0.5" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/logger" "^5.0.8" -"@ethersproject/transactions@5.0.9", "@ethersproject/transactions@^5.0.9": +"@ethersproject/transactions@5.0.10", "@ethersproject/transactions@^5.0.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.9": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.10.tgz#d50cafd80d27206336f80114bc0f18bc18687331" + integrity sha512-Tqpp+vKYQyQdJQQk4M73tDzO7ODf2D42/sJOcKlDAAbdSni13v6a+31hUdo02qYXhVYwIs+ZjHnO4zKv5BNk8w== + dependencies: + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/rlp" "^5.0.7" + "@ethersproject/signing-key" "^5.0.8" + +"@ethersproject/transactions@5.0.9": version "5.0.9" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.9.tgz#ccfcc1d395b5e3ce7342545fa28bfe5541182fd6" integrity sha512-0Fu1yhdFBkrbMjenEr+39tmDxuHmaw0pe9Jb18XuKoItj7Z3p7+UzdHLr2S/okvHDHYPbZE5gtANDdQ3ZL1nBA== @@ -663,20 +712,14 @@ "@ethersproject/rlp" "^5.0.7" "@ethersproject/signing-key" "^5.0.8" -"@ethersproject/transactions@^5.0.0", "@ethersproject/transactions@^5.0.0-beta.135", "@ethersproject/transactions@^5.0.5": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.0.8.tgz#3b4d7041e13b957a9c4f131e0aea9dae7b6f5a23" - integrity sha512-i7NtOXVzUe+YSU6QufzlRrI2WzHaTmULAKHJv4duIZMLqzehCBXGA9lTpFgFdqGYcQJ7vOtNFC2BB2mSjmuXqg== +"@ethersproject/units@5.0.10", "@ethersproject/units@^5.0.0": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.10.tgz#9cca3b65cd0c92fab1bd33f2abd233546dd61987" + integrity sha512-eaiHi9ham5lbC7qpqxpae7OY/nHJUnRUnFFuEwi2VB5Nwe3Np468OAV+e+HR+jAK4fHXQE6PFBTxWGtnZuO37g== dependencies: - "@ethersproject/address" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/rlp" "^5.0.3" - "@ethersproject/signing-key" "^5.0.4" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/constants" "^5.0.8" + "@ethersproject/logger" "^5.0.8" "@ethersproject/units@5.0.9": version "5.0.9" @@ -687,15 +730,6 @@ "@ethersproject/constants" "^5.0.8" "@ethersproject/logger" "^5.0.8" -"@ethersproject/units@^5.0.0": - version "5.0.8" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.0.8.tgz#563325b20fe1eceff7b61857711d5e2b3f38fd09" - integrity sha512-3O4MaNHFs05vC5v2ZGqVFVWtO1WyqFejO78M7Qh16njo282aoMlENtVI6cn2B36zOLFXRvYt2pYx6xCG53qKzg== - dependencies: - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/constants" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/wallet@5.0.10": version "5.0.10" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.10.tgz#16ad0864d9e0e2b57fb32d768ea4161891d62727" @@ -717,28 +751,28 @@ "@ethersproject/transactions" "^5.0.9" "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/wallet@^5.0.0": - version "5.0.9" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.9.tgz#976c7d950489c40308d676869d24e59ab7b82ad1" - integrity sha512-GfpQF56PO/945SJq7Wdg5F5U6wkxaDgkAzcgGbCW6Joz8oW8MzKItkvYCzMh+j/8gJMzFncsuqX4zg2gq3J6nQ== +"@ethersproject/wallet@5.0.11", "@ethersproject/wallet@^5.0.0": + version "5.0.11" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.0.11.tgz#9891936089d1b91e22ed59f850bc344b1544bf26" + integrity sha512-2Fg/DOvUltR7aZTOyWWlQhru+SKvq2UE3uEhXSyCFgMqDQNuc2nHXh1SHJtN65jsEbjVIppOe1Q7EQMvhmeeRw== dependencies: - "@ethersproject/abstract-provider" "^5.0.4" - "@ethersproject/abstract-signer" "^5.0.4" - "@ethersproject/address" "^5.0.4" - "@ethersproject/bignumber" "^5.0.7" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/hash" "^5.0.4" - "@ethersproject/hdnode" "^5.0.4" - "@ethersproject/json-wallets" "^5.0.6" - "@ethersproject/keccak256" "^5.0.3" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/random" "^5.0.3" - "@ethersproject/signing-key" "^5.0.4" - "@ethersproject/transactions" "^5.0.5" - "@ethersproject/wordlists" "^5.0.4" + "@ethersproject/abstract-provider" "^5.0.8" + "@ethersproject/abstract-signer" "^5.0.10" + "@ethersproject/address" "^5.0.9" + "@ethersproject/bignumber" "^5.0.13" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/hash" "^5.0.10" + "@ethersproject/hdnode" "^5.0.8" + "@ethersproject/json-wallets" "^5.0.10" + "@ethersproject/keccak256" "^5.0.7" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/random" "^5.0.7" + "@ethersproject/signing-key" "^5.0.8" + "@ethersproject/transactions" "^5.0.9" + "@ethersproject/wordlists" "^5.0.8" -"@ethersproject/web@5.0.12", "@ethersproject/web@^5.0.12": +"@ethersproject/web@5.0.12": version "5.0.12" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.12.tgz#f123397c107f863c31fce5f31a97c66ec155e755" integrity sha512-gVxS5iW0bgidZ76kr7LsTxj4uzN5XpCLzvZrLp8TP+4YgxHfCeetFyQkRPgBEAJdNrexdSBayvyJvzGvOq0O8g== @@ -749,18 +783,18 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/web@^5.0.0", "@ethersproject/web@^5.0.6": - version "5.0.11" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.11.tgz#d47da612b958b4439e415782a53c8f8461522d68" - integrity sha512-x03ihbPoN1S8Gsh9WSwxkYxUIumLi02ZEKJku1C43sxBfe+mdprWyvujzYlpuoRNfWRgNhdRDKMP8JbG6MwNGA== +"@ethersproject/web@5.0.13", "@ethersproject/web@^5.0.0", "@ethersproject/web@^5.0.12": + version "5.0.13" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.0.13.tgz#5a92ac6d835d2ebce95b6b645a86668736e2f532" + integrity sha512-G3x/Ns7pQm21ALnWLbdBI5XkW/jrsbXXffI9hKNPHqf59mTxHYtlNiSwxdoTSwCef3Hn7uvGZpaSgTyxs7IufQ== dependencies: - "@ethersproject/base64" "^5.0.3" - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/strings" "^5.0.4" + "@ethersproject/base64" "^5.0.7" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/strings" "^5.0.8" -"@ethersproject/wordlists@5.0.8", "@ethersproject/wordlists@^5.0.8": +"@ethersproject/wordlists@5.0.8": version "5.0.8" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.8.tgz#593319b710a5a1f4e839b72641aa765b4f111137" integrity sha512-px2mloc1wAcdTbzv0ZotTx+Uh/dfnDO22D9Rx8xr7+/PUwAhZQjoJ9t7Hn72nsaN83rWBXsLvFcIRZju4GIaEQ== @@ -771,21 +805,21 @@ "@ethersproject/properties" "^5.0.7" "@ethersproject/strings" "^5.0.8" -"@ethersproject/wordlists@^5.0.0", "@ethersproject/wordlists@^5.0.4": - version "5.0.7" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.7.tgz#4e5ad38cfbef746b196a3290c0d41696eb7ab468" - integrity sha512-ZjQtYxm41FmHfYgpkdQG++EDcBPQWv9O6FfP6NndYRVaXaQZh6eq3sy7HQP8zCZ8dznKgy6ZyKECS8qdvnGHwA== +"@ethersproject/wordlists@5.0.9", "@ethersproject/wordlists@^5.0.0", "@ethersproject/wordlists@^5.0.8": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.0.9.tgz#f16cc0b317637c3ae9c689ebd7bc2cbbffadd013" + integrity sha512-Sn6MTjZkfbriod6GG6+p43W09HOXT4gwcDVNj0YoPYlo4Zq2Fk6b1CU9KUX3c6aI17PrgYb4qwZm5BMuORyqyQ== dependencies: - "@ethersproject/bytes" "^5.0.4" - "@ethersproject/hash" "^5.0.4" - "@ethersproject/logger" "^5.0.5" - "@ethersproject/properties" "^5.0.3" - "@ethersproject/strings" "^5.0.4" + "@ethersproject/bytes" "^5.0.9" + "@ethersproject/hash" "^5.0.10" + "@ethersproject/logger" "^5.0.8" + "@ethersproject/properties" "^5.0.7" + "@ethersproject/strings" "^5.0.8" "@google-cloud/common@^3.4.1": - version "3.5.0" - resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-3.5.0.tgz#0959e769e8075a06eb0823cc567eef00fd0c2d02" - integrity sha512-10d7ZAvKhq47L271AqvHEd8KzJqGU45TY+rwM2Z3JHuB070FeTi7oJJd7elfrnKaEvaktw3hH2wKnRWxk/3oWQ== + version "3.6.0" + resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-3.6.0.tgz#c2f6da5f79279a4a9ac7c71fc02d582beab98e8b" + integrity sha512-aHIFTqJZmeTNO9md8XxV+ywuvXF3xBm5WNmgWeeCK+XN5X+kGW0WEX94wGwj+/MdOnrVf4dL2RvSIt9J5yJG6Q== dependencies: "@google-cloud/projectify" "^2.0.0" "@google-cloud/promisify" "^2.0.0" @@ -793,26 +827,26 @@ duplexify "^4.1.1" ent "^2.2.0" extend "^3.0.2" - google-auth-library "^6.1.1" + google-auth-library "^7.0.2" retry-request "^4.1.1" teeny-request "^7.0.0" "@google-cloud/logging-winston@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@google-cloud/logging-winston/-/logging-winston-4.0.1.tgz#f01b2a770bc4fa6c6459b3512d1689b97f6e04c2" - integrity sha512-5zEBBsyY5eNidMmOKrZBTp8U3YeQrJ+5Lqowlk1UuFz/92Y12RurAk670NsWjCQKuGmFlrVMEKqKNdQ0Kfz54A== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@google-cloud/logging-winston/-/logging-winston-4.0.4.tgz#5d574ab9eab6a5112ed242fdbcb367eeeb2f2e9e" + integrity sha512-C3D05pPAnT4SneOhsdj1xrBjiRoCOTXHaWbodUGstjqGFjKv+zOUQJpW9wcK6Fp+i2ioOSLBW9ssNDOCBI1znQ== dependencies: - "@google-cloud/logging" "^8.0.5" - google-auth-library "^6.0.0" + "@google-cloud/logging" "^9.0.0" + google-auth-library "^7.0.0" lodash.mapvalues "^4.6.0" logform "^2.1.2" triple-beam "^1.3.0" winston-transport "^4.3.0" -"@google-cloud/logging@^8.0.5": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@google-cloud/logging/-/logging-8.1.1.tgz#a290cac9abdc718e9d4bc0ee34ef65aba3879130" - integrity sha512-QELRv9eDKpzZYOgiSlpmkvhK9Ocubhiz3qR//FHWLIpoR8yCcbR6g9/xPwR+rwlQLJsn5+8QhQ5EGnuKnBHOGA== +"@google-cloud/logging@^9.0.0": + version "9.0.2" + resolved "https://registry.yarnpkg.com/@google-cloud/logging/-/logging-9.0.2.tgz#be1dcd2e1e6264fcb6f83d03e24c905cb7cb9dec" + integrity sha512-Okj4mGcHGFpqIkmIKe7f+anAIlB+yqZsXlgafYyv8vFmsOttB1D2v0UMq0xFNS6LX0BvkAlPa31d41XoiXR+uQ== dependencies: "@google-cloud/common" "^3.4.1" "@google-cloud/paginator" "^3.0.0" @@ -824,7 +858,7 @@ eventid "^1.0.0" extend "^3.0.2" gcp-metadata "^4.0.0" - google-auth-library "^6.0.0" + google-auth-library "^7.0.0" google-gax "^2.9.2" is "^3.3.0" on-finished "^2.3.0" @@ -832,7 +866,7 @@ snakecase-keys "^3.1.2" stream-events "^1.0.5" through2 "^4.0.0" - type-fest "^0.19.0" + type-fest "^0.20.0" "@google-cloud/paginator@^3.0.0": version "3.0.5" @@ -852,35 +886,23 @@ resolved "https://registry.yarnpkg.com/@google-cloud/promisify/-/promisify-2.0.3.tgz#f934b5cdc939e3c7039ff62b9caaf59a9d89e3a8" integrity sha512-d4VSA86eL/AFTe5xtyZX+ePUjE8dIFu2T8zmdeNBSa5/kNgXPCx/o/wbFNHAGLJdGnk1vddRuMESD9HbOC8irw== -"@grpc/grpc-js@~1.1.1": - version "1.1.8" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.1.8.tgz#2845f0fc3d1bfbb150ed7a78a76bdf41b126d367" - integrity sha512-64hg5rmEm6F/NvlWERhHmmgxbWU8nD2TMWE+9TvG7/WcOrFT3fzg/Uu631pXRFwmJ4aWO/kp9vVSlr8FUjBDLA== +"@grpc/grpc-js@~1.2.0": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.2.8.tgz#7e1aab8fd4d576b8ee6e73bba475d94a67ffd07d" + integrity sha512-9C1xiCbnYe/3OFpSuRqz2JgFSOxv6+SlqFhXgRC1nHfXYbLnXvtmsI/NpaMs6k9ZNyV4gyaOOh5Z4McfegQGew== dependencies: - "@grpc/proto-loader" "^0.6.0-pre14" - "@types/node" "^12.12.47" - google-auth-library "^6.0.0" + "@types/node" ">=12.12.47" + google-auth-library "^6.1.1" semver "^6.2.0" "@grpc/proto-loader@^0.5.1": - version "0.5.5" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.5.tgz#6725e7a1827bdf8e92e29fbf4e9ef0203c0906a9" - integrity sha512-WwN9jVNdHRQoOBo9FDH7qU+mgfjPc8GygPYms3M+y3fbQLfnCe/Kv/E01t7JRgnrsOHH8euvSbed3mIalXhwqQ== + version "0.5.6" + resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.5.6.tgz#1dea4b8a6412b05e2d58514d507137b63a52a98d" + integrity sha512-DT14xgw3PSzPxwS13auTEwxhMMOoz33DPUKNtmYK/QYbBSpLXJy78FGGs5yVoxVobEqPm4iW9MOIoz0A3bLTRQ== dependencies: lodash.camelcase "^4.3.0" protobufjs "^6.8.6" -"@grpc/proto-loader@^0.6.0-pre14": - version "0.6.0-pre9" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.6.0-pre9.tgz#0c6fe42f6c5ef9ce1b3cef7be64d5b09d6fe4d6d" - integrity sha512-oM+LjpEjNzW5pNJjt4/hq1HYayNeQT+eGrOPABJnYHv7TyNPDNzkQ76rDYZF86X5swJOa4EujEMzQ9iiTdPgww== - dependencies: - "@types/long" "^4.0.1" - lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^6.9.0" - yargs "^15.3.1" - "@harmony-js/account@0.1.56": version "0.1.56" resolved "https://registry.yarnpkg.com/@harmony-js/account/-/account-0.1.56.tgz#74d008d199b81af6bd554a6cc455c882e85d61e7" @@ -981,6 +1003,27 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" +"@nomiclabs/ethereumjs-vm@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@nomiclabs/ethereumjs-vm/-/ethereumjs-vm-4.2.2.tgz#2f8817113ca0fb6c44c1b870d0a809f0e026a6cc" + integrity sha512-8WmX94mMcJaZ7/m7yBbyuS6B+wuOul+eF+RY9fBpGhNaUpyMR/vFIcDojqcWQ4Yafe1tMKY5LDu2yfT4NZgV4Q== + dependencies: + async "^2.1.2" + async-eventemitter "^0.2.2" + core-js-pure "^3.0.1" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-blockchain "^4.0.3" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + fake-merkle-patricia-tree "^1.0.1" + functional-red-black-tree "^1.0.1" + merkle-patricia-tree "3.0.0" + rustbn.js "~0.2.0" + safe-buffer "^5.1.1" + util.promisify "^1.0.0" + "@opencensus/core@^0.0.22": version "0.0.22" resolved "https://registry.yarnpkg.com/@opencensus/core/-/core-0.0.22.tgz#dad55faf24825e7ed5e9f3d7a7795aafe15eb021" @@ -1012,9 +1055,9 @@ integrity sha512-qIy6tLx8rtybEsIOAlrM4J/85s2q2nPkDqj/Rx46VakBZ0LwtFhXIVub96LXHczQX0vaqmAueDqNPXtbSXSaYQ== "@openzeppelin/contracts@^3.3.0": - version "3.3.0" - resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.3.0.tgz#ffdb693c5c349fc33bba420248dd3ac0a2d7c408" - integrity sha512-AemZEsQYtUp1WRkcmZm1div5ORfTpLquLaziCIrSagjxyKdmObxuaY1yjQ5SHFMctR8rLwp706NXTbiIRJg7pw== + version "3.4.0" + resolved "https://registry.yarnpkg.com/@openzeppelin/contracts/-/contracts-3.4.0.tgz#9a1669ad5f9fdfb6e273bb5a4fed10cb4cc35eb0" + integrity sha512-qh+EiHWzfY/9CORr+eRUkeEUP1WiFUcq3974bLHwyYzLBUtK6HPaMkIUHi74S1rDTZ0sNz42DwPc5A4IJvN3rg== "@openzeppelin/upgrades@^2.7.2": version "2.8.0" @@ -1230,15 +1273,83 @@ debug "^3.1.0" hosted-git-info "^2.6.0" +"@sentry/core@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/core/-/core-5.30.0.tgz#6b203664f69e75106ee8b5a2fe1d717379b331f3" + integrity sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/hub@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/hub/-/hub-5.30.0.tgz#2453be9b9cb903404366e198bd30c7ca74cdc100" + integrity sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ== + dependencies: + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/minimal@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/minimal/-/minimal-5.30.0.tgz#ce3d3a6a273428e0084adcb800bc12e72d34637b" + integrity sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/types" "5.30.0" + tslib "^1.9.3" + +"@sentry/node@^5.18.1": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/node/-/node-5.30.0.tgz#4ca479e799b1021285d7fe12ac0858951c11cd48" + integrity sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg== + dependencies: + "@sentry/core" "5.30.0" + "@sentry/hub" "5.30.0" + "@sentry/tracing" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + cookie "^0.4.1" + https-proxy-agent "^5.0.0" + lru_map "^0.3.3" + tslib "^1.9.3" + +"@sentry/tracing@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/tracing/-/tracing-5.30.0.tgz#501d21f00c3f3be7f7635d8710da70d9419d4e1f" + integrity sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw== + dependencies: + "@sentry/hub" "5.30.0" + "@sentry/minimal" "5.30.0" + "@sentry/types" "5.30.0" + "@sentry/utils" "5.30.0" + tslib "^1.9.3" + +"@sentry/types@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/types/-/types-5.30.0.tgz#19709bbe12a1a0115bc790b8942917da5636f402" + integrity sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw== + +"@sentry/utils@5.30.0": + version "5.30.0" + resolved "https://registry.yarnpkg.com/@sentry/utils/-/utils-5.30.0.tgz#9a5bd7ccff85ccfe7856d493bffa64cabc41e980" + integrity sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww== + dependencies: + "@sentry/types" "5.30.0" + tslib "^1.9.3" + "@sindresorhus/is@^0.14.0": version "0.14.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== -"@sinonjs/commons@^1", "@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.1.tgz#e7df00f98a203324f6dc7cc606cad9d4a8ab2217" - integrity sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw== +"@sinonjs/commons@^1.6.0", "@sinonjs/commons@^1.7.0", "@sinonjs/commons@^1.8.1": + version "1.8.2" + resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.2.tgz#858f5c4b48d80778fde4b9d541f27edc0d56488b" + integrity sha512-sruwd86RJHdsVf/AtBoijDmUqJp3B6hF/DGC23C+JaegnDHaZyewCjoVGTdg3J0uz3Zs7NnIT05OBOmML72lQw== dependencies: type-detect "4.0.8" @@ -1249,18 +1360,10 @@ dependencies: "@sinonjs/commons" "^1.7.0" -"@sinonjs/formatio@^5.0.1": - version "5.0.1" - resolved "https://registry.yarnpkg.com/@sinonjs/formatio/-/formatio-5.0.1.tgz#f13e713cb3313b1ab965901b01b0828ea6b77089" - integrity sha512-KaiQ5pBf1MpS09MuA0kp6KBQt2JUOQycqVG1NZXvzeaXe5LGFqAKueIS0bw4w0P9r7KuBSVdUk5QjXsUdu2CxQ== - dependencies: - "@sinonjs/commons" "^1" - "@sinonjs/samsam" "^5.0.2" - -"@sinonjs/samsam@^5.0.2", "@sinonjs/samsam@^5.2.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.0.tgz#1d2f0743dc54bf13fe9d508baefacdffa25d4329" - integrity sha512-hXpcfx3aq+ETVBwPlRFICld5EnrkexXuXDwqUNhDdr5L8VjvMeSRwyOa0qL7XFmR+jVWR4rUZtnxlG7RX72sBg== +"@sinonjs/samsam@^5.3.1": + version "5.3.1" + resolved "https://registry.yarnpkg.com/@sinonjs/samsam/-/samsam-5.3.1.tgz#375a45fe6ed4e92fca2fb920e007c48232a6507f" + integrity sha512-1Hc0b1TtyfBu8ixF/tpfSHTVWKwCBLY4QJbkgnE7HcwyvT2xArDxb4K7dMgqRm3szI+LJbzmW/s4xxEhv6hwDg== dependencies: "@sinonjs/commons" "^1.6.0" lodash.get "^4.4.2" @@ -1271,6 +1374,11 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== +"@solidity-parser/parser@^0.11.0": + version "0.11.1" + resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.11.1.tgz#fa840af64840c930f24a9c82c08d4a092a068add" + integrity sha512-H8BSBoKE8EubJa0ONqecA2TviT3TnHeC4NpgnAHSUiuhZoQBfPB4L2P9bs8R6AoTW10Endvh3vc+fomVMIDIYQ== + "@solidity-parser/parser@^0.8.0": version "0.8.2" resolved "https://registry.yarnpkg.com/@solidity-parser/parser/-/parser-0.8.2.tgz#a6a5e93ac8dca6884a99a532f133beba59b87b69" @@ -1288,53 +1396,53 @@ resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@truffle/blockchain-utils@^0.0.25": - version "0.0.25" - resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.25.tgz#f4b320890113d282f25f1a1ecd65b94a8b763ac1" - integrity sha512-XA5m0BfAWtysy5ChHyiAf1fXbJxJXphKk+eZ9Rb9Twi6fn3Jg4gnHNwYXJacYFEydqT5vr2s4Ou812JHlautpw== +"@truffle/blockchain-utils@^0.0.26": + version "0.0.26" + resolved "https://registry.yarnpkg.com/@truffle/blockchain-utils/-/blockchain-utils-0.0.26.tgz#f4ea794e0a18c74d73ea10e29a506c9ed0a503ee" + integrity sha512-M91NJkfapK1RqdzVwKSSenPEE2cHzAAFwC3aPhA8Y3DznRfzOcck4mDH6eY71sytVCrGaXGm/Wirn3drGSH+qQ== dependencies: source-map-support "^0.5.19" -"@truffle/codec@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.9.0.tgz#ccdc9f79c6784447c70925bebc26319cd8bca155" - integrity sha512-lAkycqrvALZn677H9/CWxmpWVBejYCWbytNeFuHHM69gqNtwzZjDjbIyEUHQNiH5ZWed4XkIdJj7AxvlaCc5jw== +"@truffle/codec@^0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@truffle/codec/-/codec-0.10.0.tgz#306425897f92b71cf2c9f665a45351e445b4c09b" + integrity sha512-jRqUPQkiiZmXtms34nI2WmEAA4pQnKUhdtJGCeqKUKu2igZ35ZMKoXkpg1stvahawn17XZxo+8DjbFlR0uHq7w== dependencies: big.js "^5.2.2" - bn.js "^4.11.8" - borc "^2.1.2" - debug "^4.1.0" + bn.js "^5.1.3" + cbor "^5.1.0" + debug "^4.3.1" lodash.clonedeep "^4.5.0" lodash.escaperegexp "^4.1.2" lodash.partition "^4.6.0" lodash.sum "^4.0.2" - semver "^6.3.0" + semver "^7.3.4" source-map-support "^0.5.19" utf8 "^3.0.0" web3-utils "1.2.9" -"@truffle/contract-schema@^3.3.2": - version "3.3.2" - resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.3.2.tgz#6450738c35859ed087760d826031a8247f7bc907" - integrity sha512-PFNUHlcMFh6CDLDXTYCpm1G5rM5EJlneA9ml5y1TbkLgjLMICI2XLilimFZ/DC0THQekHpoQC+W/QMD/OTiTiw== +"@truffle/contract-schema@^3.3.4": + version "3.3.4" + resolved "https://registry.yarnpkg.com/@truffle/contract-schema/-/contract-schema-3.3.4.tgz#95f0265cac7de7bcaa0542f5fe671a7896011bfe" + integrity sha512-HzscBl/GhZBvPNQeD9l6ewSHSkvNmE+bA0iTVa0Y2mNf5GD5Y3fK2NPyfbOdtckOvLqebvYGEDEPRiXc3BZ05g== dependencies: ajv "^6.10.0" crypto-js "^3.1.9-1" - debug "^4.1.0" + debug "^4.3.1" "@truffle/contract@^4.2.6": - version "4.3.0" - resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.0.tgz#37458d9f9fbf287d45130a09d855f68a1923cb77" - integrity sha512-3x9dhBuDElRixR8bxknPfuGH7bBaOlYGF2FsUsuK3CoXo7hblXSQg8VZ/ns55oTSxDzRkwmI0EnRtwzGObaoGA== - dependencies: - "@truffle/blockchain-utils" "^0.0.25" - "@truffle/contract-schema" "^3.3.2" - "@truffle/debug-utils" "^5.0.3" - "@truffle/error" "^0.0.11" - "@truffle/interface-adapter" "^0.4.18" + version "4.3.7" + resolved "https://registry.yarnpkg.com/@truffle/contract/-/contract-4.3.7.tgz#9a9a8bec9ae54722d9acce8344efbf455548eeda" + integrity sha512-ClWwNBhT8xuoULjAFu4HWVY8fbe2LKSZ24LjTPcMwfrSEKMMoaxmC82sfoGEyKcfbckGLqRhmqBIopzMXxOebw== + dependencies: + "@truffle/blockchain-utils" "^0.0.26" + "@truffle/contract-schema" "^3.3.4" + "@truffle/debug-utils" "^5.0.10" + "@truffle/error" "^0.0.12" + "@truffle/interface-adapter" "^0.4.19" bignumber.js "^7.2.1" ethereum-ens "^0.8.0" - ethers "^4.0.0-beta.1" + ethers "^4.0.32" source-map-support "^0.5.19" web3 "1.2.9" web3-core-helpers "1.2.9" @@ -1342,37 +1450,38 @@ web3-eth-abi "1.2.9" web3-utils "1.2.9" -"@truffle/debug-utils@^5.0.3": - version "5.0.3" - resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.3.tgz#c4e5f9489adb3f85d055934625e0d9c84fdd6599" - integrity sha512-pVbYg13gsCWTUQzEycr2fy0knIeegkj/yxJi2xf3BwEIAw4poYPl4ItxDMX9MTgNTwSe7ta+BykECj6djMuQNw== +"@truffle/debug-utils@^5.0.10": + version "5.0.10" + resolved "https://registry.yarnpkg.com/@truffle/debug-utils/-/debug-utils-5.0.10.tgz#8dad322d404870e313b3b92cdaa609ad6212e08e" + integrity sha512-N2jqeHDmVh0xIe/BL2Or0/AAGAHxd2dM55NOAJYFPdekbigQEpmobTZtfUfpZU1oTQe+1yHpXXonkkjf+AKY7w== dependencies: - "@truffle/codec" "^0.9.0" - "@trufflesuite/chromafi" "^2.2.1" + "@truffle/codec" "^0.10.0" + "@trufflesuite/chromafi" "^2.2.2" + bn.js "^5.1.3" chalk "^2.4.2" - debug "^4.1.0" + debug "^4.3.1" highlight.js "^10.4.0" - highlightjs-solidity "^1.0.19" + highlightjs-solidity "^1.0.21" -"@truffle/error@^0.0.11": - version "0.0.11" - resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.11.tgz#2789c0042d7e796dcbb840c7a9b5d2bcd8e0e2d8" - integrity sha512-ju6TucjlJkfYMmdraYY/IBJaFb+Sa+huhYtOoyOJ+G29KcgytUVnDzKGwC7Kgk6IsxQMm62Mc1E0GZzFbGGipw== +"@truffle/error@^0.0.12": + version "0.0.12" + resolved "https://registry.yarnpkg.com/@truffle/error/-/error-0.0.12.tgz#83e02e6ffe1d154fe274141d90038a91fd1e186d" + integrity sha512-kZqqnPR9YDJG7KCDOcN1qH16Qs0oz1PzF0Y93AWdhXuL9S9HYo/RUUeqGKbPpRBEZldQUS8aa4EzfK08u5pu6g== -"@truffle/interface-adapter@^0.4.18": - version "0.4.18" - resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.18.tgz#1aac45596997d208085d5168f82b990624610646" - integrity sha512-P9JVSYD/CX3V+NgTWu+Bf71sLh8pMwrCpbiYRB93pRw/1H3ZTvt5iDC2MVvVxCs8FkSiy4OZzQK/DJ8+hXAmYw== +"@truffle/interface-adapter@^0.4.19": + version "0.4.19" + resolved "https://registry.yarnpkg.com/@truffle/interface-adapter/-/interface-adapter-0.4.19.tgz#19248ac88099f8df34f58a3d43a95ba3470dc89a" + integrity sha512-+Zz6Fr8+I2wYSS8RM3WBOMzf22QffMQTnlsYsRgRHzv3gYoRA9ZDLb84lFRfmWyw+IdXTo90tjRHEb5krC6uxg== dependencies: - bn.js "^4.11.8" + bn.js "^5.1.3" ethers "^4.0.32" source-map-support "^0.5.19" web3 "1.2.9" -"@trufflesuite/chromafi@^2.2.1": - version "2.2.1" - resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.1.tgz#6bad90d7cb52b3a414c9640346085482dbd41fd9" - integrity sha512-kODhM/LsjPrSRGQdaHe113v4xob/aheRmdwN0i6seVNavmHGBvC4ob3COlD1GjaklXsl9QWw4fengowIx1+07Q== +"@trufflesuite/chromafi@^2.2.2": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@trufflesuite/chromafi/-/chromafi-2.2.2.tgz#d3fc507aa8504faffc50fb892cedcfe98ff57f77" + integrity sha512-mItQwVBsb8qP/vaYHQ1kDt2vJLhjoEXJptT6y6fJGvFophMFhOI/NsTVUa0nJL1nyMeFiS6hSYuNVdpQZzB1gA== dependencies: ansi-mark "^1.0.0" ansi-regex "^3.0.0" @@ -1382,7 +1491,7 @@ cheerio "^1.0.0-rc.2" detect-indent "^5.0.0" he "^1.1.1" - highlight.js "^9.12.0" + highlight.js "^10.4.1" lodash.merge "^4.6.2" min-indent "^1.0.0" strip-ansi "^4.0.0" @@ -1429,14 +1538,14 @@ "@types/node" "*" "@types/chai@^4.2.11", "@types/chai@^4.2.14": - version "4.2.14" - resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.14.tgz#44d2dd0b5de6185089375d976b4ec5caf6861193" - integrity sha512-G+ITQPXkwTrslfG5L/BksmbLUA0M1iybEsmCWPqzSxsRRhJZimBKJkoMi8fr/CPygPTj4zO5pJH7I2/cm9M7SQ== + version "4.2.15" + resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.2.15.tgz#b7a6d263c2cecf44b6de9a051cf496249b154553" + integrity sha512-rYff6FI+ZTKAPkJUoyz7Udq3GaoDZnxYDEvdEdFZASiA7PoErltHezDishqQiSDWrGxvxmplH304jyzQmjp0AQ== "@types/connect@*": - version "3.4.33" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.33.tgz#31610c901eca573b8713c3330abc6e6b9f588546" - integrity sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== + version "3.4.34" + resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.34.tgz#170a40223a6d666006d93ca128af2beb1d9b1901" + integrity sha512-ePPA/JuI+X0vb+gSWlPKOY0NdNAie/rPUqX2GUPpbZwiKTkSPhjXWuee47E4MtE54QVzGCQMQkAL6JhV2E1+cQ== dependencies: "@types/node" "*" @@ -1452,22 +1561,22 @@ dependencies: bignumber.js "7.2.1" -"@types/express-serve-static-core@*": - version "4.17.14" - resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.14.tgz#cabf91debeeb3cb04b798e2cff908864e89b6106" - integrity sha512-uFTLwu94TfUFMToXNgRZikwPuZdOtDgs3syBtAIr/OXorL1kJqUJT9qCLnRZ5KBOWfZQikQ2xKgR2tnDj1OgDA== +"@types/express-serve-static-core@^4.17.18": + version "4.17.18" + resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.18.tgz#8371e260f40e0e1ca0c116a9afcd9426fa094c40" + integrity sha512-m4JTwx5RUBNZvky/JJ8swEJPKFd8si08pPF2PfizYjGZOKr/svUWPcoUmLow6MmPzhasphB7gSTINY67xn3JNA== dependencies: "@types/node" "*" "@types/qs" "*" "@types/range-parser" "*" "@types/express@^4.17.6": - version "4.17.9" - resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.9.tgz#f5f2df6add703ff28428add52bdec8a1091b0a78" - integrity sha512-SDzEIZInC4sivGIFY4Sz1GG6J9UObPwCInYJjko2jzOf/Imx/dlpume6Xxwj1ORL82tBbmN4cPDIDkLbWHk9hw== + version "4.17.11" + resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.11.tgz#debe3caa6f8e5fcda96b47bd54e2f40c4ee59545" + integrity sha512-no+R6rW60JEc59977wIxreQVsIEOAYwgCqldrA/vkpCnbD7MqTefO97lmoBe4WE0F156bC4uLSP1XHDOySnChg== dependencies: "@types/body-parser" "*" - "@types/express-serve-static-core" "*" + "@types/express-serve-static-core" "^4.17.18" "@types/qs" "*" "@types/serve-static" "*" @@ -1492,9 +1601,9 @@ "@types/istanbul-lib-report" "*" "@types/json-schema@^7.0.3": - version "7.0.6" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.6.tgz#f4c7ec43e81b319a9815115031709f26987891f0" - integrity sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw== + version "7.0.7" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" + integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== "@types/json5@^0.0.29": version "0.0.29" @@ -1511,20 +1620,20 @@ resolved "https://registry.yarnpkg.com/@types/lru-cache/-/lru-cache-5.1.0.tgz#57f228f2b80c046b4a1bd5cac031f81f207f4f03" integrity sha512-RaE0B+14ToE4l6UqdarKPnXwVDuigfFv+5j9Dze/Nqr23yyuqdNvzcZi3xB+3Agvi5R4EOgAksfv3lXX4vBt9w== -"@types/mime@*": - version "2.0.3" - resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.3.tgz#c893b73721db73699943bfc3653b1deb7faa4a3a" - integrity sha512-Jus9s4CDbqwocc5pOAnh8ShfrnMcPHuJYzVcSUU7lrh8Ni5HuIqX3oilL86p3dlTrk0LzHRCgA/GQ7uNCw6l2Q== +"@types/mime@^1": + version "1.3.2" + resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" + integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== "@types/mocha@^7.0.2": version "7.0.2" resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-7.0.2.tgz#b17f16cf933597e10d6d78eae3251e692ce8b0ce" integrity sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w== -"@types/node@*", "@types/node@^14.0.5": - version "14.14.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.10.tgz#5958a82e41863cfc71f2307b3748e3491ba03785" - integrity sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ== +"@types/node@*", "@types/node@>=12.12.47", "@types/node@^14.0.13", "@types/node@^14.0.5": + version "14.14.28" + resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.28.tgz#cade4b64f8438f588951a6b35843ce536853f25b" + integrity sha512-lg55ArB+ZiHHbBBttLpzD07akz0QPrZgUODNakeC09i62dnrywr9mFErHuaPlB6I7z+sEbK+IYmplahvplCj2g== "@types/node@10.12.18": version "10.12.18" @@ -1537,24 +1646,19 @@ integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== "@types/node@^10.12.18", "@types/node@^10.3.2": - version "10.17.48" - resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.48.tgz#726e7f25d00bf58d79c8f00dd586dd9a10d06a4f" - integrity sha512-Agl6xbYP6FOMDeAsr3QVZ+g7Yzg0uhPHWx0j5g4LFdUBHVtqtU+gH660k/lCEe506jJLOGbEzsnqPDTZGJQLag== + version "10.17.52" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.52.tgz#dc960d4e256331b3c697b7a573ee98b882febee5" + integrity sha512-bKnO8Rcj03i6JTzweabq96k29uVNcXGB0bkwjVQTFagDgxxNged18281AZ0nTMHl+aFpPPWyPrk4Z3+NtW/z5w== -"@types/node@^12.12.47", "@types/node@^12.12.6", "@types/node@^12.6.1": - version "12.19.8" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.19.8.tgz#efd6d1a90525519fc608c9db16c8a78f7693a978" - integrity sha512-D4k2kNi0URNBxIRCb1khTnkWNHv8KSL1owPmS/K5e5t8B2GzMReY7AsJIY1BnP5KdlgC4rj9jk2IkDMasIE7xg== +"@types/node@^12.12.6", "@types/node@^12.6.1": + version "12.20.1" + resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.1.tgz#63d36c10e162666f0107f247cdca76542c3c7472" + integrity sha512-tCkE96/ZTO+cWbln2xfyvd6ngHLanvVlJ3e5BeirJ3BYI5GbAyubIrmV4JjjugDly5D9fHjOL5MNsqsCnqwW6g== "@types/node@^13.13.4", "@types/node@^13.7.0": - version "13.13.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.34.tgz#c9300a1b6560d90817fb2bba650e250116a575f9" - integrity sha512-g8D1HF2dMDKYSDl5+79izRwRgNPsSynmWMbj50mj7GZ0b7Lv4p8EmZjbo3h0h+6iLr6YmVz9VnF6XVZ3O6V1Ug== - -"@types/node@^14.0.13": - version "14.14.20" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.20.tgz#f7974863edd21d1f8a494a73e8e2b3658615c340" - integrity sha512-Y93R97Ouif9JEOWPIUyU+eyIdyRqQR0I8Ez1dzku4hDx34NWh4HbtIc3WNzwB1Y9ULvNGeu5B8h8bVL5cAk4/A== + version "13.13.42" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.42.tgz#ff343be01fca44b59e785e20b840357cb704a7f2" + integrity sha512-g+w2QgbW7k2CWLOXzQXbO37a7v5P9ObPvYahKphdBLV5aqpbVZRhTpWCT0SMRqX1i30Aig791ZmIM2fJGL2S8A== "@types/object-hash@^1.3.4": version "1.3.4" @@ -1598,11 +1702,11 @@ "@types/node" "*" "@types/serve-static@*": - version "1.13.8" - resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.8.tgz#851129d434433c7082148574ffec263d58309c46" - integrity sha512-MoJhSQreaVoL+/hurAZzIm8wafFR6ajiTM1m4A0kv6AGeVBl4r4pOV8bGFrjjq1sGxDTnCoF8i22o0/aE5XCyA== + version "1.13.9" + resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.9.tgz#aacf28a85a05ee29a11fb7c3ead935ac56f33e4e" + integrity sha512-ZFqF6qa48XsPdjXV5Gsz0Zqmux2PerNd3a/ktL45mHpa19cuMi/cL8tcxdAx497yRh+QtYPuofjT9oWw9P7nkA== dependencies: - "@types/mime" "*" + "@types/mime" "^1" "@types/node" "*" "@types/sinon@^9.0.8": @@ -1630,9 +1734,9 @@ "@types/ethereum-protocol" "*" "@types/yargs-parser@*": - version "15.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" - integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== + version "20.2.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9" + integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== "@types/yargs@^13.0.0": version "13.0.11" @@ -1641,13 +1745,6 @@ dependencies: "@types/yargs-parser" "*" -"@types/yauzl@^2.9.1": - version "2.9.1" - resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.1.tgz#d10f69f9f522eef3cf98e30afb684a1e1ec923af" - integrity sha512-A1b8SU4D10uoPjwb0lnHmmu8wZhR9d+9o2PKBQT2jU5YPTKsxac6M2qGAdY7VcL+dHHhARVUDmeg0rOrcd9EjA== - dependencies: - "@types/node" "*" - "@typescript-eslint/eslint-plugin@^3.3.0", "@typescript-eslint/eslint-plugin@^3.9.0": version "3.10.1" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.10.1.tgz#7e061338a1383f59edc204c605899f93dc2e2c8f" @@ -1728,6 +1825,27 @@ abort-controller@^3.0.0: dependencies: event-target-shim "^5.0.0" +abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6" + integrity sha512-5mU5P1gXtsMIXg65/rsYGsi93+MlogXZ9FA8JnwKurHQg64bfXwGYVdVdijNTVNOlAsuIiOwHdvFFD5JqCJQ7A== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.6.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz#1c5e8c6a5ef965ae8c35dfb3a8770c476b82c4b8" + integrity sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA== + dependencies: + xtend "~4.0.0" + +abstract-leveldown@~2.7.1: + version "2.7.2" + resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz#87a44d7ebebc341d59665204834c8b7e0932cc93" + integrity sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w== + dependencies: + xtend "~4.0.0" + accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -1736,7 +1854,7 @@ accepts@~1.3.7: mime-types "~2.1.24" negotiator "0.6.2" -acorn-jsx@^5.2.0: +acorn-jsx@^5.3.1: version "5.3.1" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b" integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng== @@ -1746,6 +1864,11 @@ acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +adm-zip@^0.4.16: + version "0.4.16" + resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" + integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== + aes-js@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.0.0.tgz#e21df10ad6c2053295bcbb8dab40b09dbea87e4d" @@ -1756,11 +1879,6 @@ aes-js@^3.1.1, aes-js@^3.1.2: resolved "https://registry.yarnpkg.com/aes-js/-/aes-js-3.1.2.tgz#db9aabde85d5caabbfc0d4f2a4446960f627146a" integrity sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ== -agent-base@5: - version "5.1.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-5.1.1.tgz#e8fb3f242959db44d63be665db7a8e739537a32c" - integrity sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g== - agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -1768,7 +1886,7 @@ agent-base@6: dependencies: debug "4" -ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: +ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1778,6 +1896,16 @@ ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^7.0.2: + version "7.1.1" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-7.1.1.tgz#1e6b37a454021fa9941713f38b952fc1c8d32a84" + integrity sha512-ga/aqDYnUy/o7vbsRTFhhTsNeXiYb5JWDIcRIeZfwRNCefwjNTVYCGdGSUrEmiu3yDK3vFvNbgJxvrQW4JXrYQ== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-colors@3.2.3: version "3.2.3" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.3.tgz#57d35b8686e851e2cc04c403f1c00203976a1813" @@ -1788,6 +1916,13 @@ ansi-colors@4.1.1, ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-escapes@^4.3.0: + version "4.3.1" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" + integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== + dependencies: + type-fest "^0.11.0" + ansi-mark@^1.0.0: version "1.0.4" resolved "https://registry.yarnpkg.com/ansi-mark/-/ansi-mark-1.0.4.tgz#1cd4ba8d57f15f109d6aaf6ec9ca9786c8a4ee6c" @@ -1858,6 +1993,16 @@ argparse@^1.0.7: dependencies: sprintf-js "~1.0.2" +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-filter@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-1.0.0.tgz#baf79e62e6ef4c2a4c0b831232daffec251f9d83" + integrity sha1-uveeYubvTCpMC4MSMtr/7CUfnYM= + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -1920,10 +2065,17 @@ assertion-error@^1.1.0: resolved "https://registry.yarnpkg.com/assertion-error/-/assertion-error-1.1.0.tgz#e60b6b0e8f301bd97e5375215bda406c85118c0b" integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== -astral-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9" - integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg== +astral-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" + integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== + +async-eventemitter@^0.2.2: + version "0.2.4" + resolved "https://registry.yarnpkg.com/async-eventemitter/-/async-eventemitter-0.2.4.tgz#f5e7c8ca7d3e46aab9ec40a292baf686a0bafaca" + integrity sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw== + dependencies: + async "^2.4.0" async-limiter@~1.0.0: version "1.0.1" @@ -1938,6 +2090,18 @@ async-listener@^0.6.0: semver "^5.3.0" shimmer "^1.1.0" +async@^1.4.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= + +async@^2.0.1, async@^2.1.2, async@^2.4.0, async@^2.6.1: + version "2.6.3" + resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" + integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== + dependencies: + lodash "^4.17.14" + async@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/async/-/async-3.2.0.tgz#b3a2685c5ebb641d3de02d161002c60fc9f85720" @@ -1948,6 +2112,13 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= +available-typed-arrays@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5" + integrity sha512-XWX3OX8Onv97LMk/ftVyBibpGwY5a8SmuxZPzeOxqmuEqUCOM9ZE+uIaD1VNJ5QnvU2UQusvmKbuM1FR8QWGfQ== + dependencies: + array-filter "^1.0.0" + aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -2037,7 +2208,7 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bigi@^1.1.0, bigi@^1.4.0: +bigi@^1.1.0, bigi@^1.4.0, bigi@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/bigi/-/bigi-1.4.2.tgz#9c665a95f88b8b08fc05cfd731f561859d725825" integrity sha1-nGZalfiLiwj8Bc/XMfVhhZ1yWCU= @@ -2047,15 +2218,15 @@ bignumber.js@7.2.1, bignumber.js@^7.2.0, bignumber.js@^7.2.1: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-7.2.1.tgz#80c048759d826800807c4bfd521e50edbba57a5f" integrity sha512-S4XzBk5sMB+Rcb/LNcpzXr57VRTxgAvaAEDAl1AwRx27j00hT84O6OkteE7u8UB3NuaaygCRrEpqox4uDOrbdQ== -bignumber.js@^9.0.0: +bignumber.js@^9.0.0, bignumber.js@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5" integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA== binary-extensions@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.1.0.tgz#30fa40c9e7fe07dbc895678cd287024dea241dd9" - integrity sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ== + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== bindings@^1.3.0, bindings@^1.5.0: version "1.5.0" @@ -2064,6 +2235,11 @@ bindings@^1.3.0, bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" +bintrees@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bintrees/-/bintrees-1.0.1.tgz#0e655c9b9c2435eaab68bf4027226d2b55a34524" + integrity sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ= + bip174@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.0.1.tgz#39cf8ca99e50ce538fb762589832f4481d07c254" @@ -2137,11 +2313,11 @@ bitcoinjs-lib@^5.1.10: wif "^2.0.1" bitcore-lib-cash@^8.20.3: - version "8.23.1" - resolved "https://registry.yarnpkg.com/bitcore-lib-cash/-/bitcore-lib-cash-8.23.1.tgz#188be56f15f4b4d49aa85777cb998252be67cf60" - integrity sha512-QenfM9v5L4UdicRbmMYQbxeNqJjzifBT7DQbK0ambqkxiaUi7k5qY62Gx0N2GIdY5B+edmMib+xCbadaHRTN+w== + version "8.24.2" + resolved "https://registry.yarnpkg.com/bitcore-lib-cash/-/bitcore-lib-cash-8.24.2.tgz#3d6e94925d4fb1e03247d787a521d0a465ef5271" + integrity sha512-SZiDkEyQqQ5pyerDPgqqBA2orvk93txBFczPLfSBYJ7sUMRH5ylc9cHEvxo26n09Gt/+31AqEAiOshI3MtQiyg== dependencies: - bitcore-lib "^8.23.1" + bitcore-lib "^8.24.2" bn.js "=4.11.8" bs58 "^4.0.1" buffer-compare "=1.1.1" @@ -2160,10 +2336,10 @@ bitcore-lib-zcash@zcash-hackworks/bitcore-lib-zcash: inherits "=2.0.1" lodash "=3.10.1" -bitcore-lib@^8.20.3, bitcore-lib@^8.23.1: - version "8.23.1" - resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-8.23.1.tgz#d3eac6aca0ce7e04564cd127e9813f4c0120fa27" - integrity sha512-pYC/w9pE09d61ZKl6TQ7IuIMNKyOBZ97F6wSw+ZZQ40Hm1UW+w+QZimbKKBuUKyr4+QuOPTDw4XUWgbUGbSZIQ== +bitcore-lib@^8.20.3, bitcore-lib@^8.24.2: + version "8.24.2" + resolved "https://registry.yarnpkg.com/bitcore-lib/-/bitcore-lib-8.24.2.tgz#62d2b5dd6622c1ce6c3edc8945bcee462544ee17" + integrity sha512-jujnK/wsLs4ARe3VEfZmbzNrPRgLoirz7PYJn4uRVk2vP7zeiN8pStnbYUNSB0ZgNnB7lcB1F3ZnSBk2y1By/Q== dependencies: bech32 "=1.1.3" bn.js "=4.11.8" @@ -2205,15 +2381,6 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" -bl@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/bl/-/bl-4.0.3.tgz#12d6287adc29080e22a705e5764b2a9522cdc489" - integrity sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg== - dependencies: - buffer "^5.5.0" - inherits "^2.0.4" - readable-stream "^3.4.0" - "blake2b-wasm@https://github.com/ren-forks/blake2b-wasm#af30a83f77029b53d71650164b305a64c18d1957": version "2.1.0" resolved "https://github.com/ren-forks/blake2b-wasm#af30a83f77029b53d71650164b305a64c18d1957" @@ -2261,11 +2428,11 @@ bn.js@=2.0.4: integrity sha1-Igp81nf38b+pNif/QZN3b+eBlIA= bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.0, bn.js@^4.11.1, bn.js@^4.11.6, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.4.0, bn.js@^4.8.0: - version "4.11.9" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.9.tgz#26d556829458f9d1e81fc48952493d0ba3507828" - integrity sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw== + version "4.12.0" + resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" + integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== -bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2: +bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.1.2, bn.js@^5.1.3: version "5.1.3" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== @@ -2294,34 +2461,11 @@ body-parser@1.19.0, body-parser@^1.16.0: raw-body "2.4.0" type-is "~1.6.17" -boolbase@~1.0.0: +boolbase@^1.0.0, boolbase@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -borc@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/borc/-/borc-2.1.2.tgz#6ce75e7da5ce711b963755117dd1b187f6f8cf19" - integrity sha512-Sy9eoUi4OiKzq7VovMn246iTo17kzuyHJKomCfpWMlI6RpfN1gk95w7d7gH264nApVLg0HZfcpz62/g4VH1Y4w== - dependencies: - bignumber.js "^9.0.0" - buffer "^5.5.0" - commander "^2.15.0" - ieee754 "^1.1.13" - iso-url "~0.4.7" - json-text-sequence "~0.1.0" - readable-stream "^3.6.0" - -boxhock_google-finance-data@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/boxhock_google-finance-data/-/boxhock_google-finance-data-0.2.0.tgz#622cfe59ba7b2eebd9e02c1804cf3d8e50a9b440" - integrity sha512-CwFmmwLmuDHBpOz0UfePWGiKTpciKy4leLz4UT4Bhr2B5lObn56gQlKv2EzUGdboDHBPElfhINhzv3u1B/+iXw== - dependencies: - chrome-aws-lambda "5.3.1" - nested-error-stacks "2.1.0" - puppeteer "5.3.1" - puppeteer-core "5.3.1" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -2337,7 +2481,7 @@ braces@~3.0.2: dependencies: fill-range "^7.0.1" -brorand@^1.0.1: +brorand@^1.0.1, brorand@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= @@ -2475,6 +2619,13 @@ buffer-xor@^1.0.3: resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= +buffer-xor@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-2.0.2.tgz#34f7c64f04c777a1f8aac5e661273bb9dd320289" + integrity sha512-eHslX0bin3GB+Lx2p7lEYRShRewuNZL3fUl4qlVJGGiwoPGftmt8JQgk2Y9Ji5/01TnVDo33E5b5O3vUB1HdqQ== + dependencies: + safe-buffer "^5.1.1" + buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" @@ -2484,9 +2635,9 @@ buffer@^5.0.5, buffer@^5.2.1, buffer@^5.5.0, buffer@^5.6.0: ieee754 "^1.1.13" bufferutil@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.2.tgz#79f68631910f6b993d870fc77dc0a2894eb96cd5" - integrity sha512-AtnG3W6M8B2n4xDQ5R+70EXvOpnXsFYg/AK2yTZd+HQ/oxAdz+GI+DvjmhBw3L0ole+LJ0ngqY4JMbDzkfNzhA== + version "4.0.3" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" + integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== dependencies: node-gyp-build "^4.2.0" @@ -2508,13 +2659,13 @@ cacheable-request@^6.0.0: normalize-url "^4.1.0" responselike "^1.0.2" -call-bind@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" - integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== dependencies: function-bind "^1.1.1" - get-intrinsic "^1.0.0" + get-intrinsic "^1.0.2" callsites@^3.0.0: version "3.1.0" @@ -2558,10 +2709,18 @@ cbor@^4.1.5: json-text-sequence "^0.1" nofilter "^1.0.3" +cbor@^5.1.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/cbor/-/cbor-5.2.0.tgz#4cca67783ccd6de7b50ab4ed62636712f287a67c" + integrity sha512-5IMhi9e1QU76ppa5/ajP1BmMWZ2FHkhAhjeVKQ/EFCgYSEaeVaoGtL7cxJskf9oCCk+XjzaIdc3IuU/dbA/o2A== + dependencies: + bignumber.js "^9.0.1" + nofilter "^1.0.4" + chai@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.2.0.tgz#760aa72cf20e3795e84b12877ce0e83737aa29e5" - integrity sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw== + version "4.3.0" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.0.tgz#5523a5faf7f819c8a92480d70a8cccbadacfc25f" + integrity sha512-/BFd2J30EcOwmdOgXvVsmM48l0Br0nmZPlO0uOW4XKh6kpsUumRXBgPV+IlaqFaqr9cYbeoZAM1Npx0i4A+aiA== dependencies: assertion-error "^1.1.0" check-error "^1.0.2" @@ -2592,17 +2751,36 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha1-V00xLt2Iu13YkS6Sht1sCu1KrII= -cheerio@^1.0.0-rc.2: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== +checkpoint-store@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/checkpoint-store/-/checkpoint-store-1.1.0.tgz#04e4cb516b91433893581e6d4601a78e9552ea06" + integrity sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY= dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" + functional-red-black-tree "^1.0.1" + +cheerio-select-tmp@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/cheerio-select-tmp/-/cheerio-select-tmp-0.1.1.tgz#55bbef02a4771710195ad736d5e346763ca4e646" + integrity sha512-YYs5JvbpU19VYJyj+F7oYrIE2BOll1/hRU7rEy/5+v9BzkSo3bK81iAeeQEMI92vRIxz677m72UmJUiVwwgjfQ== + dependencies: + css-select "^3.1.2" + css-what "^4.0.0" + domelementtype "^2.1.0" + domhandler "^4.0.0" + domutils "^2.4.4" + +cheerio@^1.0.0-rc.2: + version "1.0.0-rc.5" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.5.tgz#88907e1828674e8f9fee375188b27dadd4f0fa2f" + integrity sha512-yoqps/VCaZgN4pfXtenwHROTp8NG6/Hlt4Jpz2FEP0ZJQ+ZUkVDd0hAPDNKhj3nakpfPt/CNs57yEtxD1bXQiw== + dependencies: + cheerio-select-tmp "^0.1.0" + dom-serializer "~1.2.0" + domhandler "^4.0.0" + entities "~2.1.0" + htmlparser2 "^6.0.0" + parse5 "^6.0.0" + parse5-htmlparser2-tree-adapter "^6.0.0" chokidar@3.3.0: version "3.3.0" @@ -2619,10 +2797,10 @@ chokidar@3.3.0: optionalDependencies: fsevents "~2.1.1" -chokidar@3.4.3: - version "3.4.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" - integrity sha512-DtM3g7juCXQxFVSNPNByEC2+NImtBuxQQvWlHunpJIS5Ocr0lG306cC7FCi7cEA0fzmybPUIl4txBIobk1gGOQ== +chokidar@3.5.1, chokidar@^3.4.0: + version "3.5.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" + integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== dependencies: anymatch "~3.1.1" braces "~3.0.2" @@ -2632,19 +2810,17 @@ chokidar@3.4.3: normalize-path "~3.0.0" readdirp "~3.5.0" optionalDependencies: - fsevents "~2.1.2" + fsevents "~2.3.1" chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chrome-aws-lambda@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/chrome-aws-lambda/-/chrome-aws-lambda-5.3.1.tgz#4dbf75cb0d56d634b7422fd8fe0368165c52c7ea" - integrity sha512-KklZTy2p2U/86cCh1hKL3PGDqoirQCAg7uejvNIn0rDBkWj4Su4GOjYMRVB3kthLwmnWhh3x63RDBTCAaQ9Q3Q== - dependencies: - lambdafs "^2.0.0" +ci-info@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz#67a9e964be31a51e15e5010d58e6f12834002f46" + integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== cids@^0.7.1: version "0.7.5" @@ -2686,14 +2862,14 @@ cliui@^5.0.0: strip-ansi "^5.2.0" wrap-ansi "^5.1.0" -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" + wrap-ansi "^7.0.0" clone-response@^1.0.2: version "1.0.2" @@ -2762,21 +2938,26 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +command-exists@^1.2.8: + version "1.2.9" + resolved "https://registry.yarnpkg.com/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69" + integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w== + +commander@3.0.2, commander@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" + integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== + commander@5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== -commander@^2.15.0, commander@^2.8.1: +commander@^2.8.1: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e" - integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow== - component-emitter@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -2787,6 +2968,13 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= +conflux-address-js@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/conflux-address-js/-/conflux-address-js-1.0.4.tgz#fc2f0972ea7a4aea9ac30218e8af199ee75ca0de" + integrity sha512-RGfeX5t7evZKGtq82EbsTiCkiA2KUJ5SVosVQuMFg1zURuXdstk1GESLgt3j6fDuK+hZNXUKhtCsCS2lMLkFgA== + dependencies: + bigi "^1.4.2" + contains-path@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" @@ -2831,11 +3019,21 @@ cookie@0.4.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== +cookie@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" + integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== + cookiejar@^2.1.1, cookiejar@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.2.tgz#dd8a235530752f988f9a0844f3fc589e3111125c" integrity sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA== +core-js-pure@^3.0.1: + version "3.8.3" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.3.tgz#10e9e3b2592ecaede4283e8f3ad7020811587c02" + integrity sha512-V5qQZVAr9K0xu7jXg1M7qTEwuxUgqr7dUOezGaNa7i+Xn9oXAU/d1fzqD9ObuwpVQOaorO5s70ckyi1woP9lVA== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -2925,29 +3123,45 @@ crypto-browserify@3.12.0: randomfill "^1.0.3" crypto-es@^1.2.2: - version "1.2.6" - resolved "https://registry.yarnpkg.com/crypto-es/-/crypto-es-1.2.6.tgz#468f3573a5d7b82e3b63b0004f55f905a6d3b12c" - integrity sha512-PQnrovdr5ibmOxqAh/Vy+A30RokHom7kb9Z61EPwfASfbcJCrCG4+vNNegmebNVHiXvS7WjYpHDePxnE/biEbA== + version "1.2.7" + resolved "https://registry.yarnpkg.com/crypto-es/-/crypto-es-1.2.7.tgz#754a6d52319a94fb4eb1f119297f17196b360f88" + integrity sha512-UUqiVJ2gUuZFmbFsKmud3uuLcNP2+Opt+5ysmljycFCyhA0+T16XJmo1ev/t5kMChMqWh7IEvURNCqsg+SjZGQ== crypto-js@^3.1.9-1: version "3.3.0" resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-3.3.0.tgz#846dd1cce2f68aacfa156c8578f926a609b7976b" integrity sha512-DIT51nX0dCfKltpRiXV+/TVZq+Qq2NgF4644+K7Ttnla7zEzqc+kjJyiB96BHNyUTBxyjzRcZYpUdZa+QAqi6Q== -css-select@^1.1.0, css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= +css-select@^2.0.2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" + integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" + boolbase "^1.0.0" + css-what "^3.2.1" + domutils "^1.7.0" + nth-check "^1.0.2" -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz#a6d7604573365fe74686c3f311c56513d88285f2" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== +css-select@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-3.1.2.tgz#d52cbdc6fee379fba97fb0d3925abbd18af2d9d8" + integrity sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA== + dependencies: + boolbase "^1.0.0" + css-what "^4.0.0" + domhandler "^4.0.0" + domutils "^2.4.3" + nth-check "^2.0.0" + +css-what@^3.2.1: + version "3.4.2" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" + integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== + +css-what@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-4.0.0.tgz#35e73761cab2eeb3d3661126b23d7aa0e8432233" + integrity sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A== d64@^1.0.0: version "1.0.0" @@ -2983,20 +3197,13 @@ debug@3.2.6: dependencies: ms "^2.1.1" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: +debug@4, debug@4.3.1, debug@^4.0.1, debug@^4.1.1, debug@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== dependencies: ms "2.1.2" -debug@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== - dependencies: - ms "2.1.2" - debug@=3.1.0, debug@~3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -3108,6 +3315,21 @@ defer-to-connect@^1.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== +deferred-leveldown@~1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz#3acd2e0b75d1669924bc0a4b642851131173e1eb" + integrity sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA== + dependencies: + abstract-leveldown "~2.6.0" + +deferred-leveldown@~4.0.0: + version "4.0.2" + resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz#0b0570087827bf480a23494b398f04c128c19a20" + integrity sha512-5fMC8ek8alH16QiV0lTCis610D1Zt1+LA4MS4d63JgS32lrCjTFDUFz2ao09/j2I4Bqb5jL4FZYwu7Jz0XO1ww== + dependencies: + abstract-leveldown "~5.0.0" + inherits "^2.0.3" + define-properties@^1.1.2, define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -3126,9 +3348,9 @@ delimit-stream@0.1.0: integrity sha1-m4MZR3wOX4rrPONXrjBfwl6hzSs= denque@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/denque/-/denque-1.4.1.tgz#6744ff7641c148c3f8a69c307e51235c1f4a37cf" - integrity sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ== + version "1.5.0" + resolved "https://registry.yarnpkg.com/denque/-/denque-1.5.0.tgz#773de0686ff2d8ec2ff92914316a47b73b1c73de" + integrity sha512-CYiCSgIF1p6EUByQPlGkKnP1M9g0ZV3qMIrqMqZqdwazygIA/YP2vrbcyl1h/WppKJTdl1F85cXIle+394iDAQ== depd@~1.1.2: version "1.1.2" @@ -3153,17 +3375,17 @@ detect-indent@^5.0.0: resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= -devtools-protocol@0.0.799653: - version "0.0.799653" - resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.799653.tgz#86fc95ce5bf4fdf4b77a58047ba9d2301078f119" - integrity sha512-t1CcaZbvm8pOlikqrsIM9GOa7Ipp07+4h/q9u0JXBWjPCjHdBl9KkddX87Vv9vBHoBGtwV79sYQNGnQM6iS5gg== - diff@3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" integrity sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA== -diff@4.0.2, diff@^4.0.1, diff@^4.0.2: +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +diff@^4.0.1, diff@^4.0.2: version "4.0.2" resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== @@ -3207,28 +3429,29 @@ dom-serializer@0: domelementtype "^2.0.1" entities "^2.0.0" -dom-serializer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz#1ec4059e284babed36eec2941d4a970a189ce7c0" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== +dom-serializer@^1.0.1, dom-serializer@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.2.0.tgz#3433d9136aeb3c627981daa385fc7f32d27c48f1" + integrity sha512-n6kZFH/KlCrqs/1GHMOd5i2fd/beQHuehKdWvNNffbGHTr/almdhuVvTVFb3V7fglz+nC50fFusu3lY33h12pA== dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" + domelementtype "^2.0.1" + domhandler "^4.0.0" + entities "^2.0.0" dom-walk@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: +domelementtype@1, domelementtype@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.0.2.tgz#f3b6e549201e46f588b59463dd77187131fe6971" - integrity sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA== +domelementtype@^2.0.1, domelementtype@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== domhandler@^2.3.0: version "2.4.2" @@ -3237,15 +3460,14 @@ domhandler@^2.3.0: dependencies: domelementtype "1" -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= +domhandler@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.0.0.tgz#01ea7821de996d85f69029e81fa873c21833098e" + integrity sha512-KPTbnGQ1JeEMQyO1iYXoagsI6so/C96HZiFyByU3T6iAzpXn8EGEvct6unm1ZGoed8ByO2oirxgwxBmqKF9haA== dependencies: - dom-serializer "0" - domelementtype "1" + domelementtype "^2.1.0" -domutils@^1.5.1: +domutils@^1.5.1, domutils@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== @@ -3253,6 +3475,15 @@ domutils@^1.5.1: dom-serializer "0" domelementtype "1" +domutils@^2.4.3, domutils@^2.4.4: + version "2.4.4" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" + integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.0.1" + domhandler "^4.0.0" + dot-prop@^6.0.0: version "6.0.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" @@ -3317,18 +3548,18 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= -elliptic@6.3.3, elliptic@6.5.3, elliptic@=3.0.3, elliptic@^6.4.0, elliptic@^6.4.1, elliptic@^6.5.2, elliptic@^6.5.3: - version "6.5.3" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.3.tgz#cb59eb2efdaf73a0bd78ccd7015a62ad6e0f93d6" - integrity sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw== +elliptic@6.3.3, elliptic@6.5.3, elliptic@6.5.4, elliptic@=3.0.3, elliptic@^6.4.0, elliptic@^6.4.1, elliptic@^6.5.2, elliptic@^6.5.3: + version "6.5.4" + resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" + integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" + bn.js "^4.11.9" + brorand "^1.1.0" hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" + hmac-drbg "^1.0.1" + inherits "^2.0.4" + minimalistic-assert "^1.0.1" + minimalistic-crypto-utils "^1.0.1" emitter-listener@^1.1.1: version "1.1.2" @@ -3366,6 +3597,17 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding-down@~5.0.0: + version "5.0.4" + resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614" + integrity sha512-8CIZLDcSKxgzT+zX8ZVfgNbu8Md2wq/iqa1Y7zyVR18QBEAc0Nmzuvj/N5ykSKpfGzjM8qxbaFntLPwnVoUhZw== + dependencies: + abstract-leveldown "^5.0.0" + inherits "^2.0.3" + level-codec "^9.0.0" + level-errors "^2.0.0" + xtend "^4.0.1" + end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -3373,7 +3615,7 @@ end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: dependencies: once "^1.4.0" -enquirer@^2.3.5: +enquirer@^2.3.0, enquirer@^2.3.5: version "2.3.6" resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.6.tgz#2a7fe5dd634a1e4125a975ec994ff5456dc3734d" integrity sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg== @@ -3385,16 +3627,33 @@ ent@^2.2.0: resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= -entities@^1.1.1, entities@~1.1.1: +entities@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== +env-paths@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43" + integrity sha512-6u0VYSCo/OW6IoD5WCLLy9JUGARbamfSavcNXry/eu8aHVFei6CD3Sw+VGX5alea1i9pgPHW0mbu6Xj0uBh7gA== + +errno@~0.1.1: + version "0.1.8" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + error-ex@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -3402,23 +3661,25 @@ error-ex@^1.2.0: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1: - version "1.18.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== +es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: + version "1.18.0-next.2" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.2.tgz#088101a55f0541f595e7e057199e27ddc8f3a5c2" + integrity sha512-Ih4ZMFHEtZupnUh6497zEL4y2+w8+1ljnCyaTa+adcoafI1GOvMwFlDjBLfWR7y9VLfrjRJe9ocuHY1PSR9jjw== dependencies: + call-bind "^1.0.2" es-to-primitive "^1.2.1" function-bind "^1.1.1" + get-intrinsic "^1.0.2" has "^1.0.3" has-symbols "^1.0.1" is-callable "^1.2.2" - is-negative-zero "^2.0.0" + is-negative-zero "^2.0.1" is-regex "^1.1.1" - object-inspect "^1.8.0" + object-inspect "^1.9.0" object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.3" + string.prototype.trimstart "^1.0.3" es-to-primitive@^1.2.1: version "1.2.1" @@ -3455,6 +3716,11 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -3538,16 +3804,16 @@ eslint-plugin-node@^11.1.0: semver "^6.1.0" eslint-plugin-prettier@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.4.tgz#168ab43154e2ea57db992a2cd097c828171f75c2" - integrity sha512-jZDa8z76klRqo+TdGDTFJSavwbnWK2ZpqGKNZ+VvweMW516pDUMmQ2koXvxEE4JhzNvTv+radye/bWGBmA6jmg== + version "3.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz#7079cfa2497078905011e6f82e8dd8453d1371b7" + integrity sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ== dependencies: prettier-linter-helpers "^1.0.0" eslint-plugin-promise@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.2.1.tgz#845fd8b2260ad8f82564c1222fce44ad71d9418a" - integrity sha512-VoM09vT7bfA7D+upt+FjeBO5eHIJQBUWki1aPvB+vbNiHS3+oGIJGIeyBtKQTME6UPXXy3vV07OL1tHd3ANuDw== + version "4.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-4.3.1.tgz#61485df2a359e03149fdafc0a68b0e030ad2ac45" + integrity sha512-bY2sGqyptzFBDLh/GMbAxfdJC+b0f23ME63FOE4+Jao0oZ3E1LEwFtWJX/1pGMJLiTtrSSern2CRM/g+dfc0eQ== eslint-plugin-standard@^4.0.1: version "4.1.0" @@ -3580,12 +3846,12 @@ eslint-visitor-keys@^2.0.0: integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== eslint@^7.2.0, eslint@^7.3.1: - version "7.14.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.14.0.tgz#2d2cac1d28174c510a97b377f122a5507958e344" - integrity sha512-5YubdnPXrlrYAFCKybPuHIAH++PINe1pmKNc5wQRB9HSbqIK1ywAnntE3Wwua4giKu0bjligf1gLF6qxMGOYRA== + version "7.20.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-7.20.0.tgz#db07c4ca4eda2e2316e7aa57ac7fc91ec550bdc7" + integrity sha512-qGi0CTcOGP2OtCQBgWZlQjcTuP0XkIpYFj25XtRTQSHC+umNnp7UMshr2G8SLsRFYDdAPFeHOsiteadmMH02Yw== dependencies: - "@babel/code-frame" "^7.0.0" - "@eslint/eslintrc" "^0.2.1" + "@babel/code-frame" "7.12.11" + "@eslint/eslintrc" "^0.3.0" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -3595,10 +3861,10 @@ eslint@^7.2.0, eslint@^7.3.1: eslint-scope "^5.1.1" eslint-utils "^2.1.0" eslint-visitor-keys "^2.0.0" - espree "^7.3.0" - esquery "^1.2.0" + espree "^7.3.1" + esquery "^1.4.0" esutils "^2.0.2" - file-entry-cache "^5.0.1" + file-entry-cache "^6.0.0" functional-red-black-tree "^1.0.1" glob-parent "^5.0.0" globals "^12.1.0" @@ -3609,7 +3875,7 @@ eslint@^7.2.0, eslint@^7.3.1: js-yaml "^3.13.1" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" - lodash "^4.17.19" + lodash "^4.17.20" minimatch "^3.0.4" natural-compare "^1.4.0" optionator "^0.9.1" @@ -3618,17 +3884,17 @@ eslint@^7.2.0, eslint@^7.3.1: semver "^7.2.1" strip-ansi "^6.0.0" strip-json-comments "^3.1.0" - table "^5.2.3" + table "^6.0.4" text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.0.tgz#dc30437cf67947cf576121ebd780f15eeac72348" - integrity sha512-dksIWsvKCixn1yrEXO8UosNSxaDoSYpq9reEjZSbHLpT5hpaCAKTLBwq0RHtLrIr+c0ByiYzWT8KTMRzoRCNlw== +espree@^7.3.0, espree@^7.3.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-7.3.1.tgz#f2df330b752c6f55019f8bd89b7660039c1bbbb6" + integrity sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g== dependencies: acorn "^7.4.0" - acorn-jsx "^5.2.0" + acorn-jsx "^5.3.1" eslint-visitor-keys "^1.3.0" esprima@^4.0.0: @@ -3636,10 +3902,10 @@ esprima@^4.0.0: resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== -esquery@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.3.1.tgz#b78b5828aa8e214e29fb74c4d5b752e1c033da57" - integrity sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ== +esquery@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" + integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== dependencies: estraverse "^5.1.0" @@ -3708,10 +3974,30 @@ eth-lib@^0.1.26: ws "^3.0.0" xhr-request-promise "^0.1.2" +eth-sig-util@^2.5.2: + version "2.5.4" + resolved "https://registry.yarnpkg.com/eth-sig-util/-/eth-sig-util-2.5.4.tgz#577b01fe491b6bf59b0464be09633e20c1677bc5" + integrity sha512-aCMBwp8q/4wrW4QLsF/HYBOSA7TpLKmkVwP3pYQNkEEseW2Rr8Z5Uxc9/h6HX+OG3tuHo+2bINVSihIeBfym6A== + dependencies: + ethereumjs-abi "0.6.8" + ethereumjs-util "^5.1.1" + tweetnacl "^1.0.3" + tweetnacl-util "^0.15.0" + +ethashjs@~0.0.7: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ethashjs/-/ethashjs-0.0.8.tgz#227442f1bdee409a548fb04136e24c874f3aa6f9" + integrity sha512-/MSbf/r2/Ld8o0l15AymjOTlPqpN8Cr4ByUEA9GtR4x0yAh3TdtDzEg29zMjXCNPI7u6E5fOQdj/Cf9Tc7oVNw== + dependencies: + async "^2.1.2" + buffer-xor "^2.0.1" + ethereumjs-util "^7.0.2" + miller-rabin "^4.0.0" + ethereum-bloom-filters@^1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.7.tgz#b7b80735e385dbb7f944ce6b4533e24511306060" - integrity sha512-cDcJJSJ9GMAcURiAWO3DxIEhTL/uWqlQnvgKpuYQzYPrt/izuGU+1ntQmHt0IRq6ADoSYHFnB+aCEFIldjhkMQ== + version "1.0.9" + resolved "https://registry.yarnpkg.com/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.9.tgz#4a59dead803af0c9e33834170bd7695df67061ec" + integrity sha512-GiK/RQkAkcVaEdxKVkPcG07PQ5vD7v2MFSHgZmBJSfMzNRHimntdBithsHAT89tAXnIpzVDWt8iaCD1DvkaxGg== dependencies: js-sha3 "^0.8.0" @@ -3720,7 +4006,7 @@ ethereum-common@^0.0.18: resolved "https://registry.yarnpkg.com/ethereum-common/-/ethereum-common-0.0.18.tgz#2fdc3576f232903358976eb39da783213ff9523f" integrity sha1-L9w1dvIykDNYl26znaeDIT/5Uj8= -ethereum-cryptography@^0.1.3: +ethereum-cryptography@^0.1.2, ethereum-cryptography@^0.1.3: version "0.1.3" resolved "https://registry.yarnpkg.com/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz#8d6143cfc3d74bf79bbd8edecdf29e4ae20dd191" integrity sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ== @@ -3753,6 +4039,50 @@ ethereum-ens@^0.8.0: underscore "^1.8.3" web3 "^1.0.0-beta.34" +ethereumjs-abi@0.6.8, ethereumjs-abi@^0.6.8: + version "0.6.8" + resolved "https://registry.yarnpkg.com/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz#71bc152db099f70e62f108b7cdfca1b362c6fcae" + integrity sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA== + dependencies: + bn.js "^4.11.8" + ethereumjs-util "^6.0.0" + +ethereumjs-account@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ethereumjs-account/-/ethereumjs-account-3.0.0.tgz#728f060c8e0c6e87f1e987f751d3da25422570a9" + integrity sha512-WP6BdscjiiPkQfF9PVfMcwx/rDvfZTjFKY0Uwc09zSQr9JfIVH87dYIJu0gNhBhpmovV4yq295fdllS925fnBA== + dependencies: + ethereumjs-util "^6.0.0" + rlp "^2.2.1" + safe-buffer "^5.1.1" + +ethereumjs-block@^2.2.2, ethereumjs-block@~2.2.2: + version "2.2.2" + resolved "https://registry.yarnpkg.com/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz#c7654be7e22df489fda206139ecd63e2e9c04965" + integrity sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg== + dependencies: + async "^2.0.1" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.1" + ethereumjs-util "^5.0.0" + merkle-patricia-tree "^2.1.2" + +ethereumjs-blockchain@^4.0.3: + version "4.0.4" + resolved "https://registry.yarnpkg.com/ethereumjs-blockchain/-/ethereumjs-blockchain-4.0.4.tgz#30f2228dc35f6dcf94423692a6902604ae34960f" + integrity sha512-zCxaRMUOzzjvX78DTGiKjA+4h2/sF0OYL1QuPux0DHpyq8XiNoF5GYHtb++GUxVlMsMfZV7AVyzbtgcRdIcEPQ== + dependencies: + async "^2.6.1" + ethashjs "~0.0.7" + ethereumjs-block "~2.2.2" + ethereumjs-common "^1.5.0" + ethereumjs-util "^6.1.0" + flow-stoplight "^1.0.0" + level-mem "^3.0.1" + lru-cache "^5.1.1" + rlp "^2.2.2" + semaphore "^1.1.0" + ethereumjs-common@^1.3.2, ethereumjs-common@^1.5.0: version "1.5.2" resolved "https://registry.yarnpkg.com/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz#2065dbe9214e850f2e955a80e650cb6999066979" @@ -3766,7 +4096,7 @@ ethereumjs-tx@^1.3.7: ethereum-common "^0.0.18" ethereumjs-util "^5.0.0" -ethereumjs-tx@^2.1.1: +ethereumjs-tx@^2.1.1, ethereumjs-tx@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz#5dfe7688bf177b45c9a23f86cf9104d47ea35fed" integrity sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw== @@ -3774,7 +4104,7 @@ ethereumjs-tx@^2.1.1: ethereumjs-common "^1.5.0" ethereumjs-util "^6.0.0" -ethereumjs-util@^5.0.0: +ethereumjs-util@^5.0.0, ethereumjs-util@^5.1.1, ethereumjs-util@^5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz#a833f0e5fca7e5b361384dc76301a721f537bf65" integrity sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ== @@ -3787,7 +4117,7 @@ ethereumjs-util@^5.0.0: rlp "^2.0.0" safe-buffer "^5.1.1" -ethereumjs-util@^6.0.0: +ethereumjs-util@^6.0.0, ethereumjs-util@^6.1.0, ethereumjs-util@^6.2.0: version "6.2.1" resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz#fcb4e4dd5ceacb9d2305426ab1a5cd93e3163b69" integrity sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw== @@ -3801,9 +4131,9 @@ ethereumjs-util@^6.0.0: rlp "^2.2.3" ethereumjs-util@^7.0.1, ethereumjs-util@^7.0.2: - version "7.0.7" - resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.7.tgz#484fb9c03b766b2ee64821281070616562fb5a59" - integrity sha512-vU5rtZBlZsgkTw3o6PDKyB8li2EgLavnAbsKcfsH2YhHH1Le+PP8vEiMnAnvgc1B6uMoaM5GDCrVztBw0Q5K9g== + version "7.0.8" + resolved "https://registry.yarnpkg.com/ethereumjs-util/-/ethereumjs-util-7.0.8.tgz#5258762b7b17e3d828e41834948363ff0a703ffd" + integrity sha512-JJt7tDpCAmDPw/sGoFYeq0guOVqT3pTE9xlEbBmc/nlCij3JRCoS2c96SQ6kXVHOT3xWUNLDm5QCJLQaUnVAtQ== dependencies: "@types/bn.js" "^4.11.3" bn.js "^5.1.2" @@ -3877,7 +4207,7 @@ ethers@5.0.0: "@ethersproject/web" "^5.0.0" "@ethersproject/wordlists" "^5.0.0" -ethers@5.0.26, ethers@^5.0.26: +ethers@5.0.26: version "5.0.26" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.26.tgz#ef43c6b6aad71f10c1a184003f69b142d7d03bae" integrity sha512-MqA8Fvutn3qEW0yBJOHeV6KZmRpF2rqlL2B5058AGkUFsuu6j5Ns/FRlMsbGeQwBz801IB23jQp7vjRfFsKSkg== @@ -3913,7 +4243,7 @@ ethers@5.0.26, ethers@^5.0.26: "@ethersproject/web" "5.0.12" "@ethersproject/wordlists" "5.0.8" -ethers@^4.0.0-beta.1, ethers@^4.0.20, ethers@^4.0.27, ethers@^4.0.32, ethers@^4.0.45, ethers@^4.0.48: +ethers@^4.0.20, ethers@^4.0.27, ethers@^4.0.32, ethers@^4.0.45, ethers@^4.0.48: version "4.0.48" resolved "https://registry.yarnpkg.com/ethers/-/ethers-4.0.48.tgz#330c65b8133e112b0613156e57e92d9009d8fbbe" integrity sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g== @@ -3928,6 +4258,42 @@ ethers@^4.0.0-beta.1, ethers@^4.0.20, ethers@^4.0.27, ethers@^4.0.32, ethers@^4. uuid "2.0.1" xmlhttprequest "1.8.0" +ethers@^5.0.26: + version "5.0.31" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.0.31.tgz#60e3b1425864fe5d2babc147ede01be8382a7d2a" + integrity sha512-zpq0YbNFLFn+t+ibS8UkVWFeK5w6rVMSvbSHrHAQslfazovLnQ/mc2gdN5+6P45/k8fPgHrfHrYvJ4XvyK/S1A== + dependencies: + "@ethersproject/abi" "5.0.12" + "@ethersproject/abstract-provider" "5.0.9" + "@ethersproject/abstract-signer" "5.0.13" + "@ethersproject/address" "5.0.10" + "@ethersproject/base64" "5.0.8" + "@ethersproject/basex" "5.0.8" + "@ethersproject/bignumber" "5.0.14" + "@ethersproject/bytes" "5.0.10" + "@ethersproject/constants" "5.0.9" + "@ethersproject/contracts" "5.0.11" + "@ethersproject/hash" "5.0.11" + "@ethersproject/hdnode" "5.0.9" + "@ethersproject/json-wallets" "5.0.11" + "@ethersproject/keccak256" "5.0.8" + "@ethersproject/logger" "5.0.9" + "@ethersproject/networks" "5.0.8" + "@ethersproject/pbkdf2" "5.0.8" + "@ethersproject/properties" "5.0.8" + "@ethersproject/providers" "5.0.23" + "@ethersproject/random" "5.0.8" + "@ethersproject/rlp" "5.0.8" + "@ethersproject/sha2" "5.0.8" + "@ethersproject/signing-key" "5.0.10" + "@ethersproject/solidity" "5.0.9" + "@ethersproject/strings" "5.0.9" + "@ethersproject/transactions" "5.0.10" + "@ethersproject/units" "5.0.10" + "@ethersproject/wallet" "5.0.11" + "@ethersproject/web" "5.0.13" + "@ethersproject/wordlists" "5.0.9" + ethjs-unit@0.1.6, ethjs-unit@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/ethjs-unit/-/ethjs-unit-0.1.6.tgz#c665921e476e87bce2a9d588a6fe0405b2c41699" @@ -4051,17 +4417,6 @@ extend@^3.0.2, extend@~3.0.2: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -extract-zip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a" - integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== - dependencies: - debug "^4.1.1" - get-stream "^5.1.0" - yauzl "^2.10.0" - optionalDependencies: - "@types/yauzl" "^2.9.1" - extsprintf@1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" @@ -4072,6 +4427,13 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= +fake-merkle-patricia-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz#4b8c3acfb520afadf9860b1f14cd8ce3402cddd3" + integrity sha1-S4w6z7Ugr635hgsfFM2M40As3dM= + dependencies: + checkpoint-store "^1.1.0" + fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4097,7 +4459,7 @@ fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.7: resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== -fast-text-encoding@^1.0.0: +fast-text-encoding@^1.0.0, fast-text-encoding@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== @@ -4114,12 +4476,12 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -file-entry-cache@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-5.0.1.tgz#ca0f6efa6dd3d561333fb14515065c2fafdf439c" - integrity sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g== +file-entry-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.0.tgz#7921a89c391c6d93efec2169ac6bf300c527ea0a" + integrity sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA== dependencies: - flat-cache "^2.0.1" + flat-cache "^3.0.4" file-type@^3.8.0: version "3.9.0" @@ -4183,22 +4545,13 @@ find-up@^2.0.0, find-up@^2.1.0: dependencies: locate-path "^2.0.0" -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -flat-cache@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" - integrity sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA== +flat-cache@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" + integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== dependencies: - flatted "^2.0.0" - rimraf "2.6.3" - write "1.0.3" + flatted "^3.1.0" + rimraf "^3.0.2" flat@^4.1.0: version "4.1.1" @@ -4212,10 +4565,15 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== +flatted@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469" + integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA== + +flow-stoplight@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/flow-stoplight/-/flow-stoplight-1.0.0.tgz#4a292c5bcff8b39fa6cc0cb1a853d86f27eeff7b" + integrity sha1-SiksW8/4s5+mzAyxqFPYbyfu/3s= fn.name@1.x.x: version "1.1.0" @@ -4229,10 +4587,22 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.10.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.1.tgz#5f69b813376cee4fd0474a3aba835df04ab763b7" - integrity sha512-SSG5xmZh1mkPGyKzjZP8zLjltIfpW32Y5QpdNJyjcfGxK3qo3NDDkZOZSFiGn1A6SclQxY9GzEwAHQ3dmYRWpg== +follow-redirects@^1.10.0, follow-redirects@^1.12.1: + version "1.13.2" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.2.tgz#dd73c8effc12728ba5cf4259d760ea5fb83e3147" + integrity sha512-6mPTgLxYm3r6Bkkg0vNM0HTjfGrOEtsfbhagQvbxDEsEkpNhw582upBaoRZylzen6krEmxXJgt9Ju6HiI4O7BA== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +foreach@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" + integrity sha1-C+4AUBiusmDQo6865ljdATbsG5k= forever-agent@~0.6.1: version "0.6.1" @@ -4240,9 +4610,9 @@ forever-agent@~0.6.1: integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= form-data@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.0.tgz#31b7e39c85f1355b7139ee0c647cf0de7f83c682" - integrity sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg== + version "3.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-3.0.1.tgz#ebd53791b78356a99af9a300d4282c4d5eb9755f" + integrity sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg== dependencies: asynckit "^0.4.0" combined-stream "^1.0.8" @@ -4267,6 +4637,16 @@ forwarded@~0.1.2: resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= +fp-ts@1.19.3: + version "1.19.3" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.3.tgz#261a60d1088fbff01f91256f91d21d0caaaaa96f" + integrity sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg== + +fp-ts@^1.0.0: + version "1.19.5" + resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-1.19.5.tgz#3da865e585dfa1fdfd51785417357ac50afc520a" + integrity sha512-wDNqTimnzs8QqpldiId9OavWK2NptormjXnRJTQecNjzwfyp6P/8s/zG8e4h3ja3oqkKaY72UlTjQYt/1yXf9A== + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -4277,6 +4657,17 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" + integrity sha1-8jP/zAjU2n1DLapEl3aYnbHfk/A= + dependencies: + graceful-fs "^4.1.2" + jsonfile "^2.1.0" + klaw "^1.0.0" + path-is-absolute "^1.0.0" + rimraf "^2.2.8" + fs-extra@^4.0.2: version "4.0.3" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.3.tgz#0d852122e5bc5beb453fb028e9c0c9bf36340c94" @@ -4286,6 +4677,15 @@ fs-extra@^4.0.2: jsonfile "^4.0.0" universalify "^0.1.0" +fs-extra@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" + integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -4298,25 +4698,30 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= -fsevents@~2.1.1, fsevents@~2.1.2: +fsevents@~2.1.1: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== +fsevents@~2.3.1: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -functional-red-black-tree@^1.0.1: +functional-red-black-tree@^1.0.1, functional-red-black-tree@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= gaxios@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.0.1.tgz#bc7b205a89d883452822cc75e138620c35e3291e" - integrity sha512-jOin8xRZ/UytQeBpSXFqIzqU7Fi5TqgPNLlUsSB8kjJ76+FiGBfImF8KJu++c6J4jOldfJUtt0YmkRj2ZpSHTQ== + version "4.1.0" + resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-4.1.0.tgz#e8ad466db5a4383c70b9d63bfd14dfaa87eb0099" + integrity sha512-vb0to8xzGnA2qcgywAjtshOKKVDf2eQhJoiL6fHhgW5tVN7wNk7egnYIO9zotfn3lQ3De1VPdf7V5/BWfCtCmg== dependencies: abort-controller "^3.0.0" extend "^3.0.2" @@ -4332,7 +4737,7 @@ gcp-metadata@^4.0.0, gcp-metadata@^4.2.0: gaxios "^4.0.0" json-bigint "^1.0.0" -get-caller-file@^2.0.1: +get-caller-file@^2.0.1, get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== @@ -4342,10 +4747,10 @@ get-func-name@^2.0.0: resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" integrity sha1-6td0q+5y4gQJQzoGY2YCPdaIekE= -get-intrinsic@^1.0.0, get-intrinsic@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" - integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== +get-intrinsic@^1.0.1, get-intrinsic@^1.0.2: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== dependencies: function-bind "^1.1.1" has "^1.0.3" @@ -4436,10 +4841,25 @@ globals@^12.1.0: dependencies: type-fest "^0.8.1" -google-auth-library@^6.0.0, google-auth-library@^6.1.1, google-auth-library@^6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.3.tgz#39d868140b70d0c4b32c6f6d8f4ccc1400d84dca" - integrity sha512-m9mwvY3GWbr7ZYEbl61isWmk+fvTmOt0YNUfPOUY2VH8K5pZlAIWJjxEi0PqR3OjMretyiQLI6GURMrPSwHQ2g== +google-auth-library@^6.1.1: + version "6.1.6" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-6.1.6.tgz#deacdcdb883d9ed6bac78bb5d79a078877fdf572" + integrity sha512-Q+ZjUEvLQj/lrVHF/IQwRo6p3s8Nc44Zk/DALsN+ac3T4HY/g/3rrufkgtl+nZ1TW7DNAw5cTChdVp4apUXVgQ== + dependencies: + arrify "^2.0.0" + base64-js "^1.3.0" + ecdsa-sig-formatter "^1.0.11" + fast-text-encoding "^1.0.0" + gaxios "^4.0.0" + gcp-metadata "^4.2.0" + gtoken "^5.0.4" + jws "^4.0.0" + lru-cache "^6.0.0" + +google-auth-library@^7.0.0, google-auth-library@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-7.0.2.tgz#cab6fc7f94ebecc97be6133d6519d9946ccf3e9d" + integrity sha512-vjyNZR3pDLC0u7GHLfj+Hw9tGprrJwoMwkYGqURCXYITjCrP9HprOyxVV+KekdLgATtWGuDkQG2MTh0qpUPUgg== dependencies: arrify "^2.0.0" base64-js "^1.3.0" @@ -4452,19 +4872,20 @@ google-auth-library@^6.0.0, google-auth-library@^6.1.1, google-auth-library@^6.1 lru-cache "^6.0.0" google-gax@^2.9.2: - version "2.9.2" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.9.2.tgz#780b2c0fc031c864007e1e198a9b90c7e946cca0" - integrity sha512-Pve4osEzNKpBZqFXMfGKBbKCtgnHpUe5IQMh5Ou+Xtg8nLcba94L3gF0xgM5phMdGRRqJn0SMjcuEVmOYu7EBg== + version "2.10.3" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-2.10.3.tgz#53278bb5cc4b654876ade774a249f0241fdb15cc" + integrity sha512-jESs/ME9WgMzfGQKJDu9ea2mEKjznKByRL+5xb8mKfHlbUfS/LxNLNCg/35RgXwVXcNSCqkEY90z8wHxvgdd/Q== dependencies: - "@grpc/grpc-js" "~1.1.1" + "@grpc/grpc-js" "~1.2.0" "@grpc/proto-loader" "^0.5.1" "@types/long" "^4.0.0" abort-controller "^3.0.0" duplexify "^4.0.0" - google-auth-library "^6.1.3" + fast-text-encoding "^1.0.3" + google-auth-library "^7.0.2" is-stream-ended "^0.1.4" node-fetch "^2.6.1" - protobufjs "^6.9.0" + protobufjs "^6.10.2" retry-request "^4.0.0" google-p12-pem@^3.0.3: @@ -4511,10 +4932,10 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.2.4" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" - integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== +graceful-fs@^4.1.10, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9: + version "4.2.6" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" + integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== growl@1.10.5: version "1.10.5" @@ -4522,14 +4943,13 @@ growl@1.10.5: integrity sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA== gtoken@^5.0.4: - version "5.1.0" - resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.1.0.tgz#4ba8d2fc9a8459098f76e7e8fd7beaa39fda9fe4" - integrity sha512-4d8N6Lk8TEAHl9vVoRVMh9BNOKWVgl2DdNtr3428O75r3QFrF/a5MMu851VmK0AA8+iSvbwRv69k5XnMLURGhg== + version "5.2.1" + resolved "https://registry.yarnpkg.com/gtoken/-/gtoken-5.2.1.tgz#4dae1fea17270f457954b4a45234bba5fc796d16" + integrity sha512-OY0BfPKe3QnMsY9MzTHTSKn+Vl2l1CcLe6BwDEQj00mbbkl5nyQ/7EUREstg4fQNZ8iYE7br4JJ7TdKeDOPWmw== dependencies: gaxios "^4.0.0" google-p12-pem "^3.0.3" jws "^4.0.0" - mime "^2.2.0" har-schema@^2.0.0: version "2.0.0" @@ -4544,6 +4964,56 @@ har-validator@~5.1.3: ajv "^6.12.3" har-schema "^2.0.0" +hardhat@^2.0.8: + version "2.0.10" + resolved "https://registry.yarnpkg.com/hardhat/-/hardhat-2.0.10.tgz#9b50da13b6915bb9b61b7f38f8f2b9b352447462" + integrity sha512-ZAcC+9Nb1AEb22/2hWj/zLPyIRLD9y1O3LW2KhbONpxn1bf0qWLW8QegB9J3KP9Bvt8LbW9pWuSyRQJU0vUWqA== + dependencies: + "@nomiclabs/ethereumjs-vm" "4.2.2" + "@sentry/node" "^5.18.1" + "@solidity-parser/parser" "^0.11.0" + "@types/bn.js" "^4.11.5" + "@types/lru-cache" "^5.1.0" + abort-controller "^3.0.0" + adm-zip "^0.4.16" + ansi-escapes "^4.3.0" + chalk "^2.4.2" + chokidar "^3.4.0" + ci-info "^2.0.0" + debug "^4.1.1" + enquirer "^2.3.0" + env-paths "^2.2.0" + eth-sig-util "^2.5.2" + ethereum-cryptography "^0.1.2" + ethereumjs-abi "^0.6.8" + ethereumjs-account "^3.0.0" + ethereumjs-block "^2.2.2" + ethereumjs-common "^1.5.0" + ethereumjs-tx "^2.1.2" + ethereumjs-util "^6.2.0" + find-up "^2.1.0" + fp-ts "1.19.3" + fs-extra "^7.0.1" + glob "^7.1.3" + immutable "^4.0.0-rc.12" + io-ts "1.10.4" + lodash "^4.17.11" + merkle-patricia-tree "3.0.0" + mocha "^7.1.2" + node-fetch "^2.6.0" + qs "^6.7.0" + raw-body "^2.4.1" + resolve "1.17.0" + semver "^6.3.0" + slash "^3.0.0" + solc "0.7.3" + source-map-support "^0.5.13" + stacktrace-parser "^0.1.10" + "true-case-path" "^2.2.1" + tsort "0.0.1" + uuid "^3.3.2" + ws "^7.2.1" + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4622,22 +5092,17 @@ hex2dec@^1.0.1: resolved "https://registry.yarnpkg.com/hex2dec/-/hex2dec-1.1.2.tgz#8e1ce4bef36a74f7d5723c3fb3090c2860077338" integrity sha512-Yu+q/XWr2fFQ11tHxPq4p4EiNkb2y+lAacJNhAdRXVfRIcDH6gi7htWFnnlIzvqHMHoWeIsfXlNAjZInpAOJDA== -highlight.js@^10.4.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.4.0.tgz#ef3ce475e5dfa7a48484260b49ea242ddab823a0" - integrity sha512-EfrUGcQ63oLJbj0J0RI9ebX6TAITbsDBLbsjr881L/X5fMO9+oadKzEF21C7R3ULKG6Gv3uoab2HiqVJa/4+oA== - -highlight.js@^9.12.0: - version "9.18.5" - resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-9.18.5.tgz#d18a359867f378c138d6819edfc2a8acd5f29825" - integrity sha512-a5bFyofd/BHCX52/8i8uJkjr9DYwXIPnM/plwI6W7ezItLGqzt7X2G2nXuYSfsIJdkwwj/g9DG1LkcGJI/dDoA== +highlight.js@^10.4.0, highlight.js@^10.4.1: + version "10.6.0" + resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.6.0.tgz#0073aa71d566906965ba6e1b7be7b2682f5e18b6" + integrity sha512-8mlRcn5vk/r4+QcqerapwBYTe+iPL5ih6xrNylxrnBdHQiijDETfXX7VIxC3UiCRiINBJfANBAsPzAvRQj8RpQ== -highlightjs-solidity@^1.0.19: - version "1.0.19" - resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.0.19.tgz#a2f05bcfadd295a8eefb9cc20dcb18a3ba48e49c" - integrity sha512-ZIzMlxZxkcNnzWC1LeOeUjQjywzXnGyDxexOPKzz8hWFqdE2uRvz1BxD0joOkr41z4SU2ABXVGDx6EWlbzTBLQ== +highlightjs-solidity@^1.0.21: + version "1.0.21" + resolved "https://registry.yarnpkg.com/highlightjs-solidity/-/highlightjs-solidity-1.0.21.tgz#6d257215b5b635231d4d0c523f2c419bbff6fe42" + integrity sha512-ozOtTD986CBIxuIuauzz2lqCOTpd27TbfYm+msMtNSB69mJ0cdFNvZ6rOO5iFtEHtDkVYVEFQywXffG2sX3XTw== -hmac-drbg@^1.0.0, hmac-drbg@^1.0.1: +hmac-drbg@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= @@ -4651,7 +5116,7 @@ hosted-git-info@^2.1.4, hosted-git-info@^2.6.0: resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== -htmlparser2@^3.3.0, htmlparser2@^3.9.1: +htmlparser2@^3.10.1: version "3.10.1" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== @@ -4663,6 +5128,16 @@ htmlparser2@^3.3.0, htmlparser2@^3.9.1: inherits "^2.0.1" readable-stream "^3.1.1" +htmlparser2@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.0.0.tgz#c2da005030390908ca4c91e5629e418e0665ac01" + integrity sha512-numTQtDZMoh78zJpaNdJ9MXb2cv5G3jwUoe3dMQODubZvLoGvTE/Ofp6sHvH8OGKcN/8A47pGLi/k58xHP/Tfw== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.4.4" + entities "^2.0.0" + http-cache-semantics@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -4679,7 +5154,7 @@ http-errors@1.7.2: statuses ">= 1.5.0 < 2" toidentifier "1.0.0" -http-errors@~1.7.2: +http-errors@1.7.3, http-errors@~1.7.2: version "1.7.3" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== @@ -4713,14 +5188,6 @@ http-signature@~1.2.0: jsprim "^1.2.2" sshpk "^1.7.0" -https-proxy-agent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz#702b71fb5520a132a66de1f67541d9e62154d82b" - integrity sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg== - dependencies: - agent-base "5" - debug "4" - https-proxy-agent@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2" @@ -4758,15 +5225,25 @@ ignore@^5.1.1: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== +immediate@^3.2.3: + version "3.3.0" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.3.0.tgz#1aef225517836bcdf7f2a2de2600c79ff0269266" + integrity sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q== + +immediate@~3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c" + integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw= + immutable@^4.0.0-rc.12: version "4.0.0-rc.12" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.0.0-rc.12.tgz#ca59a7e4c19ae8d9bf74a97bdf0f6e2f2a5d0217" integrity sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A== import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.2.2" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e" - integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw== + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== dependencies: parent-module "^1.0.0" resolve-from "^4.0.0" @@ -4784,7 +5261,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -4799,11 +5276,25 @@ inherits@=2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +io-ts@1.10.4: + version "1.10.4" + resolved "https://registry.yarnpkg.com/io-ts/-/io-ts-1.10.4.tgz#cd5401b138de88e4f920adbcb7026e2d1967e6e2" + integrity sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g== + dependencies: + fp-ts "^1.0.0" + ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== +is-arguments@^1.0.4: + version "1.1.0" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" + integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== + dependencies: + call-bind "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -4826,12 +5317,12 @@ is-buffer@^2.0.2, is-buffer@~2.0.3: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== -is-callable@^1.1.4, is-callable@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== -is-core-module@^2.1.0: +is-core-module@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== @@ -4863,6 +5354,11 @@ is-function@^1.0.1: resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.2.tgz#4f097f30abf6efadac9833b17ca5dc03f8144e08" integrity sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ== +is-generator-function@^1.0.7: + version "1.0.8" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.8.tgz#dfb5c2b120e02b0a8d9d2c6806cd5621aa922f7b" + integrity sha512-2Omr/twNtufVZFr1GhxjOMFPAj2sjc/dKaIqBhvo4qciXfJmITGH6ZGd8eZYNHza8t1y0e01AuqRhJwfWp26WQ== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" @@ -4880,10 +5376,10 @@ is-natural-number@^4.0.1: resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= -is-negative-zero@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" - integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= +is-negative-zero@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" + integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== is-number@^7.0.0: version "7.0.0" @@ -4896,9 +5392,9 @@ is-obj@^2.0.0: integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - integrity sha1-iVJojF7C/9awPsyF52ngKQMINHA= + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.2.tgz#a56552e1c665c9e950b4a025461da87e72f86fcf" + integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== is-plain-obj@^1.1.0: version "1.1.0" @@ -4911,10 +5407,11 @@ is-plain-obj@^2.1.0: integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== + version "1.1.2" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" + integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== dependencies: + call-bind "^1.0.2" has-symbols "^1.0.1" is-retry-allowed@^1.0.0: @@ -4949,6 +5446,17 @@ is-symbol@^1.0.2: dependencies: has-symbols "^1.0.1" +is-typed-array@^1.1.3: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.5.tgz#f32e6e096455e329eb7b423862456aa213f0eb4e" + integrity sha512-S+GRDgJlR3PyEbsX/Fobd9cqpZBuvUS+8asRqYDMLCb2qMzt1oz5m5oxQCxOgUDxiWsOVNi4yaF+/uvdlHlYug== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.2" + es-abstract "^1.18.0-next.2" + foreach "^2.0.5" + has-symbols "^1.0.1" + is-typedarray@1.0.0, is-typedarray@^1.0.0, is-typedarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4974,11 +5482,6 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= -iso-url@~0.4.7: - version "0.4.7" - resolved "https://registry.yarnpkg.com/iso-url/-/iso-url-0.4.7.tgz#de7e48120dae46921079fe78f325ac9e9217a385" - integrity sha512-27fFRDnPAMnHGLq36bWTpKET+eiXct3ENlCcdcMdk+mjXrb2kw3mhBUg1B7ewAC0kVzlOPhADzQgz1SE6Tglog== - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -5001,12 +5504,27 @@ jest-changed-files@^24.9.0: execa "^1.0.0" throat "^4.0.0" +js-conflux-sdk@^1.5.11: + version "1.5.12" + resolved "https://registry.yarnpkg.com/js-conflux-sdk/-/js-conflux-sdk-1.5.12.tgz#9f79205ec6903401cb9a1deaa03cd62d83eec70d" + integrity sha512-xSYIQwcReiF89zeDOiy6kg+ojYDxrWE+9tBT7h64NHHgM51BvU9VrFGj1s/VEDi766WJ9zrlVkB3SRZzXpHcsA== + dependencies: + "@babel/runtime" "^7.8.4" + big.js "^5.2.2" + conflux-address-js "^1.0.4" + keccak "^2.0.0" + lodash "^4.17.19" + scrypt-js "^3.0.1" + secp256k1 "^3.7.1" + superagent "^5.1.0" + websocket "^1.0.31" + js-sha3@0.5.7, js-sha3@^0.5.7: version "0.5.7" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.5.7.tgz#0d4ffd8002d5333aabaf4a23eed2f6374c9f28e7" integrity sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc= -js-sha3@^0.8.0: +js-sha3@0.8.0, js-sha3@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== @@ -5024,10 +5542,17 @@ js-yaml@3.13.1: argparse "^1.0.7" esprima "^4.0.0" -js-yaml@3.14.0, js-yaml@^3.13.1: - version "3.14.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482" - integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A== +js-yaml@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.0.0.tgz#f426bc0ff4b4051926cd588c71113183409a121f" + integrity sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q== + dependencies: + argparse "^2.0.1" + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" @@ -5054,6 +5579,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" @@ -5069,7 +5599,7 @@ json-stringify-safe@~5.0.1: resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= -json-text-sequence@^0.1, json-text-sequence@~0.1.0: +json-text-sequence@^0.1: version "0.1.1" resolved "https://registry.yarnpkg.com/json-text-sequence/-/json-text-sequence-0.1.1.tgz#a72f217dc4afc4629fff5feb304dc1bd51a2f3d2" integrity sha1-py8hfcSvxGKf/1/rME3BvVGi89I= @@ -5083,6 +5613,13 @@ json5@^1.0.1: dependencies: minimist "^1.2.0" +jsonfile@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" + integrity sha1-NzaitCi4e72gzIO1P6PWM6NcKug= + optionalDependencies: + graceful-fs "^4.1.6" + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -5135,6 +5672,16 @@ keccak256@^1.0.2: bn.js "^4.11.8" keccak "^3.0.1" +keccak@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/keccak/-/keccak-2.1.0.tgz#734ea53f2edcfd0f42cdb8d5f4c358fef052752b" + integrity sha512-m1wbJRTo+gWbctZWay9i26v5fFnYkOn7D5PCxJ3fZUGUEb49dE1Pm4BREUYCt/aoO6di7jeoGmhvqN9Nzylm3Q== + dependencies: + bindings "^1.5.0" + inherits "^2.0.4" + nan "^2.14.0" + safe-buffer "^5.2.0" + keccak@^3.0.0, keccak@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/keccak/-/keccak-3.0.1.tgz#ae30a0e94dbe43414f741375cff6d64c8bea0bff" @@ -5150,17 +5697,125 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" +klaw@^1.0.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/klaw/-/klaw-1.3.1.tgz#4088433b46b3b1ba259d78785d8e96f73ba02439" + integrity sha1-QIhDO0azsbolnXh4XY6W9zugJDk= + optionalDependencies: + graceful-fs "^4.1.9" + kuler@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/kuler/-/kuler-2.0.0.tgz#e2c570a3800388fb44407e851531c1d670b061b3" integrity sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A== -lambdafs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lambdafs/-/lambdafs-2.0.0.tgz#41c982486cdf5357d6ae447c47506e7b01952c40" - integrity sha512-aLZoFHQGOj1gShKC8xjbVcxkSVMTav6kN/+bAJ/ORTWzTA+TeUKZRjzIIFFER5wUHd2FyJd3KygdFJobH2I9cg== +level-codec@^9.0.0: + version "9.0.2" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.2.tgz#fd60df8c64786a80d44e63423096ffead63d8cbc" + integrity sha512-UyIwNb1lJBChJnGfjmO0OR+ezh2iVu1Kas3nvBS/BzGnx79dv6g7unpKIDNPMhfdTEGoc7mC8uAu51XEtX+FHQ== + dependencies: + buffer "^5.6.0" + +level-codec@~7.0.0: + version "7.0.1" + resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-7.0.1.tgz#341f22f907ce0f16763f24bddd681e395a0fb8a7" + integrity sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ== + +level-errors@^1.0.3: + version "1.1.2" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.1.2.tgz#4399c2f3d3ab87d0625f7e3676e2d807deff404d" + integrity sha512-Sw/IJwWbPKF5Ai4Wz60B52yj0zYeqzObLh8k1Tk88jVmD51cJSKWSYpRyhVIvFzZdvsPqlH5wfhp/yxdsaQH4w== + dependencies: + errno "~0.1.1" + +level-errors@^2.0.0, level-errors@~2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.1.tgz#2132a677bf4e679ce029f517c2f17432800c05c8" + integrity sha512-UVprBJXite4gPS+3VznfgDSU8PTRuVX0NXwoWW50KLxd2yw4Y1t2JUR5In1itQnudZqRMT9DlAM3Q//9NCjCFw== + dependencies: + errno "~0.1.1" + +level-errors@~1.0.3: + version "1.0.5" + resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-1.0.5.tgz#83dbfb12f0b8a2516bdc9a31c4876038e227b859" + integrity sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig== + dependencies: + errno "~0.1.1" + +level-iterator-stream@~1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz#e43b78b1a8143e6fa97a4f485eb8ea530352f2ed" + integrity sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0= + dependencies: + inherits "^2.0.1" + level-errors "^1.0.3" + readable-stream "^1.0.33" + xtend "^4.0.0" + +level-iterator-stream@~3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-3.0.1.tgz#2c98a4f8820d87cdacab3132506815419077c730" + integrity sha512-nEIQvxEED9yRThxvOrq8Aqziy4EGzrxSZK+QzEFAVuJvQ8glfyZ96GB6BoI4sBbLfjMXm2w4vu3Tkcm9obcY0g== + dependencies: + inherits "^2.0.1" + readable-stream "^2.3.6" + xtend "^4.0.0" + +level-mem@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/level-mem/-/level-mem-3.0.1.tgz#7ce8cf256eac40f716eb6489654726247f5a89e5" + integrity sha512-LbtfK9+3Ug1UmvvhR2DqLqXiPW1OJ5jEh0a3m9ZgAipiwpSxGj/qaVVy54RG5vAQN1nCuXqjvprCuKSCxcJHBg== + dependencies: + level-packager "~4.0.0" + memdown "~3.0.0" + +level-packager@~4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/level-packager/-/level-packager-4.0.1.tgz#7e7d3016af005be0869bc5fa8de93d2a7f56ffe6" + integrity sha512-svCRKfYLn9/4CoFfi+d8krOtrp6RoX8+xm0Na5cgXMqSyRru0AnDYdLl+YI8u1FyS6gGZ94ILLZDE5dh2but3Q== + dependencies: + encoding-down "~5.0.0" + levelup "^3.0.0" + +level-ws@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-0.0.0.tgz#372e512177924a00424b0b43aef2bb42496d228b" + integrity sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos= + dependencies: + readable-stream "~1.0.15" + xtend "~2.1.1" + +level-ws@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/level-ws/-/level-ws-1.0.0.tgz#19a22d2d4ac57b18cc7c6ecc4bd23d899d8f603b" + integrity sha512-RXEfCmkd6WWFlArh3X8ONvQPm8jNpfA0s/36M4QzLqrLEIt1iJE9WBHLZ5vZJK6haMjJPJGJCQWfjMNnRcq/9Q== + dependencies: + inherits "^2.0.3" + readable-stream "^2.2.8" + xtend "^4.0.1" + +levelup@^1.2.1: + version "1.3.9" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-1.3.9.tgz#2dbcae845b2bb2b6bea84df334c475533bbd82ab" + integrity sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ== dependencies: - tar-fs "^2.1.0" + deferred-leveldown "~1.2.1" + level-codec "~7.0.0" + level-errors "~1.0.3" + level-iterator-stream "~1.3.0" + prr "~1.0.1" + semver "~5.4.1" + xtend "~4.0.0" + +levelup@^3.0.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189" + integrity sha512-9N10xRkUU4dShSRRFTBdNaBxofz+PGaIZO962ckboJZiNmLuhVT6FZ6ZKAsICKfUBO76ySaYU6fJWX/jnj3Lcg== + dependencies: + deferred-leveldown "~4.0.0" + level-errors "~2.0.0" + level-iterator-stream "~3.0.0" + xtend "~4.0.0" levn@^0.4.1: version "0.4.1" @@ -5196,13 +5851,6 @@ locate-path@^3.0.0: p-locate "^3.0.0" path-exists "^3.0.0" -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -5255,7 +5903,7 @@ lodash.sum@^4.0.2: resolved "https://registry.yarnpkg.com/lodash.sum/-/lodash.sum-4.0.2.tgz#ad90e397965d803d4f1ff7aa5b2d0197f3b4637b" integrity sha1-rZDjl5ZdgD1PH/eqWy0Bl/O0Y3s= -lodash@=3.10.1, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.4: +lodash@=3.10.1, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.4: version "4.17.20" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== @@ -5305,6 +5953,13 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" + integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== + dependencies: + yallist "^3.0.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -5312,10 +5967,20 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru_map@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" + integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= + +ltgt@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5" + integrity sha1-81ypHEk/e3PaDgdJUwTxezH4fuU= + luxon@^1.23.0: - version "1.25.0" - resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72" - integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ== + version "1.26.0" + resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578" + integrity sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A== make-dir@^1.0.0: version "1.3.0" @@ -5355,6 +6020,35 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +memdown@^1.0.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-1.4.1.tgz#b4e4e192174664ffbae41361aa500f3119efe215" + integrity sha1-tOThkhdGZP+65BNhqlAPMRnv4hU= + dependencies: + abstract-leveldown "~2.7.1" + functional-red-black-tree "^1.0.1" + immediate "^3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memdown@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/memdown/-/memdown-3.0.0.tgz#93aca055d743b20efc37492e9e399784f2958309" + integrity sha512-tbV02LfZMWLcHcq4tw++NuqMO+FZX8tNJEiD2aNRm48ZZusVg5N8NART+dmBkepJVye986oixErf7jfXboMGMA== + dependencies: + abstract-leveldown "~5.0.0" + functional-red-black-tree "~1.0.1" + immediate "~3.2.3" + inherits "~2.0.1" + ltgt "~2.2.0" + safe-buffer "~5.1.1" + +memorystream@^0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/memorystream/-/memorystream-0.3.1.tgz#86d7090b30ce455d63fbae12dda51a47ddcaf9b2" + integrity sha1-htcJCzDORV1j+64S3aUaR93K+bI= + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -5365,6 +6059,33 @@ merkle-lib@^2.0.10: resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326" integrity sha1-grjbrnXieneFOItz+ddyXQ9vMyY= +merkle-patricia-tree@3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-3.0.0.tgz#448d85415565df72febc33ca362b8b614f5a58f8" + integrity sha512-soRaMuNf/ILmw3KWbybaCjhx86EYeBbD8ph0edQCTed0JN/rxDt1EBN52Ajre3VyGo+91f8+/rfPIRQnnGMqmQ== + dependencies: + async "^2.6.1" + ethereumjs-util "^5.2.0" + level-mem "^3.0.1" + level-ws "^1.0.0" + readable-stream "^3.0.6" + rlp "^2.0.0" + semaphore ">=1.0.1" + +merkle-patricia-tree@^2.1.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz#982ca1b5a0fde00eed2f6aeed1f9152860b8208a" + integrity sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g== + dependencies: + async "^1.4.2" + ethereumjs-util "^5.0.0" + level-ws "0.0.0" + levelup "^1.2.1" + memdown "^1.0.0" + readable-stream "^2.0.0" + rlp "^2.0.0" + semaphore ">=1.0.1" + methods@^1.1.2, methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" @@ -5378,27 +6099,27 @@ miller-rabin@^4.0.0: bn.js "^4.0.0" brorand "^1.0.1" -mime-db@1.44.0: - version "1.44.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.44.0.tgz#fa11c5eb0aca1334b4233cb4d52f10c5a6272f92" - integrity sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg== +mime-db@1.46.0: + version "1.46.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" + integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== mime-types@^2.1.12, mime-types@^2.1.16, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.27" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.27.tgz#47949f98e279ea53119f5722e0f34e529bec009f" - integrity sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w== + version "2.1.29" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" + integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== dependencies: - mime-db "1.44.0" + mime-db "1.46.0" mime@1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0, mime@^2.4.6: - version "2.4.6" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1" - integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA== +mime@^2.4.6: + version "2.5.2" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" + integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== mimic-fn@^2.1.0: version "2.1.0" @@ -5427,7 +6148,7 @@ minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: +minimalistic-crypto-utils@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= @@ -5464,11 +6185,6 @@ mitt@^1.2.0: resolved "https://registry.yarnpkg.com/mitt/-/mitt-1.2.0.tgz#cb24e6569c806e31bd4e3995787fe38a04fdf90d" integrity sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw== -mkdirp-classic@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" - integrity sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A== - mkdirp-promise@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz#e9b8f68e552c68a9c1713b84883f7a1dd039b8a1" @@ -5481,7 +6197,7 @@ mkdirp@*, mkdirp@^1.0.4: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@0.5.5, mkdirp@^0.5.0, mkdirp@^0.5.1: +mkdirp@0.5.5, mkdirp@^0.5.0: version "0.5.5" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== @@ -5519,34 +6235,34 @@ mocha@^7.1.2: yargs-unparser "1.6.0" mocha@^8.1.1: - version "8.2.1" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.2.1.tgz#f2fa68817ed0e53343d989df65ccd358bc3a4b39" - integrity sha512-cuLBVfyFfFqbNR0uUKbDGXKGk+UDFe6aR4os78XIrMQpZl/nv7JYHcvP5MFIAb374b2zFXsdgEGwmzMtP0Xg8w== + version "8.3.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-8.3.0.tgz#a83a7432d382ae1ca29686062d7fdc2c36f63fe5" + integrity sha512-TQqyC89V1J/Vxx0DhJIXlq9gbbL9XFNdeLQ1+JsnZsVaSOV1z3tWfw0qZmQJGQRIfkvZcs7snQnZnOCKoldq1Q== dependencies: "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" - chokidar "3.4.3" - debug "4.2.0" - diff "4.0.2" + chokidar "3.5.1" + debug "4.3.1" + diff "5.0.0" escape-string-regexp "4.0.0" find-up "5.0.0" glob "7.1.6" growl "1.10.5" he "1.2.0" - js-yaml "3.14.0" + js-yaml "4.0.0" log-symbols "4.0.0" minimatch "3.0.4" - ms "2.1.2" - nanoid "3.1.12" + ms "2.1.3" + nanoid "3.1.20" serialize-javascript "5.0.1" strip-json-comments "3.1.1" - supports-color "7.2.0" + supports-color "8.1.1" which "2.0.2" wide-align "1.1.3" - workerpool "6.0.2" - yargs "13.3.2" - yargs-parser "13.1.2" + workerpool "6.1.0" + yargs "16.2.0" + yargs-parser "20.2.4" yargs-unparser "2.0.0" mock-fs@^4.1.0: @@ -5569,11 +6285,16 @@ ms@2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -ms@2.1.2, ms@^2.1.1: +ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@2.1.3, ms@^2.1.1: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multibase@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b" @@ -5629,10 +6350,10 @@ nanoassert@^1.0.0: resolved "https://registry.yarnpkg.com/nanoassert/-/nanoassert-1.1.0.tgz#4f3152e09540fde28c76f44b19bbcd1d5a42478d" integrity sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40= -nanoid@3.1.12: - version "3.1.12" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.12.tgz#6f7736c62e8d39421601e4a0c77623a97ea69654" - integrity sha512-1qstj9z5+x491jfiC4Nelk+f8XBad7LN20PmyWINJEMRSf3wcAjAWysw1qaA8z6NSKe2sjq1hRSDpBH5paCb6A== +nanoid@3.1.20: + version "3.1.20" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.20.tgz#badc263c6b1dcf14b71efaa85f6ab4c1d6cfc788" + integrity sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw== natural-compare@^1.4.0: version "1.4.0" @@ -5644,11 +6365,6 @@ negotiator@0.6.2: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== -nested-error-stacks@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" - integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== - next-tick@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -5683,7 +6399,7 @@ node-environment-flags@1.0.6: object.getownpropertydescriptors "^2.0.3" semver "^5.7.0" -node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.1: +node-fetch@2.6.1, node-fetch@^2.3.0, node-fetch@^2.6.0, node-fetch@^2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052" integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw== @@ -5698,7 +6414,7 @@ node-gyp-build@^4.2.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== -nofilter@^1.0.3: +nofilter@^1.0.3, nofilter@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/nofilter/-/nofilter-1.0.4.tgz#78d6f4b6a613e7ced8b015cec534625f7667006e" integrity sha512-N8lidFp+fCz+TD51+haYdbDGrcBWwuHX40F5+z0qkUjMJ5Tp+rdSuAkMJ9N9eoolDlEVTf6u5icM+cNKkKW2mA== @@ -5730,13 +6446,20 @@ npm-run-path@^2.0.0: dependencies: path-key "^2.0.0" -nth-check@~1.0.1: +nth-check@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== dependencies: boolbase "~1.0.0" +nth-check@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.0.0.tgz#1bb4f6dac70072fc313e8c9cd1417b5074c0a125" + integrity sha512-i4sc/Kj8htBrAiH1viZ0TgU8Y5XqCaV/FziYK6TBczxmeKm3AEFWqqF3195yKudrarqy7Zu80Ra5dobFjn9X/Q== + dependencies: + boolbase "^1.0.0" + number-to-bn@1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/number-to-bn/-/number-to-bn-1.7.0.tgz#bb3623592f7e5f9e0030b1977bd41a0c53fe1ea0" @@ -5756,11 +6479,11 @@ object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1 integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= object-hash@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" - integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== + version "2.1.1" + resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-2.1.1.tgz#9447d0279b4fcf80cff3259bf66a1dc73afabe09" + integrity sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ== -object-inspect@^1.8.0: +object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== @@ -5770,6 +6493,11 @@ object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== +object-keys@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-0.4.0.tgz#28a6aae7428dd2c3a92f3d95f21335dd204e0336" + integrity sha1-KKaq50KN0sOpLz2V8hM13SBOAzY= + object-path@^0.11.4: version "0.11.5" resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.5.tgz#d4e3cf19601a5140a55a16ad712019a9c50b577a" @@ -5785,7 +6513,7 @@ object.assign@4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.assign@^4.1.1: +object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -5795,7 +6523,7 @@ object.assign@^4.1.1: has-symbols "^1.0.1" object-keys "^1.1.1" -object.getownpropertydescriptors@^2.0.3: +object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.1.tgz#0dfda8d108074d9c563e80490c883b6661091544" integrity sha512-6DtXgZ/lIZ9hqx4GtZETobXLR/ZLaa0aqV0kzbn80Rf8Z2e/XFnhA0I7p07N2wH8bBBltr2xQPi6sbKWAY2Eng== @@ -5873,6 +6601,11 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= + p-cancelable@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" @@ -5895,7 +6628,7 @@ p-limit@^1.1.0: dependencies: p-try "^1.0.0" -p-limit@^2.0.0, p-limit@^2.2.0: +p-limit@^2.0.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== @@ -5923,13 +6656,6 @@ p-locate@^3.0.0: dependencies: p-limit "^2.0.0" -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -5989,12 +6715,17 @@ parse-json@^2.2.0: dependencies: error-ex "^1.2.0" -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz#042f792ffdd36851551cf4e9e066b3874ab45b5c" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== +parse5-htmlparser2-tree-adapter@^6.0.0: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== dependencies: - "@types/node" "*" + parse5 "^6.0.1" + +parse5@^6.0.0, parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== parseurl@~1.3.3: version "1.3.3" @@ -6051,9 +6782,9 @@ path-type@^2.0.0: pify "^2.0.0" pathval@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.0.tgz#b942e6d4bde653005ef6b71361def8727d0645e0" - integrity sha1-uULm1L3mUwBe9rcTYd74cn0GReA= + version "1.1.1" + resolved "https://registry.yarnpkg.com/pathval/-/pathval-1.1.1.tgz#8534e77a77ce7ac5a2512ea21e0fdb8fcf6c3d8d" + integrity sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ== pbkdf2@^3.0.17, pbkdf2@^3.0.3, pbkdf2@^3.0.9: version "3.1.1" @@ -6110,13 +6841,6 @@ pkg-dir@^2.0.0: dependencies: find-up "^2.1.0" -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -6162,17 +6886,24 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= -progress@^2.0.0, progress@^2.0.1: +progress@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== +prom-client@^13.1.0: + version "13.1.0" + resolved "https://registry.yarnpkg.com/prom-client/-/prom-client-13.1.0.tgz#1185caffd8691e28d32e373972e662964e3dba45" + integrity sha512-jT9VccZCWrJWXdyEtQddCDszYsiuWj5T0ekrPszi/WEegj3IZy6Mm09iOOVM86A4IKMWq8hZkT2dD9MaSe+sng== + dependencies: + tdigest "^0.1.1" + promise-timeout@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/promise-timeout/-/promise-timeout-1.3.0.tgz#d1c78dd50a607d5f0a5207410252a3a0914e1014" integrity sha512-5yANTE0tmi5++POym6OgtFmwfDvOXABD9oj/jLQr5GPEyuNEb7jH4wbbANJceJid49jwhi1RddxnhnEAb/doqg== -protobufjs@^6.8.6, protobufjs@^6.9.0: +protobufjs@^6.10.2, protobufjs@^6.8.6: version "6.10.2" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== @@ -6199,10 +6930,10 @@ proxy-addr@~2.0.5: forwarded "~0.1.2" ipaddr.js "1.9.1" -proxy-from-env@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" - integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= psl@^1.1.28: version "1.8.0" @@ -6248,40 +6979,6 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -puppeteer-core@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/puppeteer-core/-/puppeteer-core-5.3.1.tgz#1affb1738afac499416a7fd4ed2ed0c18577e88f" - integrity sha512-YE6c6FvHAFKQUyNTqFs78SgGmpcqOPhhmVfEVNYB4abv7bV2V+B3r72T3e7vlJkEeTloy4x9bQLrGbHHoKSg1w== - dependencies: - debug "^4.1.0" - devtools-protocol "0.0.799653" - extract-zip "^2.0.0" - https-proxy-agent "^4.0.0" - pkg-dir "^4.2.0" - progress "^2.0.1" - proxy-from-env "^1.0.0" - rimraf "^3.0.2" - tar-fs "^2.0.0" - unbzip2-stream "^1.3.3" - ws "^7.2.3" - -puppeteer@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/puppeteer/-/puppeteer-5.3.1.tgz#324e190d89f25ac33dba539f57b82a18553f8646" - integrity sha512-YTM1RaBeYrj6n7IlRXRYLqJHF+GM7tasbvrNFx6w1S16G76NrPq7oYFKLDO+BQsXNtS8kW2GxWCXjIMPvfDyaQ== - dependencies: - debug "^4.1.0" - devtools-protocol "0.0.799653" - extract-zip "^2.0.0" - https-proxy-agent "^4.0.0" - pkg-dir "^4.2.0" - progress "^2.0.1" - proxy-from-env "^1.0.0" - rimraf "^3.0.2" - tar-fs "^2.0.0" - unbzip2-stream "^1.3.3" - ws "^7.2.3" - pushdata-bitcoin@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz#15931d3cd967ade52206f523aa7331aef7d43af7" @@ -6294,10 +6991,10 @@ qs@6.7.0: resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.9.4: - version "6.9.4" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.4.tgz#9090b290d1f91728d3c22e54843ca44aea5ab687" - integrity sha512-A1kFqHekCTM7cz0udomYUoYNWjBebHm/5wzU/XqrBRBNWectVH0QIiN+NEcZ0Dte5hvzHwbr8+XQmguPhJ6WdQ== +qs@^6.7.0, qs@^6.9.4: + version "6.9.6" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" + integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== qs@~6.5.2: version "6.5.2" @@ -6348,6 +7045,16 @@ raw-body@2.4.0: iconv-lite "0.4.24" unpipe "1.0.0" +raw-body@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.1.tgz#30ac82f98bb5ae8c152e67149dac8d55153b168c" + integrity sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== + dependencies: + bytes "3.1.0" + http-errors "1.7.3" + iconv-lite "0.4.24" + unpipe "1.0.0" + read-pkg-up@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" @@ -6365,7 +7072,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: +readable-stream@3, readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== @@ -6374,7 +7081,17 @@ readable-stream@3, readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stre string_decoder "^1.1.1" util-deprecate "^1.0.1" -readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.7: +readable-stream@^1.0.33: + version "1.1.14" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" + integrity sha1-fPTFTvZI44EwhMY23SB54WbAgdk= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readable-stream@^2.0.0, readable-stream@^2.2.8, readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@^2.3.7: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -6387,6 +7104,16 @@ readable-stream@^2.3.0, readable-stream@^2.3.5, readable-stream@^2.3.7: string_decoder "~1.1.1" util-deprecate "~1.0.1" +readable-stream@~1.0.15: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw= + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + readdirp@~3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" @@ -6402,9 +7129,9 @@ readdirp@~3.5.0: picomatch "^2.2.1" redis-commands@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.6.0.tgz#36d4ca42ae9ed29815cdb30ad9f97982eba1ce23" - integrity sha512-2jnZ0IkjZxvguITjFTrGiLyzQZcTvaw8DAaCXxZq/dsHXz7KfMQ3OUJy7Tz9vnRtZRVz6VRCPDvruvU8Ts44wQ== + version "1.7.0" + resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" + integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" @@ -6439,13 +7166,13 @@ regexpp@^3.0.0, regexpp@^3.1.0: integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== renderkid@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.4.tgz#d325e532afb28d3f8796ffee306be8ffd6fc864c" - integrity sha512-K2eXrSOJdq+HuKzlcjOlGoOarUu5SDguDEhE7+Ah4zuOWL40j8A/oHvLlLob9PSTNvVnBd+/q0Er1QfpEuem5g== + version "2.0.5" + resolved "https://registry.yarnpkg.com/renderkid/-/renderkid-2.0.5.tgz#483b1ac59c6601ab30a7a596a5965cabccfdd0a5" + integrity sha512-ccqoLg+HLOHq1vdfYNm4TBeaCDIi1FLt3wGojTDSvdewUv65oTmI3cnT2E4hRjl1gzKZIPK+KZrXzlUYKnR+vQ== dependencies: - css-select "^1.1.0" + css-select "^2.0.2" dom-converter "^0.2" - htmlparser2 "^3.3.0" + htmlparser2 "^3.10.1" lodash "^4.17.20" strip-ansi "^3.0.0" @@ -6480,6 +7207,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= +require-from-string@^2.0.0, require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -6495,12 +7227,19 @@ resolve-from@^4.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== +resolve@1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" + integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== + dependencies: + path-parse "^1.0.6" + resolve@^1.10.0, resolve@^1.10.1, resolve@^1.13.1, resolve@^1.17.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" - integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== + version "1.20.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" + integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== dependencies: - is-core-module "^2.1.0" + is-core-module "^2.2.0" path-parse "^1.0.6" responselike@^1.0.2: @@ -6525,10 +7264,10 @@ retry-request@^4.0.0, retry-request@^4.1.1: dependencies: debug "^4.1.1" -rimraf@2.6.3: - version "2.6.3" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" - integrity sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA== +rimraf@^2.2.8: + version "2.7.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" + integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" @@ -6547,7 +7286,7 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rlp@^2.0.0, rlp@^2.2.3, rlp@^2.2.4: +rlp@^2.0.0, rlp@^2.2.1, rlp@^2.2.2, rlp@^2.2.3, rlp@^2.2.4: version "2.2.6" resolved "https://registry.yarnpkg.com/rlp/-/rlp-2.2.6.tgz#c80ba6266ac7a483ef1e69e8e2f056656de2fb2c" integrity sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg== @@ -6562,6 +7301,11 @@ rsv-signature@^1.1.0: bn.js "^4.8.0" enc-utils "^2.2.1" +rustbn.js@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/rustbn.js/-/rustbn.js-0.2.0.tgz#8082cb886e707155fd1cb6f23bd591ab8d55d0ca" + integrity sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA== + rxjs@^6.4.0: version "6.6.3" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" @@ -6611,7 +7355,7 @@ scryptsy@2.1.0, scryptsy@^2.1.0: resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== -secp256k1@^3.0.1, secp256k1@^3.5.2: +secp256k1@^3.0.1, secp256k1@^3.5.2, secp256k1@^3.7.1: version "3.8.0" resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-3.8.0.tgz#28f59f4b01dbee9575f56a47034b7d2e3b3b352d" integrity sha512-k5ke5avRZbtl9Tqx/SA7CbY3NF6Ro+Sj9cZxezFzuBlLDmyqPiL8hJJ+EmzD8Ig4LUDByHJ3/iPOVoRixs/hmw== @@ -6641,6 +7385,11 @@ seek-bzip@^1.0.5: dependencies: commander "^2.8.1" +semaphore@>=1.0.1, semaphore@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/semaphore/-/semaphore-1.1.0.tgz#aaad8b86b20fe8e9b32b16dc2ee682a8cd26a8aa" + integrity sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA== + "semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.5.1, semver@^5.7.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" @@ -6656,10 +7405,17 @@ semver@^6.1.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== -semver@^7.0.0, semver@^7.2.1, semver@^7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== +semver@^7.0.0, semver@^7.2.1, semver@^7.3.2, semver@^7.3.4: + version "7.3.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" + integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== + dependencies: + lru-cache "^6.0.0" + +semver@~5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.4.1.tgz#e059c09d8571f0540823733433505d3a2f00b18e" + integrity sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg== send-crypto@0.2.15: version "0.2.15" @@ -6809,26 +7565,30 @@ simple-swizzle@^0.2.2: is-arrayish "^0.3.1" sinon@^9.0.3: - version "9.2.1" - resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.1.tgz#64cc88beac718557055bd8caa526b34a2231be6d" - integrity sha512-naPfsamB5KEE1aiioaoqJ6MEhdUs/2vtI5w1hPAXX/UwvoPjXcwh1m5HiKx0HGgKR8lQSoFIgY5jM6KK8VrS9w== + version "9.2.4" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.2.4.tgz#e55af4d3b174a4443a8762fa8421c2976683752b" + integrity sha512-zljcULZQsJxVra28qIAL6ow1Z9tpattkCTEJR4RBP3TGc00FcttsP5pK284Nas5WjMZU5Yzy3kAIp3B3KRf5Yg== dependencies: "@sinonjs/commons" "^1.8.1" "@sinonjs/fake-timers" "^6.0.1" - "@sinonjs/formatio" "^5.0.1" - "@sinonjs/samsam" "^5.2.0" + "@sinonjs/samsam" "^5.3.1" diff "^4.0.2" nise "^4.0.4" supports-color "^7.1.0" -slice-ansi@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-2.1.0.tgz#cacd7693461a637a5788d92a7dd4fba068e81636" - integrity sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ== +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b" + integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ== dependencies: - ansi-styles "^3.2.0" - astral-regex "^1.0.0" - is-fullwidth-code-point "^2.0.0" + ansi-styles "^4.0.0" + astral-regex "^2.0.0" + is-fullwidth-code-point "^3.0.0" snakecase-keys@^3.1.2: version "3.2.1" @@ -6838,12 +7598,27 @@ snakecase-keys@^3.1.2: map-obj "^4.1.0" to-snake-case "^1.0.0" +solc@0.7.3: + version "0.7.3" + resolved "https://registry.yarnpkg.com/solc/-/solc-0.7.3.tgz#04646961bd867a744f63d2b4e3c0701ffdc7d78a" + integrity sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA== + dependencies: + command-exists "^1.2.8" + commander "3.0.2" + follow-redirects "^1.12.1" + fs-extra "^0.30.0" + js-sha3 "0.8.0" + memorystream "^0.3.1" + require-from-string "^2.0.0" + semver "^5.5.0" + tmp "0.0.33" + solidity-parser-antlr@^0.4.11: version "0.4.11" resolved "https://registry.yarnpkg.com/solidity-parser-antlr/-/solidity-parser-antlr-0.4.11.tgz#af43e1f13b3b88309a875455f5d6e565b05ee5f1" integrity sha512-4jtxasNGmyC0midtjH/lTFPZYvTTUMy6agYcF+HoMnzW8+cqo3piFrINb4ZCzpPW+7tTVFCGa5ubP34zOzeuMg== -source-map-support@^0.5.17, source-map-support@^0.5.19: +source-map-support@^0.5.13, source-map-support@^0.5.17, source-map-support@^0.5.19: version "0.5.19" resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -6923,6 +7698,13 @@ stack-trace@0.0.x: resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= +stacktrace-parser@^0.1.10: + version "0.1.10" + resolved "https://registry.yarnpkg.com/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz#29fb0cae4e0d0b85155879402857a1639eb6051a" + integrity sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg== + dependencies: + type-fest "^0.7.1" + "statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" @@ -6971,7 +7753,7 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string.prototype.trimend@^1.0.1: +string.prototype.trimend@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== @@ -6979,7 +7761,7 @@ string.prototype.trimend@^1.0.1: call-bind "^1.0.0" define-properties "^1.1.3" -string.prototype.trimstart@^1.0.1: +string.prototype.trimstart@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== @@ -6994,6 +7776,11 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" +string_decoder@~0.10.x: + version "0.10.31" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" + integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ= + string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" @@ -7085,7 +7872,7 @@ super-split@^1.1.0: resolved "https://registry.yarnpkg.com/super-split/-/super-split-1.1.0.tgz#43b3ba719155f4d43891a32729d59b213d9155fc" integrity sha512-I4bA5mgcb6Fw5UJ+EkpzqXfiuvVGS/7MuND+oBxNFmxu3ugLNrdIatzBLfhFRMVMLxgSsRy+TjIktgkF9RFSNQ== -superagent@^5.2.2: +superagent@^5.1.0, superagent@^5.2.2: version "5.3.1" resolved "https://registry.yarnpkg.com/superagent/-/superagent-5.3.1.tgz#d62f3234d76b8138c1320e90fa83dc1850ccabf1" integrity sha512-wjJ/MoTid2/RuGCOFtlacyGNxN9QLMgcpYLDQlWFIhhdJ93kNscFonGvrpAHSCVjRVj++DGCglocF7Aej1KHvQ== @@ -7109,10 +7896,10 @@ supports-color@6.0.0: dependencies: has-flag "^3.0.0" -supports-color@7.2.0, supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== +supports-color@8.1.1: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" @@ -7123,6 +7910,13 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + swarm-js@0.1.39: version "0.1.39" resolved "https://registry.yarnpkg.com/swarm-js/-/swarm-js-0.1.39.tgz#79becb07f291d4b2a178c50fee7aa6e10342c0e8" @@ -7158,13 +7952,14 @@ swarm-js@^0.1.40: tar "^4.0.2" xhr-request "^1.0.1" -synthetix@^2.29.3: - version "2.34.0" - resolved "https://registry.yarnpkg.com/synthetix/-/synthetix-2.34.0.tgz#ef7ec4cc335c539b57e0c88541cce2db39bfd614" - integrity sha512-e3HCRRaz1duuhe/ZZj8jyGKRRFklM5AZNn2d9xmvgAQtRPMrElR0n2FFU+I8+Bp4+bLsGosrJS1cp1+X+qcZeg== +synthetix@2.39.2: + version "2.39.2" + resolved "https://registry.yarnpkg.com/synthetix/-/synthetix-2.39.2.tgz#455c52a4c29394f013471fdece2ef4f6e3634c91" + integrity sha512-Dp41ukNTUo2PBzzPj8sHw6BXIoOrkIUSPx9+V5MK/c04HIGuETMDbU5ccQwqvYMUmMipeHbBH7D0mvDpILfohg== dependencies: "@chainlink/contracts-0.0.10" "npm:@chainlink/contracts@0.0.10" "@eth-optimism/contracts" "0.0.2-alpha.7" + "@eth-optimism/watcher" "0.0.1-alpha.8" abi-decoder "2.3.0" commander "5.1.0" openzeppelin-solidity-2.3.0 "npm:openzeppelin-solidity@2.3.0" @@ -7172,25 +7967,15 @@ synthetix@^2.29.3: solidity-parser-antlr "^0.4.11" web3-utils "1.2.2" -table@^5.2.3: - version "5.4.6" - resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e" - integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug== - dependencies: - ajv "^6.10.2" - lodash "^4.17.14" - slice-ansi "^2.1.0" - string-width "^3.0.0" - -tar-fs@^2.0.0, tar-fs@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.1.1.tgz#489a15ab85f1f0befabb370b7de4f9eb5cbe8784" - integrity sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng== +table@^6.0.4: + version "6.0.7" + resolved "https://registry.yarnpkg.com/table/-/table-6.0.7.tgz#e45897ffbcc1bcf9e8a87bf420f2c9e5a7a52a34" + integrity sha512-rxZevLGTUzWna/qBLObOe16kB2RTnnbhciwgPbMMlazz1yZGVEgnZK762xyVdVznhqxrfCeBMmMkgOOaPwjH7g== dependencies: - chownr "^1.1.1" - mkdirp-classic "^0.5.2" - pump "^3.0.0" - tar-stream "^2.1.4" + ajv "^7.0.2" + lodash "^4.17.20" + slice-ansi "^4.0.0" + string-width "^4.2.0" tar-stream@^1.5.2: version "1.6.2" @@ -7205,17 +7990,6 @@ tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" -tar-stream@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.4.tgz#c4fb1a11eb0da29b893a5b25476397ba2d053bfa" - integrity sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - tar@^4.0.2: version "4.4.13" resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" @@ -7229,6 +8003,13 @@ tar@^4.0.2: safe-buffer "^5.1.2" yallist "^3.0.3" +tdigest@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/tdigest/-/tdigest-0.1.1.tgz#2e3cb2c39ea449e55d1e6cd91117accca4588021" + integrity sha1-Ljyyw56kSeVdHmzZEReszKRYgCE= + dependencies: + bintrees "1.0.1" + teeny-request@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/teeny-request/-/teeny-request-7.0.1.tgz#bdd41fdffea5f8fbc0d29392cb47bec4f66b2b4c" @@ -7278,9 +8059,9 @@ timed-out@^4.0.0, timed-out@^4.0.1: integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8= tiny-secp256k1@^1.1.1, tiny-secp256k1@^1.1.3: - version "1.1.5" - resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.5.tgz#3dc37b9bf0fa5b4390b9fa29e953228810cebc18" - integrity sha512-duE2hSLSQIpHGzmK48OgRrGTi+4OTkXLC6aa86uOYQ6LLCYZSarVKIAvEtY7MoXjoL6bOXMSerEGMzrvW4SkDw== + version "1.1.6" + resolved "https://registry.yarnpkg.com/tiny-secp256k1/-/tiny-secp256k1-1.1.6.tgz#7e224d2bee8ab8283f284e40e6b4acb74ffe047c" + integrity sha512-FmqJZGduTyvsr2cF3375fqGHUovSwDi/QytexX1Se4BPuPZpTE5Ftp5fg+EFSuEf3lhZqgCRjEG3ydUQ/aNiwA== dependencies: bindings "^1.3.0" bn.js "^4.11.8" @@ -7288,6 +8069,13 @@ tiny-secp256k1@^1.1.1, tiny-secp256k1@^1.1.3: elliptic "^6.4.0" nan "^2.13.2" +tmp@0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/to-buffer/-/to-buffer-1.1.1.tgz#493bd48f62d7c43fcded313a03dcadb2e1213a80" @@ -7345,11 +8133,23 @@ tradingeconomics-stream@^0.1.2: eslint "^7.3.1" ws "^7.3.0" +tradingeconomics-stream@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/tradingeconomics-stream/-/tradingeconomics-stream-0.2.0.tgz#33dd534b3a07d2b97a32eef9a5494057f0b802f2" + integrity sha512-mG81m32D2NRD7i4pIAfRpESwSaguPJKAAC2PXyCfXvKd69Hkr6Gzyb9xwMYfShtICTF4z3ARKiso0gLoup/zJQ== + dependencies: + ws "^7.3.0" + triple-beam@^1.2.0, triple-beam@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== +"true-case-path@^2.2.1": + version "2.2.1" + resolved "https://registry.yarnpkg.com/true-case-path/-/true-case-path-2.2.1.tgz#c5bf04a5bbec3fd118be4084461b3a27c4d796bf" + integrity sha512-0z3j8R7MCjy10kc/g+qg7Ln3alJTodw9aDuVWZa3uiWqfuBMKeAeP2ocWcxoyM3D73yz3Jt/Pu4qPr4wHSdB/Q== + truffle-flattener@^1.4.0: version "1.5.0" resolved "https://registry.yarnpkg.com/truffle-flattener/-/truffle-flattener-1.5.0.tgz#c358fa3e5cb0a663429f7912a58c4f0879414e64" @@ -7382,7 +8182,7 @@ tsconfig-paths@^3.9.0: minimist "^1.2.0" strip-bom "^3.0.0" -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -7393,9 +8193,9 @@ tsort@0.0.1: integrity sha1-4igPXoF/i/QnVlf9D5rr1E9aJ4Y= tsutils@^3.17.1: - version "3.17.1" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759" - integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g== + version "3.20.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" + integrity sha512-RYbuQuvkhuqVeXweWT3tJLKOEJ/UUw9GjNEZGWdrLLlM+611o1gwLHBpxoFJKKl25fLprp2eVthtKs5JOrNeXg== dependencies: tslib "^1.8.1" @@ -7406,11 +8206,21 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" +tweetnacl-util@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz#b80fcdb5c97bcc508be18c44a4be50f022eea00b" + integrity sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw== + tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= +tweetnacl@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-1.0.3.tgz#ac0af71680458d8a6378d0d0d050ab1407d35596" + integrity sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw== + type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" @@ -7423,10 +8233,20 @@ type-detect@4.0.8, type-detect@^4.0.0, type-detect@^4.0.5, type-detect@^4.0.8: resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.19.0.tgz#b2b8e34b2a1253a839f9c3b4bba0ebacbd34be9c" - integrity sha512-6lN0zC9ItzVv3jq9NicSaqo7PUjTNnmxGBECiJbz8Vv2TWaGW15mJTBS2BHZUlEKRsclzZzp8gHnBe4kzQRNfg== +type-fest@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" + integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== + +type-fest@^0.20.0: + version "0.20.2" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^0.7.1: + version "0.7.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.7.1.tgz#8dda65feaf03ed78f0a3f9678f1869147f7c5c48" + integrity sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg== type-fest@^0.8.1: version "0.8.1" @@ -7447,9 +8267,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" - integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== + version "2.3.0" + resolved "https://registry.yarnpkg.com/type/-/type-2.3.0.tgz#ada7c045f07ead08abf9e2edd29be1a0c0661132" + integrity sha512-rgPIqOdfK/4J9FhiVrZ3cveAjRRo5rsQBAIhnylX874y1DX/kEKSVdLsnuHB6l1KTjHyU01VjiMBHgU2adejyg== typedarray-to-buffer@3.1.5, typedarray-to-buffer@^3.1.5: version "3.1.5" @@ -7464,16 +8284,16 @@ typeforce@^1.11.3, typeforce@^1.11.5: integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== typescript@^3.9.7: - version "3.9.7" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.7.tgz#98d600a5ebdc38f40cb277522f12dc800e9e25fa" - integrity sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw== + version "3.9.9" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674" + integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w== ultron@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== -unbzip2-stream@^1.0.9, unbzip2-stream@^1.3.3: +unbzip2-stream@^1.0.9: version "1.4.3" resolved "https://registry.yarnpkg.com/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz#b0da04c4371311df771cdc215e87f2130991ace7" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== @@ -7507,9 +8327,9 @@ unpipe@1.0.0, unpipe@~1.0.0: integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= uri-js@^4.2.2: - version "4.4.0" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.0.tgz#aa714261de793e8a82347a7bcc9ce74e86f28602" - integrity sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g== + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== dependencies: punycode "^2.1.0" @@ -7546,9 +8366,9 @@ url-to-options@^1.0.1: integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k= utf-8-validate@^5.0.2: - version "5.0.3" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.3.tgz#3b64e418ad2ff829809025fdfef595eab2f03a27" - integrity sha512-jtJM6fpGv8C1SoH4PtG22pGto6x+Y8uPprW0tw3//gGFhDDTiuksgradgFN6yRayDP4SyZZa6ZMGHLIa17+M8A== + version "5.0.4" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.4.tgz#72a1735983ddf7a05a43a9c6b67c5ce1c910f9b8" + integrity sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q== dependencies: node-gyp-build "^4.2.0" @@ -7567,6 +8387,29 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= +util.promisify@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.1.1.tgz#77832f57ced2c9478174149cae9b96e9918cd54b" + integrity sha512-/s3UsZUrIfa6xDhr7zZhnE9SLQ5RIXyYfiVnMMyMDzOc8WhWN4Nbh36H842OyurKbCDAesZOJaVyvmSl6fhGQw== + dependencies: + call-bind "^1.0.0" + define-properties "^1.1.3" + for-each "^0.3.3" + has-symbols "^1.0.1" + object.getownpropertydescriptors "^2.1.1" + +util@^0.12.0: + version "0.12.3" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.3.tgz#971bb0292d2cc0c892dab7c6a5d37c2bec707888" + integrity sha512-I8XkoQwE+fPQEhy9v012V+TSdH2kp9ts29i20TaaDUXsg7x/onePbhFJUExBfv/2ay1ZOp/Vsm3nDlmnFGSAog== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + safe-buffer "^5.1.2" + which-typed-array "^1.1.2" + utila@~0.4: version "0.4.0" resolved "https://registry.yarnpkg.com/utila/-/utila-0.4.0.tgz#8a16a05d445657a3aea5eecc5b12a4fa5379772c" @@ -7593,9 +8436,9 @@ uuid@^3.0.1, uuid@^3.3.2: integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== uuid@^8.0.0, uuid@^8.3.0: - version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" - integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== + version "8.3.2" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== v8-compile-cache@^2.0.3: version "2.2.0" @@ -7664,10 +8507,10 @@ web3-bzz@1.2.9: swarm-js "^0.1.40" underscore "1.9.1" -web3-bzz@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.0.tgz#83dfd77fa8a64bbb660462dffd0fee2a02ef1051" - integrity sha512-ibYAnKab+sgTo/UdfbrvYfWblXjjgSMgyy9/FHa6WXS14n/HVB+HfWqGz2EM3fok8Wy5XoKGMvdqvERQ/mzq1w== +web3-bzz@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-bzz/-/web3-bzz-1.3.4.tgz#9be529353c4063bc68395370cb5d8e414c6b6c87" + integrity sha512-DBRVQB8FAgoAtZCpp2GAGPCJjgBgsuwOKEasjV044AAZiONpXcKHbkO6G1SgItIixnrJsRJpoGLGw52Byr6FKw== dependencies: "@types/node" "^12.12.6" got "9.6.0" @@ -7692,14 +8535,14 @@ web3-core-helpers@1.2.9: web3-eth-iban "1.2.9" web3-utils "1.2.9" -web3-core-helpers@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.0.tgz#697cc3246a7eaaaac64ea506828d861c981c3f31" - integrity sha512-+MFb1kZCrRctf7UYE7NCG4rGhSXaQJ/KF07di9GVK1pxy1K0+rFi61ZobuV1ky9uQp+uhhSPts4Zp55kRDB5sw== +web3-core-helpers@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-helpers/-/web3-core-helpers-1.3.4.tgz#b8549740bf24d5c71688d89c3cdd802d8d36b4e4" + integrity sha512-n7BqDalcTa1stncHMmrnFtyTgDhX5Fy+avNaHCf6qcOP2lwTQC8+mdHVBONWRJ6Yddvln+c8oY/TAaB6PzWK0A== dependencies: underscore "1.9.1" - web3-eth-iban "1.3.0" - web3-utils "1.3.0" + web3-eth-iban "1.3.4" + web3-utils "1.3.4" web3-core-helpers@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -7735,17 +8578,17 @@ web3-core-method@1.2.9: web3-core-subscriptions "1.2.9" web3-utils "1.2.9" -web3-core-method@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.0.tgz#a71387af842aec7dbad5dbbd1130c14cc6c8beb3" - integrity sha512-h0yFDrYVzy5WkLxC/C3q+hiMnzxdWm9p1T1rslnuHgOp6nYfqzu/6mUIXrsS4h/OWiGJt+BZ0xVZmtC31HDWtg== +web3-core-method@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-method/-/web3-core-method-1.3.4.tgz#6c2812d96dd6c811b9e6c8a5d25050d2c22b9527" + integrity sha512-JxmQrujsAWYRRN77P/RY7XuZDCzxSiiQJrgX/60Lfyf7FF1Y0le4L/UMCi7vUJnuYkbU1Kfl9E0udnqwyPqlvQ== dependencies: "@ethersproject/transactions" "^5.0.0-beta.135" underscore "1.9.1" - web3-core-helpers "1.3.0" - web3-core-promievent "1.3.0" - web3-core-subscriptions "1.3.0" - web3-utils "1.3.0" + web3-core-helpers "1.3.4" + web3-core-promievent "1.3.4" + web3-core-subscriptions "1.3.4" + web3-utils "1.3.4" web3-core-method@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -7776,10 +8619,10 @@ web3-core-promievent@1.2.9: dependencies: eventemitter3 "3.1.2" -web3-core-promievent@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.0.tgz#e0442dd0a8989b6bdce09293976cee6d9237a484" - integrity sha512-blv69wrXw447TP3iPvYJpllkhW6B18nfuEbrfcr3n2Y0v1Jx8VJacNZFDFsFIcgXcgUIVCtOpimU7w9v4+rtaw== +web3-core-promievent@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-promievent/-/web3-core-promievent-1.3.4.tgz#d166239012d91496cdcbe91d5d54071ea818bc73" + integrity sha512-V61dZIeBwogg6hhZZUt0qL9hTp1WDhnsdjP++9fhTDr4vy/Gz8T5vibqT2LLg6lQC8i+Py33yOpMeMNjztaUaw== dependencies: eventemitter3 "4.0.4" @@ -7805,16 +8648,17 @@ web3-core-requestmanager@1.2.9: web3-providers-ipc "1.2.9" web3-providers-ws "1.2.9" -web3-core-requestmanager@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.0.tgz#c5b9a0304504c0e6cce6c90bc1a3bff82732aa1f" - integrity sha512-3yMbuGcomtzlmvTVqNRydxsx7oPlw3ioRL6ReF9PeNYDkUsZaUib+6Dp5eBt7UXh5X+SIn/xa1smhDHz5/HpAw== +web3-core-requestmanager@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-requestmanager/-/web3-core-requestmanager-1.3.4.tgz#e105ced735c2b5fcedd5771e0ecf9879ae9c373f" + integrity sha512-xriouCrhVnVDYQ04TZXdEREZm0OOJzkSEsoN5bu4JYsA6e/HzROeU+RjDpMUxFMzN4wxmFZ+HWbpPndS3QwMag== dependencies: underscore "1.9.1" - web3-core-helpers "1.3.0" - web3-providers-http "1.3.0" - web3-providers-ipc "1.3.0" - web3-providers-ws "1.3.0" + util "^0.12.0" + web3-core-helpers "1.3.4" + web3-providers-http "1.3.4" + web3-providers-ipc "1.3.4" + web3-providers-ws "1.3.4" web3-core-subscriptions@1.2.2: version "1.2.2" @@ -7834,14 +8678,14 @@ web3-core-subscriptions@1.2.9: underscore "1.9.1" web3-core-helpers "1.2.9" -web3-core-subscriptions@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.0.tgz#c2622ccd2b84f4687475398ff966b579dba0847e" - integrity sha512-MUUQUAhJDb+Nz3S97ExVWveH4utoUnsbPWP+q1HJH437hEGb4vunIb9KvN3hFHLB+aHJfPeStM/4yYTz5PeuyQ== +web3-core-subscriptions@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core-subscriptions/-/web3-core-subscriptions-1.3.4.tgz#7b00e92bde21f792620cd02e6e508fcf4f4c31d3" + integrity sha512-drVHVDxh54hv7xmjIm44g4IXjfGj022fGw4/meB5R2D8UATFI40F73CdiBlyqk3DysP9njDOLTJFSQvEkLFUOg== dependencies: eventemitter3 "4.0.4" underscore "1.9.1" - web3-core-helpers "1.3.0" + web3-core-helpers "1.3.4" web3-core-subscriptions@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -7877,18 +8721,18 @@ web3-core@1.2.9: web3-core-requestmanager "1.2.9" web3-utils "1.2.9" -web3-core@1.3.0, web3-core@^1.2.9: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.0.tgz#b818903738461c1cca0163339e1d6d3fa51242cf" - integrity sha512-BwWvAaKJf4KFG9QsKRi3MNoNgzjI6szyUlgme1qNPxUdCkaS3Rdpa0VKYNHP7M/YTk82/59kNE66mH5vmoaXjA== +web3-core@1.3.4, web3-core@^1.2.9: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-core/-/web3-core-1.3.4.tgz#2cc7ba7f35cc167f7a0a46fd5855f86e51d34ce8" + integrity sha512-7OJu46RpCEfTerl+gPvHXANR2RkLqAfW7l2DAvQ7wN0pnCzl9nEfdgW6tMhr31k3TR2fWucwKzCyyxMGzMHeSA== dependencies: "@types/bn.js" "^4.11.5" "@types/node" "^12.12.6" bignumber.js "^9.0.0" - web3-core-helpers "1.3.0" - web3-core-method "1.3.0" - web3-core-requestmanager "1.3.0" - web3-utils "1.3.0" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-requestmanager "1.3.4" + web3-utils "1.3.4" web3-core@2.0.0-alpha.1, web3-core@^2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -7921,14 +8765,14 @@ web3-eth-abi@1.2.9: underscore "1.9.1" web3-utils "1.2.9" -web3-eth-abi@1.3.0, web3-eth-abi@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.0.tgz#387b7ea9b38be69ad8856bc7b4e9a6a69bb4d22b" - integrity sha512-1OrZ9+KGrBeBRd3lO8upkpNua9+7cBsQAgor9wbA25UrcUYSyL8teV66JNRu9gFxaTbkpdrGqM7J/LXpraXWrg== +web3-eth-abi@1.3.4, web3-eth-abi@^1.2.1: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-abi/-/web3-eth-abi-1.3.4.tgz#10f5d8b6080dbb6cbaa1bcef7e0c70573da6566f" + integrity sha512-PVSLXJ2dzdXsC+R24llIIEOS6S1KhG5qwNznJjJvXZFe3sqgdSe47eNvwUamZtCBjcrdR/HQr+L/FTxqJSf80Q== dependencies: - "@ethersproject/abi" "5.0.0-beta.153" + "@ethersproject/abi" "5.0.7" underscore "1.9.1" - web3-utils "1.3.0" + web3-utils "1.3.4" web3-eth-abi@2.0.0-alpha.1, web3-eth-abi@^2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -7975,10 +8819,10 @@ web3-eth-accounts@1.2.9: web3-core-method "1.2.9" web3-utils "1.2.9" -web3-eth-accounts@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.0.tgz#010acf389b2bee6d5e1aecb2fe78bfa5c8f26c7a" - integrity sha512-/Q7EVW4L2wWUbNRtOTwAIrYvJid/5UnKMw67x/JpvRMwYC+e+744P536Ja6SG4X3MnzFvd3E/jruV4qa6k+zIw== +web3-eth-accounts@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-accounts/-/web3-eth-accounts-1.3.4.tgz#cf513d78531c13ce079a5e7862820570350e79a5" + integrity sha512-gz9ReSmQEjqbYAjpmAx+UZF4CVMbyS4pfjSYWGAnNNI+Xz0f0u0kCIYXQ1UEaE+YeLcYiE+ZlZdgg6YoatO5nA== dependencies: crypto-browserify "3.12.0" eth-lib "0.2.8" @@ -7987,10 +8831,10 @@ web3-eth-accounts@1.3.0: scrypt-js "^3.0.1" underscore "1.9.1" uuid "3.3.2" - web3-core "1.3.0" - web3-core-helpers "1.3.0" - web3-core-method "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-utils "1.3.4" web3-eth-accounts@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8042,20 +8886,20 @@ web3-eth-contract@1.2.9: web3-eth-abi "1.2.9" web3-utils "1.2.9" -web3-eth-contract@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.0.tgz#c758340ac800788e29fa29edc8b0c0ac957b741c" - integrity sha512-3SCge4SRNCnzLxf0R+sXk6vyTOl05g80Z5+9/B5pERwtPpPWaQGw8w01vqYqsYBKC7zH+dxhMaUgVzU2Dgf7bQ== +web3-eth-contract@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-contract/-/web3-eth-contract-1.3.4.tgz#1ea2dd71be0c4a9cf4772d4f75dbb2fa99751472" + integrity sha512-Fvy8ZxUksQY2ePt+XynFfOiSqxgQtMn4m2NJs6VXRl2Inl17qyRi/nIJJVKTcENLocm+GmZ/mxq2eOE5u02nPg== dependencies: "@types/bn.js" "^4.11.5" underscore "1.9.1" - web3-core "1.3.0" - web3-core-helpers "1.3.0" - web3-core-method "1.3.0" - web3-core-promievent "1.3.0" - web3-core-subscriptions "1.3.0" - web3-eth-abi "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-promievent "1.3.4" + web3-core-subscriptions "1.3.4" + web3-eth-abi "1.3.4" + web3-utils "1.3.4" web3-eth-contract@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8103,20 +8947,20 @@ web3-eth-ens@1.2.9: web3-eth-contract "1.2.9" web3-utils "1.2.9" -web3-eth-ens@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.0.tgz#0887ba38473c104cf5fb8a715828b3b354fa02a2" - integrity sha512-WnOru+EcuM5dteiVYJcHXo/I7Wq+ei8RrlS2nir49M0QpYvUPGbCGgTbifcjJQTWamgORtWdljSA1s2Asdb74w== +web3-eth-ens@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-ens/-/web3-eth-ens-1.3.4.tgz#a7e4bb18481fb0e2ce5bfb3b3da2fbb0ad78cefe" + integrity sha512-b0580tQyQwpV2wyacwQiBEfQmjCUln5iPhge3IBIMXaI43BUNtH3lsCL9ERFQeOdweB4o+6rYyNYr6xbRcSytg== dependencies: content-hash "^2.5.2" eth-ens-namehash "2.0.8" underscore "1.9.1" - web3-core "1.3.0" - web3-core-helpers "1.3.0" - web3-core-promievent "1.3.0" - web3-eth-abi "1.3.0" - web3-eth-contract "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-promievent "1.3.4" + web3-eth-abi "1.3.4" + web3-eth-contract "1.3.4" + web3-utils "1.3.4" web3-eth-ens@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8152,13 +8996,13 @@ web3-eth-iban@1.2.9: bn.js "4.11.8" web3-utils "1.2.9" -web3-eth-iban@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.0.tgz#15b782dfaf273ebc4e3f389f1367f4e88ddce4a5" - integrity sha512-v9mZWhR4fPF17/KhHLiWir4YHWLe09O3B/NTdhWqw3fdAMJNztzMHGzgHxA/4fU+rhrs/FhDzc4yt32zMEXBZw== +web3-eth-iban@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-iban/-/web3-eth-iban-1.3.4.tgz#5eb7a564e0dcf68730d68f48f95dd207cd173d81" + integrity sha512-Y7/hLjVvIN/OhaAyZ8L/hxbTqVX6AFTl2RwUXR6EEU9oaLydPcMjAx/Fr8mghUvQS3QJSr+UGubP3W4SkyNiYw== dependencies: bn.js "^4.11.9" - web3-utils "1.3.0" + web3-utils "1.3.4" web3-eth-iban@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8193,17 +9037,17 @@ web3-eth-personal@1.2.9: web3-net "1.2.9" web3-utils "1.2.9" -web3-eth-personal@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.0.tgz#d376e03dc737d961ff1f8d1aca866efad8477135" - integrity sha512-2czUhElsJdLpuNfun9GeLiClo5O6Xw+bLSjl3f4bNG5X2V4wcIjX2ygep/nfstLLtkz8jSkgl/bV7esANJyeRA== +web3-eth-personal@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth-personal/-/web3-eth-personal-1.3.4.tgz#0d0e0abea3447283d7ee5658ed312990c9bf48dd" + integrity sha512-JiTbaktYVk1j+S2EDooXAhw5j/VsdvZfKRmHtXUe/HizPM9ETXmj1+ne4RT6m+950jQ7DJwUF3XU1FKYNtEDwQ== dependencies: "@types/node" "^12.12.6" - web3-core "1.3.0" - web3-core-helpers "1.3.0" - web3-core-method "1.3.0" - web3-net "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-net "1.3.4" + web3-utils "1.3.4" web3-eth-personal@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8257,24 +9101,24 @@ web3-eth@1.2.9: web3-net "1.2.9" web3-utils "1.2.9" -web3-eth@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.0.tgz#898e5f5a8827f9bc6844e267a52eb388916a6771" - integrity sha512-/bzJcxXPM9EM18JM5kO2JjZ3nEqVo3HxqU93aWAEgJNqaP/Lltmufl2GpvIB2Hvj+FXAjAXquxUdQ2/xP7BzHQ== +web3-eth@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-eth/-/web3-eth-1.3.4.tgz#7c4607685e66a1c43e3e315e526c959f24f96907" + integrity sha512-8OIVMLbvmx+LB5RZ4tDhXuFGWSdNMrCZ4HM0+PywQ08uEcmAcqTMFAn4vdPii+J8gCatZR501r1KdzX3SDLoPw== dependencies: underscore "1.9.1" - web3-core "1.3.0" - web3-core-helpers "1.3.0" - web3-core-method "1.3.0" - web3-core-subscriptions "1.3.0" - web3-eth-abi "1.3.0" - web3-eth-accounts "1.3.0" - web3-eth-contract "1.3.0" - web3-eth-ens "1.3.0" - web3-eth-iban "1.3.0" - web3-eth-personal "1.3.0" - web3-net "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-helpers "1.3.4" + web3-core-method "1.3.4" + web3-core-subscriptions "1.3.4" + web3-eth-abi "1.3.4" + web3-eth-accounts "1.3.4" + web3-eth-contract "1.3.4" + web3-eth-ens "1.3.4" + web3-eth-iban "1.3.4" + web3-eth-personal "1.3.4" + web3-net "1.3.4" + web3-utils "1.3.4" web3-eth@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8316,14 +9160,14 @@ web3-net@1.2.9: web3-core-method "1.2.9" web3-utils "1.2.9" -web3-net@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.0.tgz#b69068cccffab58911c2f08ca4abfbefb0f948c6" - integrity sha512-Xz02KylOyrB2YZzCkysEDrY7RbKxb7LADzx3Zlovfvuby7HBwtXVexXKtoGqksa+ns1lvjQLLQGb+OeLi7Sr7w== +web3-net@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-net/-/web3-net-1.3.4.tgz#d76158bf0b4a7b3b14352b4f95472db9efc57a2a" + integrity sha512-wVyqgVC3Zt/0uGnBiR3GpnsS8lvOFTDgWZMxAk9C6Guh8aJD9MUc7pbsw5rHrPUVe6S6RUfFJvh/Xq8oMIQgSw== dependencies: - web3-core "1.3.0" - web3-core-method "1.3.0" - web3-utils "1.3.0" + web3-core "1.3.4" + web3-core-method "1.3.4" + web3-utils "1.3.4" web3-net@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8354,12 +9198,12 @@ web3-providers-http@1.2.9: web3-core-helpers "1.2.9" xhr2-cookies "1.1.0" -web3-providers-http@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.0.tgz#88227f64c88b32abed4359383c2663616e0dc531" - integrity sha512-cMKhUI6PqlY/EC+ZDacAxajySBu8AzW8jOjt1Pe/mbRQgS0rcZyvLePGTTuoyaA8C21F8UW+EE5jj7YsNgOuqA== +web3-providers-http@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-http/-/web3-providers-http-1.3.4.tgz#89389e18e27148faa2fef58842740ffadbdda8cc" + integrity sha512-aIg/xHXvxpqpFU70sqfp+JC3sGkLfAimRKTUhG4oJZ7U+tTcYTHoxBJj+4A3Id4JAoKiiv0k1/qeyQ8f3rMC3g== dependencies: - web3-core-helpers "1.3.0" + web3-core-helpers "1.3.4" xhr2-cookies "1.1.0" web3-providers-ipc@1.2.2: @@ -8380,14 +9224,14 @@ web3-providers-ipc@1.2.9: underscore "1.9.1" web3-core-helpers "1.2.9" -web3-providers-ipc@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.0.tgz#d7c2b203733b46f7b4e7b15633d891648cf9a293" - integrity sha512-0CrLuRofR+1J38nEj4WsId/oolwQEM6Yl1sOt41S/6bNI7htdkwgVhSloFIMJMDFHtRw229QIJ6wIaKQz0X1Og== +web3-providers-ipc@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-ipc/-/web3-providers-ipc-1.3.4.tgz#b963518989b1b7847063cdd461ff73b83855834a" + integrity sha512-E0CvXEJElr/TIlG1YfJeO3Le5NI/4JZM+1SsEdiPIfBUAJN18oOoum138EBGKv5+YaLKZUtUuJSXWjIIOR/0Ig== dependencies: oboe "2.1.5" underscore "1.9.1" - web3-core-helpers "1.3.0" + web3-core-helpers "1.3.4" web3-providers-ws@1.2.2: version "1.2.2" @@ -8408,14 +9252,14 @@ web3-providers-ws@1.2.9: web3-core-helpers "1.2.9" websocket "^1.0.31" -web3-providers-ws@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.0.tgz#84adeff65acd4624d7f5bb43c5b2b22d8f0f63a4" - integrity sha512-Im5MthhJnJst8nSoq0TgbyOdaiFQFa5r6sHPOVllhgIgViDqzbnlAFW9sNzQ0Q8VXPNfPIQKi9cOrHlSRNPjRw== +web3-providers-ws@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-providers-ws/-/web3-providers-ws-1.3.4.tgz#b94c2e0ec51a0c472abdec53a472b5bf8176bec1" + integrity sha512-WBd9hk2fUAdrbA3kUyUk94ZeILtE6txLeoVVvIKAw2bPegx+RjkLyxC1Du0oceKgQ/qQWod8CCzl1E/GgTP+MQ== dependencies: eventemitter3 "4.0.4" underscore "1.9.1" - web3-core-helpers "1.3.0" + web3-core-helpers "1.3.4" websocket "^1.0.32" web3-providers@2.0.0-alpha.1: @@ -8455,15 +9299,15 @@ web3-shh@1.2.9: web3-core-subscriptions "1.2.9" web3-net "1.2.9" -web3-shh@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.0.tgz#62d15297da8fb5f733dd1b98f9ade300590f4d49" - integrity sha512-IZTojA4VCwVq+7eEIHuL1tJXtU+LJDhO8Y2QmuwetEWW1iBgWCGPHZasipWP+7kDpSm/5lo5GRxL72FF/Os/tA== +web3-shh@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-shh/-/web3-shh-1.3.4.tgz#b7d29e118f26416c1a74575e585be379cc01a77a" + integrity sha512-zoeww5mxLh3xKcqbX85irQbtFe5pc5XwrgjvmdMkhkOdZzPASlWOgqzUFtaPykpLwC3yavVx4jG5RqifweXLUA== dependencies: - web3-core "1.3.0" - web3-core-method "1.3.0" - web3-core-subscriptions "1.3.0" - web3-net "1.3.0" + web3-core "1.3.4" + web3-core-method "1.3.4" + web3-core-subscriptions "1.3.4" + web3-net "1.3.4" web3-shh@2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8507,10 +9351,10 @@ web3-utils@1.2.9: underscore "1.9.1" utf8 "3.0.0" -web3-utils@1.3.0, web3-utils@^1.2.1, web3-utils@^1.2.9: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.0.tgz#5bac16e5e0ec9fe7bdcfadb621655e8aa3cf14e1" - integrity sha512-2mS5axFCbkhicmoDRuJeuo0TVGQDgC2sPi/5dblfVC+PMtX0efrb8Xlttv/eGkq7X4E83Pds34FH98TP2WOUZA== +web3-utils@1.3.4, web3-utils@^1.2.1, web3-utils@^1.2.9, web3-utils@^1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.4.tgz#9b1aa30d7549f860b573e7bb7e690999e7192198" + integrity sha512-/vC2v0MaZNpWooJfpRw63u0Y3ag2gNjAWiLtMSL6QQLmCqCy4SQIndMt/vRyx0uMoeGt1YTwSXEcHjUzOhLg0A== dependencies: bn.js "^4.11.9" eth-lib "0.2.8" @@ -8537,20 +9381,6 @@ web3-utils@2.0.0-alpha.1: randombytes "^2.1.0" utf8 "2.1.1" -web3-utils@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/web3-utils/-/web3-utils-1.3.1.tgz#9aa880dd8c9463fe5c099107889f86a085370c2e" - integrity sha512-9gPwFm8SXtIJuzdrZ37PRlalu40fufXxo+H2PiCwaO6RpKGAvlUlWU0qQbyToFNXg7W2H8djEgoAVac8NLMCKQ== - dependencies: - bn.js "^4.11.9" - eth-lib "0.2.8" - ethereum-bloom-filters "^1.0.6" - ethjs-unit "0.1.6" - number-to-bn "1.7.0" - randombytes "^2.1.0" - underscore "1.9.1" - utf8 "3.0.0" - web3@1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/web3/-/web3-1.2.2.tgz#b1b8b69aafdf94cbaeadbb68a8aa1df2ef266aec" @@ -8579,17 +9409,17 @@ web3@1.2.9: web3-utils "1.2.9" web3@^1.0.0-beta.34, web3@^1.2.11, web3@^1.2.9: - version "1.3.0" - resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.0.tgz#8fe4cd6e2a21c91904f343ba75717ee4c76bb349" - integrity sha512-4q9dna0RecnrlgD/bD1C5S+81Untbd6Z/TBD7rb+D5Bvvc0Wxjr4OP70x+LlnwuRDjDtzBwJbNUblh2grlVArw== + version "1.3.4" + resolved "https://registry.yarnpkg.com/web3/-/web3-1.3.4.tgz#31e014873360aa5840eb17f9f171190c967cffb7" + integrity sha512-D6cMb2EtTMLHgdGbkTPGl/Qi7DAfczR+Lp7iFX3bcu/bsD9V8fZW69hA8v5cRPNGzXUwVQebk3bS17WKR4cD2w== dependencies: - web3-bzz "1.3.0" - web3-core "1.3.0" - web3-eth "1.3.0" - web3-eth-personal "1.3.0" - web3-net "1.3.0" - web3-shh "1.3.0" - web3-utils "1.3.0" + web3-bzz "1.3.4" + web3-core "1.3.4" + web3-eth "1.3.4" + web3-eth-personal "1.3.4" + web3-net "1.3.4" + web3-shh "1.3.4" + web3-utils "1.3.4" web3@^2.0.0-alpha.1: version "2.0.0-alpha.1" @@ -8606,7 +9436,7 @@ web3@^2.0.0-alpha.1: web3-shh "2.0.0-alpha.1" web3-utils "2.0.0-alpha.1" -websocket@^1.0.28: +websocket@^1.0.28, websocket@^1.0.31, websocket@^1.0.32: version "1.0.33" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.33.tgz#407f763fc58e74a3fa41ca3ae5d78d3f5e3b82a5" integrity sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA== @@ -8618,18 +9448,6 @@ websocket@^1.0.28: utf-8-validate "^5.0.2" yaeti "^0.0.6" -websocket@^1.0.31, websocket@^1.0.32: - version "1.0.32" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.32.tgz#1f16ddab3a21a2d929dec1687ab21cfdc6d3dbb1" - integrity sha512-i4yhcllSP4wrpoPMU2N0TQ/q0O94LRG/eUQjEAamRltjQ1oT1PFFKOG4i877OlJgCG8rw6LrrowJp+TYCEWF7Q== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - "websocket@github:web3-js/WebSocket-Node#polyfill/globalThis": version "1.0.29" resolved "https://codeload.github.com/web3-js/WebSocket-Node/tar.gz/ef5ea2f41daf4a2113b80c9223df884b4d56c400" @@ -8645,6 +9463,19 @@ which-module@^2.0.0: resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= +which-typed-array@^1.1.2: + version "1.1.4" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.4.tgz#8fcb7d3ee5adf2d771066fba7cf37e32fe8711ff" + integrity sha512-49E0SpUe90cjpoc7BOJwyPHRqSAd12c10Qm2amdEZrJPCY2NDxaW01zHITrem+rnETY3dwrbH3UUrUwagfCYDA== + dependencies: + available-typed-arrays "^1.0.2" + call-bind "^1.0.0" + es-abstract "^1.18.0-next.1" + foreach "^2.0.5" + function-bind "^1.1.1" + has-symbols "^1.0.1" + is-typed-array "^1.1.3" + which@1.3.1, which@^1.2.9: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" @@ -8701,10 +9532,10 @@ word-wrap@^1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== -workerpool@6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.2.tgz#e241b43d8d033f1beb52c7851069456039d1d438" - integrity sha512-DSNyvOpFKrNusaaUwk+ej6cBj1bmhLcBfj80elGk+ZIo5JSkq+unB1dLKEOcNfJDZgjGICfhQ0Q5TbP0PvF4+Q== +workerpool@6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.1.0.tgz#a8e038b4c94569596852de7a8ea4228eefdeb37b" + integrity sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg== wrap-ansi@^5.1.0: version "5.1.0" @@ -8715,10 +9546,10 @@ wrap-ansi@^5.1.0: string-width "^3.0.0" strip-ansi "^5.0.0" -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" @@ -8729,13 +9560,6 @@ wrappy@1: resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= -write@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/write/-/write-1.0.3.tgz#0800e14523b923a387e415123c865616aae0f5c3" - integrity sha512-/lg70HAjtkUgWPVZhZcm+T4hkL8Zbtp1nFNOn3lRrxnlv50SRBv7cR7RqR+GMsd3hUXy9hWBo4CHTbFTcOYwig== - dependencies: - mkdirp "^0.5.1" - ws@7.2.3: version "7.2.3" resolved "https://registry.yarnpkg.com/ws/-/ws-7.2.3.tgz#a5411e1fb04d5ed0efee76d26d5c46d830c39b46" @@ -8750,10 +9574,10 @@ ws@^3.0.0: safe-buffer "~5.1.0" ultron "~1.1.0" -ws@^7.2.3, ws@^7.3.0: - version "7.4.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.0.tgz#a5dd76a24197940d4a8bb9e0e152bb4503764da7" - integrity sha512-kyFwXuV/5ymf+IXhS6f0+eAFvydbaBW3zjpT6hUdAh/hbVjTIB5EHBGi0bPoCLSK2wcuz3BrEkB9LrYv1Nm4NQ== +ws@^7.2.1, ws@^7.3.0: + version "7.4.3" + resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" + integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== wsrun@^5.2.4: version "5.2.4" @@ -8812,22 +9636,34 @@ xmlhttprequest@1.8.0: resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" integrity sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw= -xtend@^4.0.0, xtend@^4.0.1: +xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +xtend@~2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-2.1.2.tgz#6efecc2a4dad8e6962c4901b337ce7ba87b5d28b" + integrity sha1-bv7MKk2tjmlixJAbM3znuoe10os= + dependencies: + object-keys "~0.4.0" + y18n@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== +y18n@^5.0.5: + version "5.0.5" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" + integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== + yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= -yallist@^3.0.0, yallist@^3.0.3: +yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== @@ -8845,13 +9681,15 @@ yargs-parser@13.1.2, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.5" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.5.tgz#5d37729146d3f894f39fc94b6796f5b239513186" + integrity sha512-jYRGS3zWy20NtDtK2kBgo/TlAoy5YUuhD9/LZ7z7W4j1Fdw2cqD0xEEclf8fxc8xjD6X5Qr+qQQwCEsP8iRiYg== yargs-unparser@1.6.0: version "1.6.0" @@ -8888,24 +9726,20 @@ yargs@13.3.2, yargs@^13.0.0, yargs@^13.3.0: y18n "^4.0.0" yargs-parser "^13.1.2" -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== +yargs@16.2.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" + y18n "^5.0.5" + yargs-parser "^20.2.2" -yauzl@^2.10.0, yauzl@^2.4.2: +yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=